aboutsummaryrefslogtreecommitdiffstats
path: root/packages/0x.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/0x.js')
-rw-r--r--packages/0x.js/package.json187
-rw-r--r--packages/0x.js/src/0x.ts608
-rw-r--r--packages/0x.js/src/artifacts.ts14
-rw-r--r--packages/0x.js/src/artifacts/DummyToken.json40
-rw-r--r--packages/0x.js/src/artifacts/EtherToken.json570
-rw-r--r--packages/0x.js/src/artifacts/Exchange.json1216
-rw-r--r--packages/0x.js/src/artifacts/Token.json340
-rw-r--r--packages/0x.js/src/artifacts/TokenRegistry.json1090
-rw-r--r--packages/0x.js/src/artifacts/TokenTransferProxy.json370
-rw-r--r--packages/0x.js/src/artifacts/ZRX.json36
-rw-r--r--packages/0x.js/src/contract_wrappers/contract_wrapper.ts378
-rw-r--r--packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts318
-rw-r--r--packages/0x.js/src/contract_wrappers/exchange_wrapper.ts1718
-rw-r--r--packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts224
-rw-r--r--packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts110
-rw-r--r--packages/0x.js/src/contract_wrappers/token_wrapper.ts720
-rw-r--r--packages/0x.js/src/globals.d.ts50
-rw-r--r--packages/0x.js/src/globalsAugment.d.ts34
-rw-r--r--packages/0x.js/src/index.ts88
-rw-r--r--packages/0x.js/src/order_watcher/event_watcher.ts148
-rw-r--r--packages/0x.js/src/order_watcher/expiration_watcher.ts118
-rw-r--r--packages/0x.js/src/order_watcher/order_state_watcher.ts670
-rw-r--r--packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts180
-rw-r--r--packages/0x.js/src/schemas/zero_ex_config_schema.ts50
-rw-r--r--packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts150
-rw-r--r--packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts100
-rw-r--r--packages/0x.js/src/types.ts390
-rw-r--r--packages/0x.js/src/utils/abi_decoder.ts118
-rw-r--r--packages/0x.js/src/utils/assert.ts42
-rw-r--r--packages/0x.js/src/utils/constants.ts16
-rw-r--r--packages/0x.js/src/utils/decorators.ts114
-rw-r--r--packages/0x.js/src/utils/exchange_transfer_simulator.ts182
-rw-r--r--packages/0x.js/src/utils/filter_utils.ts150
-rw-r--r--packages/0x.js/src/utils/order_state_utils.ts254
-rw-r--r--packages/0x.js/src/utils/order_validation_utils.ts404
-rw-r--r--packages/0x.js/src/utils/signature_utils.ts80
-rw-r--r--packages/0x.js/src/utils/utils.ts116
-rw-r--r--packages/0x.js/test/0x.js_test.ts538
-rw-r--r--packages/0x.js/test/artifacts_test.ts80
-rw-r--r--packages/0x.js/test/assert_test.ts60
-rw-r--r--packages/0x.js/test/ether_token_wrapper_test.ts668
-rw-r--r--packages/0x.js/test/event_watcher_test.ts212
-rw-r--r--packages/0x.js/test/exchange_transfer_simulator_test.ts198
-rw-r--r--packages/0x.js/test/exchange_wrapper_test.ts2234
-rw-r--r--packages/0x.js/test/expiration_watcher_test.ts266
-rw-r--r--packages/0x.js/test/order_state_watcher_test.ts1022
-rw-r--r--packages/0x.js/test/order_validation_test.ts900
-rw-r--r--packages/0x.js/test/remaining_fillable_calculator_test.ts436
-rw-r--r--packages/0x.js/test/subscription_test.ts130
-rw-r--r--packages/0x.js/test/token_registry_wrapper_test.ts212
-rw-r--r--packages/0x.js/test/token_transfer_proxy_wrapper_test.ts46
-rw-r--r--packages/0x.js/test/token_wrapper_test.ts994
-rw-r--r--packages/0x.js/test/utils/chai_setup.ts12
-rw-r--r--packages/0x.js/test/utils/constants.ts18
-rw-r--r--packages/0x.js/test/utils/fill_scenarios.ts374
-rw-r--r--packages/0x.js/test/utils/order_factory.ts74
-rw-r--r--packages/0x.js/test/utils/report_callback_errors.ts102
-rw-r--r--packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts34
-rw-r--r--packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts42
-rw-r--r--packages/0x.js/test/utils/token_utils.ts48
-rw-r--r--packages/0x.js/test/utils/web3_factory.ts40
-rw-r--r--packages/0x.js/tsconfig.json28
-rw-r--r--packages/0x.js/tslint.json2
63 files changed, 10080 insertions, 10083 deletions
diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json
index 10d923501..67d6452b5 100644
--- a/packages/0x.js/package.json
+++ b/packages/0x.js/package.json
@@ -1,97 +1,94 @@
{
- "name": "0x.js",
- "version": "0.30.2",
- "description": "A javascript library for interacting with the 0x protocol",
- "keywords": [
- "0x.js",
- "0xproject",
- "ethereum",
- "tokens",
- "exchange"
- ],
- "main": "lib/src/index.js",
- "types": "lib/src/index.d.ts",
- "scripts": {
- "prebuild": "run-s clean generate_contract_wrappers",
- "build": "run-p build:umd:prod build:commonjs; exit 0;",
- "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR",
- "upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json",
- "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --templates contract_templates --output src/contract_wrappers/generated",
- "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
- "test:circleci": "run-s test:coverage report_test_coverage",
- "test": "run-s clean test:commonjs",
- "test:coverage": "nyc npm run test --all",
- "report_test_coverage": "nyc report --reporter=text-lcov | coveralls",
- "update_contracts": "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../0x.js/src/artifacts; done;",
- "clean": "shx rm -rf _bundles lib test_temp",
- "build:umd:prod": "NODE_ENV=production webpack",
- "build:commonjs": "tsc && copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts;",
- "test:commonjs": "run-s build:commonjs run_mocha",
- "run_mocha": "mocha lib/test/**/*_test.js --timeout 10000 --bail --exit"
- },
- "config": {
- "artifacts": "TokenTransferProxy Exchange TokenRegistry Token EtherToken"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js"
- },
- "license": "Apache-2.0",
- "engines": {
- "node": ">=6.0.0"
- },
- "devDependencies": {
- "@0xproject/abi-gen": "^0.1.4",
- "@0xproject/dev-utils": "^0.0.7",
- "@0xproject/tslint-config": "^0.4.4",
- "@types/bintrees": "^1.0.2",
- "@types/jsonschema": "^1.1.1",
- "@types/lodash": "^4.14.86",
- "@types/mocha": "^2.2.42",
- "@types/node": "^8.0.53",
- "@types/sinon": "^2.2.2",
- "@types/uuid": "^3.4.2",
- "awesome-typescript-loader": "^3.1.3",
- "chai": "^4.0.1",
- "chai-as-promised": "^7.1.0",
- "chai-as-promised-typescript-typings": "^0.0.6",
- "chai-bignumber": "^2.0.1",
- "chai-typescript-typings": "^0.0.2",
- "copyfiles": "^1.2.0",
- "coveralls": "^3.0.0",
- "dirty-chai": "^2.0.1",
- "json-loader": "^0.5.4",
- "mocha": "^4.0.1",
- "npm-run-all": "^4.1.2",
- "nyc": "^11.0.1",
- "opn-cli": "^3.1.0",
- "request": "^2.81.0",
- "request-promise-native": "^1.0.4",
- "shx": "^0.2.2",
- "sinon": "^4.0.0",
- "source-map-support": "^0.5.0",
- "truffle-hdwallet-provider": "^0.0.3",
- "tslint": "5.8.0",
- "typedoc": "~0.8.0",
- "typescript": "~2.6.1",
- "web3-provider-engine": "^13.0.1",
- "web3-typescript-typings": "^0.9.6",
- "webpack": "^3.1.0"
- },
- "dependencies": {
- "@0xproject/assert": "^0.0.13",
- "@0xproject/json-schemas": "^0.7.5",
- "@0xproject/types": "^0.1.6",
- "@0xproject/utils": "^0.2.2",
- "@0xproject/web3-wrapper": "^0.1.7",
- "bintrees": "^1.0.2",
- "bn.js": "^4.11.8",
- "ethereumjs-abi": "^0.6.4",
- "ethereumjs-blockstream": "^2.0.6",
- "ethereumjs-util": "^5.1.1",
- "js-sha3": "^0.6.1",
- "lodash": "^4.17.4",
- "uuid": "^3.1.0",
- "web3": "^0.20.0"
- }
+ "name": "0x.js",
+ "version": "0.30.2",
+ "description": "A javascript library for interacting with the 0x protocol",
+ "keywords": ["0x.js", "0xproject", "ethereum", "tokens", "exchange"],
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "scripts": {
+ "prebuild": "run-s clean generate_contract_wrappers",
+ "build": "run-p build:umd:prod build:commonjs; exit 0;",
+ "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR",
+ "upload_docs_json":
+ "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json",
+ "generate_contract_wrappers":
+ "node ../abi-gen/lib/index.js --abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --templates contract_templates --output src/contract_wrappers/generated",
+ "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
+ "test:circleci": "run-s test:coverage report_test_coverage",
+ "test": "run-s clean test:commonjs",
+ "test:coverage": "nyc npm run test --all",
+ "report_test_coverage": "nyc report --reporter=text-lcov | coveralls",
+ "update_contracts":
+ "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../0x.js/src/artifacts; done;",
+ "clean": "shx rm -rf _bundles lib test_temp",
+ "build:umd:prod": "NODE_ENV=production webpack",
+ "build:commonjs": "tsc && copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts;",
+ "test:commonjs": "run-s build:commonjs run_mocha",
+ "run_mocha": "mocha lib/test/**/*_test.js --timeout 10000 --bail --exit"
+ },
+ "config": {
+ "artifacts": "TokenTransferProxy Exchange TokenRegistry Token EtherToken"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js"
+ },
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "devDependencies": {
+ "@0xproject/abi-gen": "^0.1.4",
+ "@0xproject/dev-utils": "^0.0.7",
+ "@0xproject/tslint-config": "^0.4.4",
+ "@types/bintrees": "^1.0.2",
+ "@types/jsonschema": "^1.1.1",
+ "@types/lodash": "^4.14.86",
+ "@types/mocha": "^2.2.42",
+ "@types/node": "^8.0.53",
+ "@types/sinon": "^2.2.2",
+ "@types/uuid": "^3.4.2",
+ "awesome-typescript-loader": "^3.1.3",
+ "chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-as-promised-typescript-typings": "^0.0.6",
+ "chai-bignumber": "^2.0.1",
+ "chai-typescript-typings": "^0.0.2",
+ "copyfiles": "^1.2.0",
+ "coveralls": "^3.0.0",
+ "dirty-chai": "^2.0.1",
+ "json-loader": "^0.5.4",
+ "mocha": "^4.0.1",
+ "npm-run-all": "^4.1.2",
+ "nyc": "^11.0.1",
+ "opn-cli": "^3.1.0",
+ "request": "^2.81.0",
+ "request-promise-native": "^1.0.4",
+ "shx": "^0.2.2",
+ "sinon": "^4.0.0",
+ "source-map-support": "^0.5.0",
+ "truffle-hdwallet-provider": "^0.0.3",
+ "tslint": "5.8.0",
+ "typedoc": "~0.8.0",
+ "typescript": "~2.6.1",
+ "web3-provider-engine": "^13.0.1",
+ "web3-typescript-typings": "^0.9.6",
+ "webpack": "^3.1.0"
+ },
+ "dependencies": {
+ "@0xproject/assert": "^0.0.13",
+ "@0xproject/json-schemas": "^0.7.5",
+ "@0xproject/types": "^0.1.6",
+ "@0xproject/utils": "^0.2.2",
+ "@0xproject/web3-wrapper": "^0.1.7",
+ "bintrees": "^1.0.2",
+ "bn.js": "^4.11.8",
+ "ethereumjs-abi": "^0.6.4",
+ "ethereumjs-blockstream": "^2.0.6",
+ "ethereumjs-util": "^5.1.1",
+ "js-sha3": "^0.6.1",
+ "lodash": "^4.17.4",
+ "uuid": "^3.1.0",
+ "web3": "^0.20.0"
+ }
}
diff --git a/packages/0x.js/src/0x.ts b/packages/0x.js/src/0x.ts
index f8a484c5d..dec26e3eb 100644
--- a/packages/0x.js/src/0x.ts
+++ b/packages/0x.js/src/0x.ts
@@ -13,13 +13,13 @@ import { TokenWrapper } from './contract_wrappers/token_wrapper';
import { OrderStateWatcher } from './order_watcher/order_state_watcher';
import { zeroExConfigSchema } from './schemas/zero_ex_config_schema';
import {
- ECSignature,
- Order,
- SignedOrder,
- TransactionReceiptWithDecodedLogs,
- Web3Provider,
- ZeroExConfig,
- ZeroExError,
+ ECSignature,
+ Order,
+ SignedOrder,
+ TransactionReceiptWithDecodedLogs,
+ Web3Provider,
+ ZeroExConfig,
+ ZeroExError,
} from './types';
import { AbiDecoder } from './utils/abi_decoder';
import { assert } from './utils/assert';
@@ -33,318 +33,318 @@ import { utils } from './utils/utils';
* and all calls to the library should be made through a ZeroEx instance.
*/
export class ZeroEx {
- /**
- * When creating an order without a specified taker or feeRecipient you must supply the Solidity
- * address null type (as opposed to Javascripts `null`, `undefined` or empty string). We expose
- * this constant for your convenience.
- */
- public static NULL_ADDRESS = constants.NULL_ADDRESS;
+ /**
+ * When creating an order without a specified taker or feeRecipient you must supply the Solidity
+ * address null type (as opposed to Javascripts `null`, `undefined` or empty string). We expose
+ * this constant for your convenience.
+ */
+ public static NULL_ADDRESS = constants.NULL_ADDRESS;
- /**
- * An instance of the ExchangeWrapper class containing methods for interacting with the 0x Exchange smart contract.
- */
- public exchange: ExchangeWrapper;
- /**
- * An instance of the TokenRegistryWrapper class containing methods for interacting with the 0x
- * TokenRegistry smart contract.
- */
- public tokenRegistry: TokenRegistryWrapper;
- /**
- * An instance of the TokenWrapper class containing methods for interacting with any ERC20 token smart contract.
- */
- public token: TokenWrapper;
- /**
- * An instance of the EtherTokenWrapper class containing methods for interacting with the
- * wrapped ETH ERC20 token smart contract.
- */
- public etherToken: EtherTokenWrapper;
- /**
- * An instance of the TokenTransferProxyWrapper class containing methods for interacting with the
- * tokenTransferProxy smart contract.
- */
- public proxy: TokenTransferProxyWrapper;
- /**
- * An instance of the OrderStateWatcher class containing methods for watching a set of orders for relevant
- * blockchain state changes.
- */
- public orderStateWatcher: OrderStateWatcher;
- private _web3Wrapper: Web3Wrapper;
- private _abiDecoder: AbiDecoder;
- /**
- * Verifies that the elliptic curve signature `signature` was generated
- * by signing `data` with the private key corresponding to the `signerAddress` address.
- * @param data The hex encoded data signed by the supplied signature.
- * @param signature An object containing the elliptic curve signature parameters.
- * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
- * @return Whether the signature is valid for the supplied signerAddress and data.
- */
- public static isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
- assert.isHexString('data', data);
- assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema);
- assert.isETHAddressHex('signerAddress', signerAddress);
+ /**
+ * An instance of the ExchangeWrapper class containing methods for interacting with the 0x Exchange smart contract.
+ */
+ public exchange: ExchangeWrapper;
+ /**
+ * An instance of the TokenRegistryWrapper class containing methods for interacting with the 0x
+ * TokenRegistry smart contract.
+ */
+ public tokenRegistry: TokenRegistryWrapper;
+ /**
+ * An instance of the TokenWrapper class containing methods for interacting with any ERC20 token smart contract.
+ */
+ public token: TokenWrapper;
+ /**
+ * An instance of the EtherTokenWrapper class containing methods for interacting with the
+ * wrapped ETH ERC20 token smart contract.
+ */
+ public etherToken: EtherTokenWrapper;
+ /**
+ * An instance of the TokenTransferProxyWrapper class containing methods for interacting with the
+ * tokenTransferProxy smart contract.
+ */
+ public proxy: TokenTransferProxyWrapper;
+ /**
+ * An instance of the OrderStateWatcher class containing methods for watching a set of orders for relevant
+ * blockchain state changes.
+ */
+ public orderStateWatcher: OrderStateWatcher;
+ private _web3Wrapper: Web3Wrapper;
+ private _abiDecoder: AbiDecoder;
+ /**
+ * Verifies that the elliptic curve signature `signature` was generated
+ * by signing `data` with the private key corresponding to the `signerAddress` address.
+ * @param data The hex encoded data signed by the supplied signature.
+ * @param signature An object containing the elliptic curve signature parameters.
+ * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
+ * @return Whether the signature is valid for the supplied signerAddress and data.
+ */
+ public static isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
+ assert.isHexString('data', data);
+ assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema);
+ assert.isETHAddressHex('signerAddress', signerAddress);
- const isValidSignature = signatureUtils.isValidSignature(data, signature, signerAddress);
- return isValidSignature;
- }
- /**
- * Generates a pseudo-random 256-bit salt.
- * The salt can be included in an 0x order, ensuring that the order generates a unique orderHash
- * and will not collide with other outstanding orders that are identical in all other parameters.
- * @return A pseudo-random 256-bit number that can be used as a salt.
- */
- public static generatePseudoRandomSalt(): BigNumber {
- // BigNumber.random returns a pseudo-random number between 0 & 1 with a passed in number of decimal places.
- // Source: https://mikemcl.github.io/bignumber.js/#random
- const randomNumber = BigNumber.random(constants.MAX_DIGITS_IN_UNSIGNED_256_INT);
- const factor = new BigNumber(10).pow(constants.MAX_DIGITS_IN_UNSIGNED_256_INT - 1);
- const salt = randomNumber.times(factor).round();
- return salt;
- }
- /**
- * Checks if the supplied hex encoded order hash is valid.
- * Note: Valid means it has the expected format, not that an order with the orderHash exists.
- * Use this method when processing orderHashes submitted as user input.
- * @param orderHash Hex encoded orderHash.
- * @return Whether the supplied orderHash has the expected format.
- */
- public static isValidOrderHash(orderHash: string): boolean {
- // Since this method can be called to check if any arbitrary string conforms to an orderHash's
- // format, we only assert that we were indeed passed a string.
- assert.isString('orderHash', orderHash);
- const schemaValidator = new SchemaValidator();
- const isValidOrderHash = schemaValidator.validate(orderHash, schemas.orderHashSchema).valid;
- return isValidOrderHash;
- }
- /**
- * A unit amount is defined as the amount of a token above the specified decimal places (integer part).
- * E.g: If a currency has 18 decimal places, 1e18 or one quintillion of the currency is equivalent
- * to 1 unit.
- * @param amount The amount in baseUnits that you would like converted to units.
- * @param decimals The number of decimal places the unit amount has.
- * @return The amount in units.
- */
- public static toUnitAmount(amount: BigNumber, decimals: number): BigNumber {
- assert.isValidBaseUnitAmount('amount', amount);
- assert.isNumber('decimals', decimals);
+ const isValidSignature = signatureUtils.isValidSignature(data, signature, signerAddress);
+ return isValidSignature;
+ }
+ /**
+ * Generates a pseudo-random 256-bit salt.
+ * The salt can be included in an 0x order, ensuring that the order generates a unique orderHash
+ * and will not collide with other outstanding orders that are identical in all other parameters.
+ * @return A pseudo-random 256-bit number that can be used as a salt.
+ */
+ public static generatePseudoRandomSalt(): BigNumber {
+ // BigNumber.random returns a pseudo-random number between 0 & 1 with a passed in number of decimal places.
+ // Source: https://mikemcl.github.io/bignumber.js/#random
+ const randomNumber = BigNumber.random(constants.MAX_DIGITS_IN_UNSIGNED_256_INT);
+ const factor = new BigNumber(10).pow(constants.MAX_DIGITS_IN_UNSIGNED_256_INT - 1);
+ const salt = randomNumber.times(factor).round();
+ return salt;
+ }
+ /**
+ * Checks if the supplied hex encoded order hash is valid.
+ * Note: Valid means it has the expected format, not that an order with the orderHash exists.
+ * Use this method when processing orderHashes submitted as user input.
+ * @param orderHash Hex encoded orderHash.
+ * @return Whether the supplied orderHash has the expected format.
+ */
+ public static isValidOrderHash(orderHash: string): boolean {
+ // Since this method can be called to check if any arbitrary string conforms to an orderHash's
+ // format, we only assert that we were indeed passed a string.
+ assert.isString('orderHash', orderHash);
+ const schemaValidator = new SchemaValidator();
+ const isValidOrderHash = schemaValidator.validate(orderHash, schemas.orderHashSchema).valid;
+ return isValidOrderHash;
+ }
+ /**
+ * A unit amount is defined as the amount of a token above the specified decimal places (integer part).
+ * E.g: If a currency has 18 decimal places, 1e18 or one quintillion of the currency is equivalent
+ * to 1 unit.
+ * @param amount The amount in baseUnits that you would like converted to units.
+ * @param decimals The number of decimal places the unit amount has.
+ * @return The amount in units.
+ */
+ public static toUnitAmount(amount: BigNumber, decimals: number): BigNumber {
+ assert.isValidBaseUnitAmount('amount', amount);
+ assert.isNumber('decimals', decimals);
- const aUnit = new BigNumber(10).pow(decimals);
- const unit = amount.div(aUnit);
- return unit;
- }
- /**
- * A baseUnit is defined as the smallest denomination of a token. An amount expressed in baseUnits
- * is the amount expressed in the smallest denomination.
- * E.g: 1 unit of a token with 18 decimal places is expressed in baseUnits as 1000000000000000000
- * @param amount The amount of units that you would like converted to baseUnits.
- * @param decimals The number of decimal places the unit amount has.
- * @return The amount in baseUnits.
- */
- public static toBaseUnitAmount(amount: BigNumber, decimals: number): BigNumber {
- assert.isBigNumber('amount', amount);
- assert.isNumber('decimals', decimals);
+ const aUnit = new BigNumber(10).pow(decimals);
+ const unit = amount.div(aUnit);
+ return unit;
+ }
+ /**
+ * A baseUnit is defined as the smallest denomination of a token. An amount expressed in baseUnits
+ * is the amount expressed in the smallest denomination.
+ * E.g: 1 unit of a token with 18 decimal places is expressed in baseUnits as 1000000000000000000
+ * @param amount The amount of units that you would like converted to baseUnits.
+ * @param decimals The number of decimal places the unit amount has.
+ * @return The amount in baseUnits.
+ */
+ public static toBaseUnitAmount(amount: BigNumber, decimals: number): BigNumber {
+ assert.isBigNumber('amount', amount);
+ assert.isNumber('decimals', decimals);
- const unit = new BigNumber(10).pow(decimals);
- const baseUnitAmount = amount.times(unit);
- const hasDecimals = baseUnitAmount.decimalPlaces() !== 0;
- if (hasDecimals) {
- throw new Error(`Invalid unit amount: ${amount.toString()} - Too many decimal places`);
- }
- return baseUnitAmount;
- }
- /**
- * Computes the orderHash for a supplied order.
- * @param order An object that conforms to the Order or SignedOrder interface definitions.
- * @return The resulting orderHash from hashing the supplied order.
- */
- @decorators.syncZeroExErrorHandler
- public static getOrderHashHex(order: Order | SignedOrder): string {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- const orderHashHex = utils.getOrderHashHex(order);
- return orderHashHex;
- }
- /**
- * Instantiates a new ZeroEx instance that provides the public interface to the 0x.js library.
- * @param provider The Web3.js Provider instance you would like the 0x.js library to use for interacting with
- * the Ethereum network.
- * @param config The configuration object. Look up the type for the description.
- * @return An instance of the 0x.js ZeroEx class.
- */
- constructor(provider: Web3Provider, config: ZeroExConfig) {
- assert.isWeb3Provider('provider', provider);
- assert.doesConformToSchema('config', config, zeroExConfigSchema);
- const artifactJSONs = _.values(artifacts);
- const abiArrays = _.map(artifactJSONs, artifact => artifact.abi);
- this._abiDecoder = new AbiDecoder(abiArrays);
- const defaults = {
- gasPrice: config.gasPrice,
- };
- this._web3Wrapper = new Web3Wrapper(provider, defaults);
- this.proxy = new TokenTransferProxyWrapper(
- this._web3Wrapper,
- config.networkId,
- config.tokenTransferProxyContractAddress,
- );
- this.token = new TokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.proxy);
- this.exchange = new ExchangeWrapper(
- this._web3Wrapper,
- config.networkId,
- this._abiDecoder,
- this.token,
- config.exchangeContractAddress,
- config.zrxContractAddress,
- );
- this.tokenRegistry = new TokenRegistryWrapper(
- this._web3Wrapper,
- config.networkId,
- config.tokenRegistryContractAddress,
- );
- this.etherToken = new EtherTokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.token);
- this.orderStateWatcher = new OrderStateWatcher(
- this._web3Wrapper,
- this._abiDecoder,
- this.token,
- this.exchange,
- config.orderWatcherConfig,
- );
- }
- /**
- * Sets a new web3 provider for 0x.js. Updating the provider will stop all
- * subscriptions so you will need to re-subscribe to all events relevant to your app after this call.
- * @param provider The Web3Provider you would like the 0x.js library to use from now on.
- * @param networkId The id of the network your provider is connected to
- */
- public setProvider(provider: Web3Provider, networkId: number): void {
- this._web3Wrapper.setProvider(provider);
- (this.exchange as any)._invalidateContractInstances();
- (this.exchange as any)._setNetworkId(networkId);
- (this.tokenRegistry as any)._invalidateContractInstance();
- (this.tokenRegistry as any)._setNetworkId(networkId);
- (this.token as any)._invalidateContractInstances();
- (this.token as any)._setNetworkId(networkId);
- (this.proxy as any)._invalidateContractInstance();
- (this.proxy as any)._setNetworkId(networkId);
- (this.etherToken as any)._invalidateContractInstance();
- (this.etherToken as any)._setNetworkId(networkId);
- }
- /**
- * Get user Ethereum addresses available through the supplied web3 provider available for sending transactions.
- * @return An array of available user Ethereum addresses.
- */
- public async getAvailableAddressesAsync(): Promise<string[]> {
- const availableAddresses = await this._web3Wrapper.getAvailableAddressesAsync();
- return availableAddresses;
- }
- /**
- * Signs an orderHash and returns it's elliptic curve signature.
- * This method currently supports TestRPC, Geth and Parity above and below V1.6.6
- * @param orderHash Hex encoded orderHash to sign.
- * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
- * must be available via the Web3.Provider supplied to 0x.js.
- * @param shouldAddPersonalMessagePrefix Some signers add the personal message prefix `\x19Ethereum Signed Message`
- * themselves (e.g Parity Signer, Ledger, TestRPC) and others expect it to already be done by the client
- * (e.g Metamask). Depending on which signer this request is going to, decide on whether to add the prefix
- * before sending the request.
- * @return An object containing the Elliptic curve signature parameters generated by signing the orderHash.
- */
- public async signOrderHashAsync(
- orderHash: string,
- signerAddress: string,
- shouldAddPersonalMessagePrefix: boolean,
- ): Promise<ECSignature> {
- assert.isHexString('orderHash', orderHash);
- await assert.isSenderAddressAsync('signerAddress', signerAddress, this._web3Wrapper);
+ const unit = new BigNumber(10).pow(decimals);
+ const baseUnitAmount = amount.times(unit);
+ const hasDecimals = baseUnitAmount.decimalPlaces() !== 0;
+ if (hasDecimals) {
+ throw new Error(`Invalid unit amount: ${amount.toString()} - Too many decimal places`);
+ }
+ return baseUnitAmount;
+ }
+ /**
+ * Computes the orderHash for a supplied order.
+ * @param order An object that conforms to the Order or SignedOrder interface definitions.
+ * @return The resulting orderHash from hashing the supplied order.
+ */
+ @decorators.syncZeroExErrorHandler
+ public static getOrderHashHex(order: Order | SignedOrder): string {
+ assert.doesConformToSchema('order', order, schemas.orderSchema);
+ const orderHashHex = utils.getOrderHashHex(order);
+ return orderHashHex;
+ }
+ /**
+ * Instantiates a new ZeroEx instance that provides the public interface to the 0x.js library.
+ * @param provider The Web3.js Provider instance you would like the 0x.js library to use for interacting with
+ * the Ethereum network.
+ * @param config The configuration object. Look up the type for the description.
+ * @return An instance of the 0x.js ZeroEx class.
+ */
+ constructor(provider: Web3Provider, config: ZeroExConfig) {
+ assert.isWeb3Provider('provider', provider);
+ assert.doesConformToSchema('config', config, zeroExConfigSchema);
+ const artifactJSONs = _.values(artifacts);
+ const abiArrays = _.map(artifactJSONs, artifact => artifact.abi);
+ this._abiDecoder = new AbiDecoder(abiArrays);
+ const defaults = {
+ gasPrice: config.gasPrice,
+ };
+ this._web3Wrapper = new Web3Wrapper(provider, defaults);
+ this.proxy = new TokenTransferProxyWrapper(
+ this._web3Wrapper,
+ config.networkId,
+ config.tokenTransferProxyContractAddress,
+ );
+ this.token = new TokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.proxy);
+ this.exchange = new ExchangeWrapper(
+ this._web3Wrapper,
+ config.networkId,
+ this._abiDecoder,
+ this.token,
+ config.exchangeContractAddress,
+ config.zrxContractAddress,
+ );
+ this.tokenRegistry = new TokenRegistryWrapper(
+ this._web3Wrapper,
+ config.networkId,
+ config.tokenRegistryContractAddress,
+ );
+ this.etherToken = new EtherTokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.token);
+ this.orderStateWatcher = new OrderStateWatcher(
+ this._web3Wrapper,
+ this._abiDecoder,
+ this.token,
+ this.exchange,
+ config.orderWatcherConfig,
+ );
+ }
+ /**
+ * Sets a new web3 provider for 0x.js. Updating the provider will stop all
+ * subscriptions so you will need to re-subscribe to all events relevant to your app after this call.
+ * @param provider The Web3Provider you would like the 0x.js library to use from now on.
+ * @param networkId The id of the network your provider is connected to
+ */
+ public setProvider(provider: Web3Provider, networkId: number): void {
+ this._web3Wrapper.setProvider(provider);
+ (this.exchange as any)._invalidateContractInstances();
+ (this.exchange as any)._setNetworkId(networkId);
+ (this.tokenRegistry as any)._invalidateContractInstance();
+ (this.tokenRegistry as any)._setNetworkId(networkId);
+ (this.token as any)._invalidateContractInstances();
+ (this.token as any)._setNetworkId(networkId);
+ (this.proxy as any)._invalidateContractInstance();
+ (this.proxy as any)._setNetworkId(networkId);
+ (this.etherToken as any)._invalidateContractInstance();
+ (this.etherToken as any)._setNetworkId(networkId);
+ }
+ /**
+ * Get user Ethereum addresses available through the supplied web3 provider available for sending transactions.
+ * @return An array of available user Ethereum addresses.
+ */
+ public async getAvailableAddressesAsync(): Promise<string[]> {
+ const availableAddresses = await this._web3Wrapper.getAvailableAddressesAsync();
+ return availableAddresses;
+ }
+ /**
+ * Signs an orderHash and returns it's elliptic curve signature.
+ * This method currently supports TestRPC, Geth and Parity above and below V1.6.6
+ * @param orderHash Hex encoded orderHash to sign.
+ * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
+ * must be available via the Web3.Provider supplied to 0x.js.
+ * @param shouldAddPersonalMessagePrefix Some signers add the personal message prefix `\x19Ethereum Signed Message`
+ * themselves (e.g Parity Signer, Ledger, TestRPC) and others expect it to already be done by the client
+ * (e.g Metamask). Depending on which signer this request is going to, decide on whether to add the prefix
+ * before sending the request.
+ * @return An object containing the Elliptic curve signature parameters generated by signing the orderHash.
+ */
+ public async signOrderHashAsync(
+ orderHash: string,
+ signerAddress: string,
+ shouldAddPersonalMessagePrefix: boolean,
+ ): Promise<ECSignature> {
+ assert.isHexString('orderHash', orderHash);
+ await assert.isSenderAddressAsync('signerAddress', signerAddress, this._web3Wrapper);
- let msgHashHex = orderHash;
- if (shouldAddPersonalMessagePrefix) {
- const orderHashBuff = ethUtil.toBuffer(orderHash);
- const msgHashBuff = ethUtil.hashPersonalMessage(orderHashBuff);
- msgHashHex = ethUtil.bufferToHex(msgHashBuff);
- }
+ let msgHashHex = orderHash;
+ if (shouldAddPersonalMessagePrefix) {
+ const orderHashBuff = ethUtil.toBuffer(orderHash);
+ const msgHashBuff = ethUtil.hashPersonalMessage(orderHashBuff);
+ msgHashHex = ethUtil.bufferToHex(msgHashBuff);
+ }
- const signature = await this._web3Wrapper.signTransactionAsync(signerAddress, msgHashHex);
+ const signature = await this._web3Wrapper.signTransactionAsync(signerAddress, msgHashHex);
- // HACK: There is no consensus on whether the signatureHex string should be formatted as
- // v + r + s OR r + s + v, and different clients (even different versions of the same client)
- // return the signature params in different orders. In order to support all client implementations,
- // we parse the signature in both ways, and evaluate if either one is a valid signature.
- const validVParamValues = [27, 28];
- const ecSignatureVRS = signatureUtils.parseSignatureHexAsVRS(signature);
- if (_.includes(validVParamValues, ecSignatureVRS.v)) {
- const isValidVRSSignature = ZeroEx.isValidSignature(orderHash, ecSignatureVRS, signerAddress);
- if (isValidVRSSignature) {
- return ecSignatureVRS;
- }
- }
+ // HACK: There is no consensus on whether the signatureHex string should be formatted as
+ // v + r + s OR r + s + v, and different clients (even different versions of the same client)
+ // return the signature params in different orders. In order to support all client implementations,
+ // we parse the signature in both ways, and evaluate if either one is a valid signature.
+ const validVParamValues = [27, 28];
+ const ecSignatureVRS = signatureUtils.parseSignatureHexAsVRS(signature);
+ if (_.includes(validVParamValues, ecSignatureVRS.v)) {
+ const isValidVRSSignature = ZeroEx.isValidSignature(orderHash, ecSignatureVRS, signerAddress);
+ if (isValidVRSSignature) {
+ return ecSignatureVRS;
+ }
+ }
- const ecSignatureRSV = signatureUtils.parseSignatureHexAsRSV(signature);
- if (_.includes(validVParamValues, ecSignatureRSV.v)) {
- const isValidRSVSignature = ZeroEx.isValidSignature(orderHash, ecSignatureRSV, signerAddress);
- if (isValidRSVSignature) {
- return ecSignatureRSV;
- }
- }
+ const ecSignatureRSV = signatureUtils.parseSignatureHexAsRSV(signature);
+ if (_.includes(validVParamValues, ecSignatureRSV.v)) {
+ const isValidRSVSignature = ZeroEx.isValidSignature(orderHash, ecSignatureRSV, signerAddress);
+ if (isValidRSVSignature) {
+ return ecSignatureRSV;
+ }
+ }
- throw new Error(ZeroExError.InvalidSignature);
- }
- /**
- * Waits for a transaction to be mined and returns the transaction receipt.
- * @param txHash Transaction hash
- * @param pollingIntervalMs How often (in ms) should we check if the transaction is mined.
- * @param timeoutMs How long (in ms) to poll for transaction mined until aborting.
- * @return Transaction receipt with decoded log args.
- */
- public async awaitTransactionMinedAsync(
- txHash: string,
- pollingIntervalMs = 1000,
- timeoutMs?: number,
- ): Promise<TransactionReceiptWithDecodedLogs> {
- let timeoutExceeded = false;
- if (timeoutMs) {
- setTimeout(() => (timeoutExceeded = true), timeoutMs);
- }
+ throw new Error(ZeroExError.InvalidSignature);
+ }
+ /**
+ * Waits for a transaction to be mined and returns the transaction receipt.
+ * @param txHash Transaction hash
+ * @param pollingIntervalMs How often (in ms) should we check if the transaction is mined.
+ * @param timeoutMs How long (in ms) to poll for transaction mined until aborting.
+ * @return Transaction receipt with decoded log args.
+ */
+ public async awaitTransactionMinedAsync(
+ txHash: string,
+ pollingIntervalMs = 1000,
+ timeoutMs?: number,
+ ): Promise<TransactionReceiptWithDecodedLogs> {
+ let timeoutExceeded = false;
+ if (timeoutMs) {
+ setTimeout(() => (timeoutExceeded = true), timeoutMs);
+ }
- const txReceiptPromise = new Promise(
- (resolve: (receipt: TransactionReceiptWithDecodedLogs) => void, reject) => {
- const intervalId = intervalUtils.setAsyncExcludingInterval(
- async () => {
- if (timeoutExceeded) {
- intervalUtils.clearAsyncExcludingInterval(intervalId);
- return reject(ZeroExError.TransactionMiningTimeout);
- }
+ const txReceiptPromise = new Promise(
+ (resolve: (receipt: TransactionReceiptWithDecodedLogs) => void, reject) => {
+ const intervalId = intervalUtils.setAsyncExcludingInterval(
+ async () => {
+ if (timeoutExceeded) {
+ intervalUtils.clearAsyncExcludingInterval(intervalId);
+ return reject(ZeroExError.TransactionMiningTimeout);
+ }
- const transactionReceipt = await this._web3Wrapper.getTransactionReceiptAsync(txHash);
- if (!_.isNull(transactionReceipt)) {
- intervalUtils.clearAsyncExcludingInterval(intervalId);
- const logsWithDecodedArgs = _.map(
- transactionReceipt.logs,
- this._abiDecoder.tryToDecodeLogOrNoop.bind(this._abiDecoder),
- );
- const transactionReceiptWithDecodedLogArgs: TransactionReceiptWithDecodedLogs = {
- ...transactionReceipt,
- logs: logsWithDecodedArgs,
- };
- resolve(transactionReceiptWithDecodedLogArgs);
- }
- },
- pollingIntervalMs,
- (err: Error) => {
- intervalUtils.clearAsyncExcludingInterval(intervalId);
- reject(err);
- },
- );
- },
- );
+ const transactionReceipt = await this._web3Wrapper.getTransactionReceiptAsync(txHash);
+ if (!_.isNull(transactionReceipt)) {
+ intervalUtils.clearAsyncExcludingInterval(intervalId);
+ const logsWithDecodedArgs = _.map(
+ transactionReceipt.logs,
+ this._abiDecoder.tryToDecodeLogOrNoop.bind(this._abiDecoder),
+ );
+ const transactionReceiptWithDecodedLogArgs: TransactionReceiptWithDecodedLogs = {
+ ...transactionReceipt,
+ logs: logsWithDecodedArgs,
+ };
+ resolve(transactionReceiptWithDecodedLogArgs);
+ }
+ },
+ pollingIntervalMs,
+ (err: Error) => {
+ intervalUtils.clearAsyncExcludingInterval(intervalId);
+ reject(err);
+ },
+ );
+ },
+ );
- return txReceiptPromise;
- }
- /*
+ return txReceiptPromise;
+ }
+ /*
* HACK: `TokenWrapper` needs a token transfer proxy address. `TokenTransferProxy` address is fetched from
* an `ExchangeWrapper`. `ExchangeWrapper` needs `TokenWrapper` to validate orders, creating a dependency cycle.
* In order to break this - we create this function here and pass it as a parameter to the `TokenWrapper`
* and `ProxyWrapper`.
*/
- private async _getTokenTransferProxyAddressAsync(): Promise<string> {
- const tokenTransferProxyAddress = await (this.exchange as any)._getTokenTransferProxyAddressAsync();
- return tokenTransferProxyAddress;
- }
+ private async _getTokenTransferProxyAddressAsync(): Promise<string> {
+ const tokenTransferProxyAddress = await (this.exchange as any)._getTokenTransferProxyAddressAsync();
+ return tokenTransferProxyAddress;
+ }
}
diff --git a/packages/0x.js/src/artifacts.ts b/packages/0x.js/src/artifacts.ts
index cbacd7d56..7d4bd6757 100644
--- a/packages/0x.js/src/artifacts.ts
+++ b/packages/0x.js/src/artifacts.ts
@@ -8,11 +8,11 @@ import * as ZRXArtifact from './artifacts/ZRX.json';
import { Artifact } from './types';
export const artifacts = {
- ZRXArtifact: (ZRXArtifact as any) as Artifact,
- DummyTokenArtifact: (DummyTokenArtifact as any) as Artifact,
- TokenArtifact: (TokenArtifact as any) as Artifact,
- ExchangeArtifact: (ExchangeArtifact as any) as Artifact,
- EtherTokenArtifact: (EtherTokenArtifact as any) as Artifact,
- TokenRegistryArtifact: (TokenRegistryArtifact as any) as Artifact,
- TokenTransferProxyArtifact: (TokenTransferProxyArtifact as any) as Artifact,
+ ZRXArtifact: (ZRXArtifact as any) as Artifact,
+ DummyTokenArtifact: (DummyTokenArtifact as any) as Artifact,
+ TokenArtifact: (TokenArtifact as any) as Artifact,
+ ExchangeArtifact: (ExchangeArtifact as any) as Artifact,
+ EtherTokenArtifact: (EtherTokenArtifact as any) as Artifact,
+ TokenRegistryArtifact: (TokenRegistryArtifact as any) as Artifact,
+ TokenTransferProxyArtifact: (TokenTransferProxyArtifact as any) as Artifact,
};
diff --git a/packages/0x.js/src/artifacts/DummyToken.json b/packages/0x.js/src/artifacts/DummyToken.json
index f64a8cd3d..f96d63c21 100644
--- a/packages/0x.js/src/artifacts/DummyToken.json
+++ b/packages/0x.js/src/artifacts/DummyToken.json
@@ -1,22 +1,22 @@
{
- "contract_name": "DummyToken",
- "abi": [
- {
- "constant": false,
- "inputs": [
- {
- "name": "_target",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "setBalance",
- "outputs": [],
- "payable": false,
- "type": "function"
- }
- ]
+ "contract_name": "DummyToken",
+ "abi": [
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_target",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "setBalance",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ }
+ ]
}
diff --git a/packages/0x.js/src/artifacts/EtherToken.json b/packages/0x.js/src/artifacts/EtherToken.json
index 26cca57cd..ca5f9c035 100644
--- a/packages/0x.js/src/artifacts/EtherToken.json
+++ b/packages/0x.js/src/artifacts/EtherToken.json
@@ -1,287 +1,287 @@
{
- "contract_name": "EtherToken",
- "abi": [
- {
- "constant": true,
- "inputs": [],
- "name": "name",
- "outputs": [
- {
- "name": "",
- "type": "string"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_spender",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "approve",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "totalSupply",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_from",
- "type": "address"
- },
- {
- "name": "_to",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "transferFrom",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "amount",
- "type": "uint256"
- }
- ],
- "name": "withdraw",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "decimals",
- "outputs": [
- {
- "name": "",
- "type": "uint8"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- }
- ],
- "name": "balanceOf",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "symbol",
- "outputs": [
- {
- "name": "",
- "type": "string"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_to",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "transfer",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [],
- "name": "deposit",
- "outputs": [],
- "payable": true,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- },
- {
- "name": "_spender",
- "type": "address"
- }
- ],
- "name": "allowance",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "payable": true,
- "type": "fallback"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_from",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "_to",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_owner",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "_spender",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_owner",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Deposit",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_owner",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Withdrawal",
- "type": "event"
- }
- ],
- "networks": {
- "1": {
- "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
- },
- "3": {
- "address": "0xc00fd9820cd2898cc4c054b7bf142de637ad129a"
- },
- "4": {
- "address": "0xc778417e063141139fce010982780140aa0cd5ab"
- },
- "42": {
- "address": "0x653e49e301e508a13237c0ddc98ae7d4cd2667a1"
- },
- "50": {
- "address": "0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c"
- }
- }
+ "contract_name": "EtherToken",
+ "abi": [
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "withdraw",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "decimals",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "symbol",
+ "outputs": [
+ {
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [],
+ "name": "deposit",
+ "outputs": [],
+ "payable": true,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "name": "_spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "payable": true,
+ "type": "fallback"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Deposit",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Withdrawal",
+ "type": "event"
+ }
+ ],
+ "networks": {
+ "1": {
+ "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
+ },
+ "3": {
+ "address": "0xc00fd9820cd2898cc4c054b7bf142de637ad129a"
+ },
+ "4": {
+ "address": "0xc778417e063141139fce010982780140aa0cd5ab"
+ },
+ "42": {
+ "address": "0x653e49e301e508a13237c0ddc98ae7d4cd2667a1"
+ },
+ "50": {
+ "address": "0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c"
+ }
+ }
}
diff --git a/packages/0x.js/src/artifacts/Exchange.json b/packages/0x.js/src/artifacts/Exchange.json
index af8db7360..44bb41d4d 100644
--- a/packages/0x.js/src/artifacts/Exchange.json
+++ b/packages/0x.js/src/artifacts/Exchange.json
@@ -1,610 +1,610 @@
{
- "contract_name": "Exchange",
- "abi": [
- {
- "constant": true,
- "inputs": [
- {
- "name": "numerator",
- "type": "uint256"
- },
- {
- "name": "denominator",
- "type": "uint256"
- },
- {
- "name": "target",
- "type": "uint256"
- }
- ],
- "name": "isRoundingError",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "bytes32"
- }
- ],
- "name": "filled",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "bytes32"
- }
- ],
- "name": "cancelled",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5][]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6][]"
- },
- {
- "name": "fillTakerTokenAmount",
- "type": "uint256"
- },
- {
- "name": "shouldThrowOnInsufficientBalanceOrAllowance",
- "type": "bool"
- },
- {
- "name": "v",
- "type": "uint8[]"
- },
- {
- "name": "r",
- "type": "bytes32[]"
- },
- {
- "name": "s",
- "type": "bytes32[]"
- }
- ],
- "name": "fillOrdersUpTo",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6]"
- },
- {
- "name": "cancelTakerTokenAmount",
- "type": "uint256"
- }
- ],
- "name": "cancelOrder",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "ZRX_TOKEN_CONTRACT",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5][]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6][]"
- },
- {
- "name": "fillTakerTokenAmounts",
- "type": "uint256[]"
- },
- {
- "name": "v",
- "type": "uint8[]"
- },
- {
- "name": "r",
- "type": "bytes32[]"
- },
- {
- "name": "s",
- "type": "bytes32[]"
- }
- ],
- "name": "batchFillOrKillOrders",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6]"
- },
- {
- "name": "fillTakerTokenAmount",
- "type": "uint256"
- },
- {
- "name": "v",
- "type": "uint8"
- },
- {
- "name": "r",
- "type": "bytes32"
- },
- {
- "name": "s",
- "type": "bytes32"
- }
- ],
- "name": "fillOrKillOrder",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "orderHash",
- "type": "bytes32"
- }
- ],
- "name": "getUnavailableTakerTokenAmount",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "signer",
- "type": "address"
- },
- {
- "name": "hash",
- "type": "bytes32"
- },
- {
- "name": "v",
- "type": "uint8"
- },
- {
- "name": "r",
- "type": "bytes32"
- },
- {
- "name": "s",
- "type": "bytes32"
- }
- ],
- "name": "isValidSignature",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "numerator",
- "type": "uint256"
- },
- {
- "name": "denominator",
- "type": "uint256"
- },
- {
- "name": "target",
- "type": "uint256"
- }
- ],
- "name": "getPartialAmount",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "TOKEN_TRANSFER_PROXY_CONTRACT",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5][]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6][]"
- },
- {
- "name": "fillTakerTokenAmounts",
- "type": "uint256[]"
- },
- {
- "name": "shouldThrowOnInsufficientBalanceOrAllowance",
- "type": "bool"
- },
- {
- "name": "v",
- "type": "uint8[]"
- },
- {
- "name": "r",
- "type": "bytes32[]"
- },
- {
- "name": "s",
- "type": "bytes32[]"
- }
- ],
- "name": "batchFillOrders",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5][]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6][]"
- },
- {
- "name": "cancelTakerTokenAmounts",
- "type": "uint256[]"
- }
- ],
- "name": "batchCancelOrders",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6]"
- },
- {
- "name": "fillTakerTokenAmount",
- "type": "uint256"
- },
- {
- "name": "shouldThrowOnInsufficientBalanceOrAllowance",
- "type": "bool"
- },
- {
- "name": "v",
- "type": "uint8"
- },
- {
- "name": "r",
- "type": "bytes32"
- },
- {
- "name": "s",
- "type": "bytes32"
- }
- ],
- "name": "fillOrder",
- "outputs": [
- {
- "name": "filledTakerTokenAmount",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6]"
- }
- ],
- "name": "getOrderHash",
- "outputs": [
- {
- "name": "",
- "type": "bytes32"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "EXTERNAL_QUERY_GAS_LIMIT",
- "outputs": [
- {
- "name": "",
- "type": "uint16"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "VERSION",
- "outputs": [
- {
- "name": "",
- "type": "string"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "inputs": [
- {
- "name": "_zrxToken",
- "type": "address"
- },
- {
- "name": "_tokenTransferProxy",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "constructor"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "maker",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "taker",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "feeRecipient",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "makerToken",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "takerToken",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "filledMakerTokenAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "name": "filledTakerTokenAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "name": "paidMakerFee",
- "type": "uint256"
- },
- {
- "indexed": false,
- "name": "paidTakerFee",
- "type": "uint256"
- },
- {
- "indexed": true,
- "name": "tokens",
- "type": "bytes32"
- },
- {
- "indexed": false,
- "name": "orderHash",
- "type": "bytes32"
- }
- ],
- "name": "LogFill",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "maker",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "feeRecipient",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "makerToken",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "takerToken",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "cancelledMakerTokenAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "name": "cancelledTakerTokenAmount",
- "type": "uint256"
- },
- {
- "indexed": true,
- "name": "tokens",
- "type": "bytes32"
- },
- {
- "indexed": false,
- "name": "orderHash",
- "type": "bytes32"
- }
- ],
- "name": "LogCancel",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "errorId",
- "type": "uint8"
- },
- {
- "indexed": true,
- "name": "orderHash",
- "type": "bytes32"
- }
- ],
- "name": "LogError",
- "type": "event"
- }
- ],
- "networks": {
- "1": {
- "address": "0x12459c951127e0c374ff9105dda097662a027093"
- },
- "3": {
- "address": "0x479cc461fecd078f766ecc58533d6f69580cf3ac"
- },
- "4": {
- "address": "0x1d16ef40fac01cec8adac2ac49427b9384192c05"
- },
- "42": {
- "address": "0x90fe2af704b34e0224bf2299c838e04d4dcf1364"
- },
- "50": {
- "address": "0x48bacb9266a570d521063ef5dd96e61686dbe788"
- }
- }
+ "contract_name": "Exchange",
+ "abi": [
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "numerator",
+ "type": "uint256"
+ },
+ {
+ "name": "denominator",
+ "type": "uint256"
+ },
+ {
+ "name": "target",
+ "type": "uint256"
+ }
+ ],
+ "name": "isRoundingError",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "name": "filled",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "name": "cancelled",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5][]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6][]"
+ },
+ {
+ "name": "fillTakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "shouldThrowOnInsufficientBalanceOrAllowance",
+ "type": "bool"
+ },
+ {
+ "name": "v",
+ "type": "uint8[]"
+ },
+ {
+ "name": "r",
+ "type": "bytes32[]"
+ },
+ {
+ "name": "s",
+ "type": "bytes32[]"
+ }
+ ],
+ "name": "fillOrdersUpTo",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6]"
+ },
+ {
+ "name": "cancelTakerTokenAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "cancelOrder",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "ZRX_TOKEN_CONTRACT",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5][]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6][]"
+ },
+ {
+ "name": "fillTakerTokenAmounts",
+ "type": "uint256[]"
+ },
+ {
+ "name": "v",
+ "type": "uint8[]"
+ },
+ {
+ "name": "r",
+ "type": "bytes32[]"
+ },
+ {
+ "name": "s",
+ "type": "bytes32[]"
+ }
+ ],
+ "name": "batchFillOrKillOrders",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6]"
+ },
+ {
+ "name": "fillTakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "fillOrKillOrder",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "orderHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getUnavailableTakerTokenAmount",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "signer",
+ "type": "address"
+ },
+ {
+ "name": "hash",
+ "type": "bytes32"
+ },
+ {
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "isValidSignature",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "numerator",
+ "type": "uint256"
+ },
+ {
+ "name": "denominator",
+ "type": "uint256"
+ },
+ {
+ "name": "target",
+ "type": "uint256"
+ }
+ ],
+ "name": "getPartialAmount",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "TOKEN_TRANSFER_PROXY_CONTRACT",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5][]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6][]"
+ },
+ {
+ "name": "fillTakerTokenAmounts",
+ "type": "uint256[]"
+ },
+ {
+ "name": "shouldThrowOnInsufficientBalanceOrAllowance",
+ "type": "bool"
+ },
+ {
+ "name": "v",
+ "type": "uint8[]"
+ },
+ {
+ "name": "r",
+ "type": "bytes32[]"
+ },
+ {
+ "name": "s",
+ "type": "bytes32[]"
+ }
+ ],
+ "name": "batchFillOrders",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5][]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6][]"
+ },
+ {
+ "name": "cancelTakerTokenAmounts",
+ "type": "uint256[]"
+ }
+ ],
+ "name": "batchCancelOrders",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6]"
+ },
+ {
+ "name": "fillTakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "shouldThrowOnInsufficientBalanceOrAllowance",
+ "type": "bool"
+ },
+ {
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "fillOrder",
+ "outputs": [
+ {
+ "name": "filledTakerTokenAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6]"
+ }
+ ],
+ "name": "getOrderHash",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "EXTERNAL_QUERY_GAS_LIMIT",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint16"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "VERSION",
+ "outputs": [
+ {
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "name": "_zrxToken",
+ "type": "address"
+ },
+ {
+ "name": "_tokenTransferProxy",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "constructor"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "taker",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "feeRecipient",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "makerToken",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "takerToken",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "filledMakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "name": "filledTakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "name": "paidMakerFee",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "name": "paidTakerFee",
+ "type": "uint256"
+ },
+ {
+ "indexed": true,
+ "name": "tokens",
+ "type": "bytes32"
+ },
+ {
+ "indexed": false,
+ "name": "orderHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "LogFill",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "feeRecipient",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "makerToken",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "takerToken",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "cancelledMakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "name": "cancelledTakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": true,
+ "name": "tokens",
+ "type": "bytes32"
+ },
+ {
+ "indexed": false,
+ "name": "orderHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "LogCancel",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "errorId",
+ "type": "uint8"
+ },
+ {
+ "indexed": true,
+ "name": "orderHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "LogError",
+ "type": "event"
+ }
+ ],
+ "networks": {
+ "1": {
+ "address": "0x12459c951127e0c374ff9105dda097662a027093"
+ },
+ "3": {
+ "address": "0x479cc461fecd078f766ecc58533d6f69580cf3ac"
+ },
+ "4": {
+ "address": "0x1d16ef40fac01cec8adac2ac49427b9384192c05"
+ },
+ "42": {
+ "address": "0x90fe2af704b34e0224bf2299c838e04d4dcf1364"
+ },
+ "50": {
+ "address": "0x48bacb9266a570d521063ef5dd96e61686dbe788"
+ }
+ }
}
diff --git a/packages/0x.js/src/artifacts/Token.json b/packages/0x.js/src/artifacts/Token.json
index 3b5a86ae0..bd4208275 100644
--- a/packages/0x.js/src/artifacts/Token.json
+++ b/packages/0x.js/src/artifacts/Token.json
@@ -1,172 +1,172 @@
{
- "contract_name": "Token",
- "abi": [
- {
- "constant": false,
- "inputs": [
- {
- "name": "_spender",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "approve",
- "outputs": [
- {
- "name": "success",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "totalSupply",
- "outputs": [
- {
- "name": "supply",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_from",
- "type": "address"
- },
- {
- "name": "_to",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "transferFrom",
- "outputs": [
- {
- "name": "success",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- }
- ],
- "name": "balanceOf",
- "outputs": [
- {
- "name": "balance",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_to",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "transfer",
- "outputs": [
- {
- "name": "success",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- },
- {
- "name": "_spender",
- "type": "address"
- }
- ],
- "name": "allowance",
- "outputs": [
- {
- "name": "remaining",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_from",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "_to",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_owner",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "_spender",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- }
- ]
+ "contract_name": "Token",
+ "abi": [
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "name": "success",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "name": "supply",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "name": "success",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "name": "balance",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "name": "success",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "name": "_spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "name": "remaining",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ }
+ ]
}
diff --git a/packages/0x.js/src/artifacts/TokenRegistry.json b/packages/0x.js/src/artifacts/TokenRegistry.json
index 0f583628c..490ea0c34 100644
--- a/packages/0x.js/src/artifacts/TokenRegistry.json
+++ b/packages/0x.js/src/artifacts/TokenRegistry.json
@@ -1,547 +1,547 @@
{
- "contract_name": "TokenRegistry",
- "abi": [
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_index",
- "type": "uint256"
- }
- ],
- "name": "removeToken",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_name",
- "type": "string"
- }
- ],
- "name": "getTokenAddressByName",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_symbol",
- "type": "string"
- }
- ],
- "name": "getTokenAddressBySymbol",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_swarmHash",
- "type": "bytes"
- }
- ],
- "name": "setTokenSwarmHash",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- }
- ],
- "name": "getTokenMetaData",
- "outputs": [
- {
- "name": "",
- "type": "address"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "uint8"
- },
- {
- "name": "",
- "type": "bytes"
- },
- {
- "name": "",
- "type": "bytes"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "owner",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_name",
- "type": "string"
- },
- {
- "name": "_symbol",
- "type": "string"
- },
- {
- "name": "_decimals",
- "type": "uint8"
- },
- {
- "name": "_ipfsHash",
- "type": "bytes"
- },
- {
- "name": "_swarmHash",
- "type": "bytes"
- }
- ],
- "name": "addToken",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_name",
- "type": "string"
- }
- ],
- "name": "setTokenName",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "name": "tokens",
- "outputs": [
- {
- "name": "token",
- "type": "address"
- },
- {
- "name": "name",
- "type": "string"
- },
- {
- "name": "symbol",
- "type": "string"
- },
- {
- "name": "decimals",
- "type": "uint8"
- },
- {
- "name": "ipfsHash",
- "type": "bytes"
- },
- {
- "name": "swarmHash",
- "type": "bytes"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "name": "tokenAddresses",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_name",
- "type": "string"
- }
- ],
- "name": "getTokenByName",
- "outputs": [
- {
- "name": "",
- "type": "address"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "uint8"
- },
- {
- "name": "",
- "type": "bytes"
- },
- {
- "name": "",
- "type": "bytes"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "getTokenAddresses",
- "outputs": [
- {
- "name": "",
- "type": "address[]"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_ipfsHash",
- "type": "bytes"
- }
- ],
- "name": "setTokenIpfsHash",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_symbol",
- "type": "string"
- }
- ],
- "name": "getTokenBySymbol",
- "outputs": [
- {
- "name": "",
- "type": "address"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "uint8"
- },
- {
- "name": "",
- "type": "bytes"
- },
- {
- "name": "",
- "type": "bytes"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_symbol",
- "type": "string"
- }
- ],
- "name": "setTokenSymbol",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "name",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "symbol",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "decimals",
- "type": "uint8"
- },
- {
- "indexed": false,
- "name": "ipfsHash",
- "type": "bytes"
- },
- {
- "indexed": false,
- "name": "swarmHash",
- "type": "bytes"
- }
- ],
- "name": "LogAddToken",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "name",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "symbol",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "decimals",
- "type": "uint8"
- },
- {
- "indexed": false,
- "name": "ipfsHash",
- "type": "bytes"
- },
- {
- "indexed": false,
- "name": "swarmHash",
- "type": "bytes"
- }
- ],
- "name": "LogRemoveToken",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "oldName",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "newName",
- "type": "string"
- }
- ],
- "name": "LogTokenNameChange",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "oldSymbol",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "newSymbol",
- "type": "string"
- }
- ],
- "name": "LogTokenSymbolChange",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "oldIpfsHash",
- "type": "bytes"
- },
- {
- "indexed": false,
- "name": "newIpfsHash",
- "type": "bytes"
- }
- ],
- "name": "LogTokenIpfsHashChange",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "oldSwarmHash",
- "type": "bytes"
- },
- {
- "indexed": false,
- "name": "newSwarmHash",
- "type": "bytes"
- }
- ],
- "name": "LogTokenSwarmHashChange",
- "type": "event"
- }
- ],
- "networks": {
- "1": {
- "address": "0x926a74c5c36adf004c87399e65f75628b0f98d2c"
- },
- "3": {
- "address": "0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed"
- },
- "4": {
- "address": "0x4e9aad8184de8833365fea970cd9149372fdf1e6"
- },
- "42": {
- "address": "0xf18e504561f4347bea557f3d4558f559dddbae7f"
- },
- "50": {
- "address": "0x0b1ba0af832d7c05fd64161e0db78e85978e8082"
- }
- }
+ "contract_name": "TokenRegistry",
+ "abi": [
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_index",
+ "type": "uint256"
+ }
+ ],
+ "name": "removeToken",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_name",
+ "type": "string"
+ }
+ ],
+ "name": "getTokenAddressByName",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_symbol",
+ "type": "string"
+ }
+ ],
+ "name": "getTokenAddressBySymbol",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_swarmHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "setTokenSwarmHash",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ }
+ ],
+ "name": "getTokenMetaData",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_name",
+ "type": "string"
+ },
+ {
+ "name": "_symbol",
+ "type": "string"
+ },
+ {
+ "name": "_decimals",
+ "type": "uint8"
+ },
+ {
+ "name": "_ipfsHash",
+ "type": "bytes"
+ },
+ {
+ "name": "_swarmHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "addToken",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_name",
+ "type": "string"
+ }
+ ],
+ "name": "setTokenName",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "tokens",
+ "outputs": [
+ {
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "symbol",
+ "type": "string"
+ },
+ {
+ "name": "decimals",
+ "type": "uint8"
+ },
+ {
+ "name": "ipfsHash",
+ "type": "bytes"
+ },
+ {
+ "name": "swarmHash",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "tokenAddresses",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_name",
+ "type": "string"
+ }
+ ],
+ "name": "getTokenByName",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getTokenAddresses",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address[]"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_ipfsHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "setTokenIpfsHash",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_symbol",
+ "type": "string"
+ }
+ ],
+ "name": "getTokenBySymbol",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_symbol",
+ "type": "string"
+ }
+ ],
+ "name": "setTokenSymbol",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "symbol",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "decimals",
+ "type": "uint8"
+ },
+ {
+ "indexed": false,
+ "name": "ipfsHash",
+ "type": "bytes"
+ },
+ {
+ "indexed": false,
+ "name": "swarmHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "LogAddToken",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "symbol",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "decimals",
+ "type": "uint8"
+ },
+ {
+ "indexed": false,
+ "name": "ipfsHash",
+ "type": "bytes"
+ },
+ {
+ "indexed": false,
+ "name": "swarmHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "LogRemoveToken",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "oldName",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "newName",
+ "type": "string"
+ }
+ ],
+ "name": "LogTokenNameChange",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "oldSymbol",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "newSymbol",
+ "type": "string"
+ }
+ ],
+ "name": "LogTokenSymbolChange",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "oldIpfsHash",
+ "type": "bytes"
+ },
+ {
+ "indexed": false,
+ "name": "newIpfsHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "LogTokenIpfsHashChange",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "oldSwarmHash",
+ "type": "bytes"
+ },
+ {
+ "indexed": false,
+ "name": "newSwarmHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "LogTokenSwarmHashChange",
+ "type": "event"
+ }
+ ],
+ "networks": {
+ "1": {
+ "address": "0x926a74c5c36adf004c87399e65f75628b0f98d2c"
+ },
+ "3": {
+ "address": "0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed"
+ },
+ "4": {
+ "address": "0x4e9aad8184de8833365fea970cd9149372fdf1e6"
+ },
+ "42": {
+ "address": "0xf18e504561f4347bea557f3d4558f559dddbae7f"
+ },
+ "50": {
+ "address": "0x0b1ba0af832d7c05fd64161e0db78e85978e8082"
+ }
+ }
}
diff --git a/packages/0x.js/src/artifacts/TokenTransferProxy.json b/packages/0x.js/src/artifacts/TokenTransferProxy.json
index 8cf551ddb..5e45e4903 100644
--- a/packages/0x.js/src/artifacts/TokenTransferProxy.json
+++ b/packages/0x.js/src/artifacts/TokenTransferProxy.json
@@ -1,187 +1,187 @@
{
- "contract_name": "TokenTransferProxy",
- "abi": [
- {
- "constant": false,
- "inputs": [
- {
- "name": "token",
- "type": "address"
- },
- {
- "name": "from",
- "type": "address"
- },
- {
- "name": "to",
- "type": "address"
- },
- {
- "name": "value",
- "type": "uint256"
- }
- ],
- "name": "transferFrom",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "target",
- "type": "address"
- }
- ],
- "name": "addAuthorizedAddress",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "name": "authorities",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "target",
- "type": "address"
- }
- ],
- "name": "removeAuthorizedAddress",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "owner",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "name": "authorized",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "getAuthorizedAddresses",
- "outputs": [
- {
- "name": "",
- "type": "address[]"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "target",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "caller",
- "type": "address"
- }
- ],
- "name": "LogAuthorizedAddressAdded",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "target",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "caller",
- "type": "address"
- }
- ],
- "name": "LogAuthorizedAddressRemoved",
- "type": "event"
- }
- ],
- "networks": {
- "1": {
- "address": "0x8da0d80f5007ef1e431dd2127178d224e32c2ef4"
- },
- "3": {
- "address": "0x4e9aad8184de8833365fea970cd9149372fdf1e6"
- },
- "4": {
- "address": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d"
- },
- "42": {
- "address": "0x087eed4bc1ee3de49befbd66c662b434b15d49d4"
- },
- "50": {
- "address": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48"
- }
- }
+ "contract_name": "TokenTransferProxy",
+ "abi": [
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "target",
+ "type": "address"
+ }
+ ],
+ "name": "addAuthorizedAddress",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "authorities",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "target",
+ "type": "address"
+ }
+ ],
+ "name": "removeAuthorizedAddress",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "authorized",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getAuthorizedAddresses",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address[]"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "caller",
+ "type": "address"
+ }
+ ],
+ "name": "LogAuthorizedAddressAdded",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "caller",
+ "type": "address"
+ }
+ ],
+ "name": "LogAuthorizedAddressRemoved",
+ "type": "event"
+ }
+ ],
+ "networks": {
+ "1": {
+ "address": "0x8da0d80f5007ef1e431dd2127178d224e32c2ef4"
+ },
+ "3": {
+ "address": "0x4e9aad8184de8833365fea970cd9149372fdf1e6"
+ },
+ "4": {
+ "address": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d"
+ },
+ "42": {
+ "address": "0x087eed4bc1ee3de49befbd66c662b434b15d49d4"
+ },
+ "50": {
+ "address": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48"
+ }
+ }
}
diff --git a/packages/0x.js/src/artifacts/ZRX.json b/packages/0x.js/src/artifacts/ZRX.json
index e40b8f268..44f478ab4 100644
--- a/packages/0x.js/src/artifacts/ZRX.json
+++ b/packages/0x.js/src/artifacts/ZRX.json
@@ -1,20 +1,20 @@
{
- "contract_name": "ZRX",
- "networks": {
- "1": {
- "address": "0xe41d2489571d322189246dafa5ebde1f4699f498"
- },
- "3": {
- "address": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d"
- },
- "4": {
- "address": "0x00f58d6d585f84b2d7267940cede30ce2fe6eae8"
- },
- "42": {
- "address": "0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570"
- },
- "50": {
- "address": "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401"
- }
- }
+ "contract_name": "ZRX",
+ "networks": {
+ "1": {
+ "address": "0xe41d2489571d322189246dafa5ebde1f4699f498"
+ },
+ "3": {
+ "address": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d"
+ },
+ "4": {
+ "address": "0x00f58d6d585f84b2d7267940cede30ce2fe6eae8"
+ },
+ "42": {
+ "address": "0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570"
+ },
+ "50": {
+ "address": "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401"
+ }
+ }
}
diff --git a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts
index 27551c01d..d1b753f45 100644
--- a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts
@@ -5,203 +5,203 @@ import * as _ from 'lodash';
import * as Web3 from 'web3';
import {
- Artifact,
- BlockParamLiteral,
- BlockRange,
- ContractEventArgs,
- ContractEvents,
- EventCallback,
- IndexedFilterValues,
- InternalZeroExError,
- LogWithDecodedArgs,
- RawLog,
- ZeroExError,
+ Artifact,
+ BlockParamLiteral,
+ BlockRange,
+ ContractEventArgs,
+ ContractEvents,
+ EventCallback,
+ IndexedFilterValues,
+ InternalZeroExError,
+ LogWithDecodedArgs,
+ RawLog,
+ ZeroExError,
} from '../types';
import { AbiDecoder } from '../utils/abi_decoder';
import { constants } from '../utils/constants';
import { filterUtils } from '../utils/filter_utils';
const CONTRACT_NAME_TO_NOT_FOUND_ERROR: {
- [contractName: string]: ZeroExError;
+ [contractName: string]: ZeroExError;
} = {
- ZRX: ZeroExError.ZRXContractDoesNotExist,
- EtherToken: ZeroExError.EtherTokenContractDoesNotExist,
- Token: ZeroExError.TokenContractDoesNotExist,
- TokenRegistry: ZeroExError.TokenRegistryContractDoesNotExist,
- TokenTransferProxy: ZeroExError.TokenTransferProxyContractDoesNotExist,
- Exchange: ZeroExError.ExchangeContractDoesNotExist,
+ ZRX: ZeroExError.ZRXContractDoesNotExist,
+ EtherToken: ZeroExError.EtherTokenContractDoesNotExist,
+ Token: ZeroExError.TokenContractDoesNotExist,
+ TokenRegistry: ZeroExError.TokenRegistryContractDoesNotExist,
+ TokenTransferProxy: ZeroExError.TokenTransferProxyContractDoesNotExist,
+ Exchange: ZeroExError.ExchangeContractDoesNotExist,
};
export class ContractWrapper {
- protected _web3Wrapper: Web3Wrapper;
- private _networkId: number;
- private _abiDecoder?: AbiDecoder;
- private _blockAndLogStreamerIfExists: BlockAndLogStreamer | undefined;
- private _blockAndLogStreamInterval: NodeJS.Timer;
- private _filters: { [filterToken: string]: Web3.FilterObject };
- private _filterCallbacks: {
- [filterToken: string]: EventCallback<ContractEventArgs>;
- };
- private _onLogAddedSubscriptionToken: string | undefined;
- private _onLogRemovedSubscriptionToken: string | undefined;
- constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder?: AbiDecoder) {
- this._web3Wrapper = web3Wrapper;
- this._networkId = networkId;
- this._abiDecoder = abiDecoder;
- this._filters = {};
- this._filterCallbacks = {};
- this._blockAndLogStreamerIfExists = undefined;
- this._onLogAddedSubscriptionToken = undefined;
- this._onLogRemovedSubscriptionToken = undefined;
- }
- protected unsubscribeAll(): void {
- const filterTokens = _.keys(this._filterCallbacks);
- _.each(filterTokens, filterToken => {
- this._unsubscribe(filterToken);
- });
- }
- protected _unsubscribe(filterToken: string, err?: Error): void {
- if (_.isUndefined(this._filters[filterToken])) {
- throw new Error(ZeroExError.SubscriptionNotFound);
- }
- if (!_.isUndefined(err)) {
- const callback = this._filterCallbacks[filterToken];
- callback(err, undefined);
- }
- delete this._filters[filterToken];
- delete this._filterCallbacks[filterToken];
- if (_.isEmpty(this._filters)) {
- this._stopBlockAndLogStream();
- }
- }
- protected _subscribe<ArgsType extends ContractEventArgs>(
- address: string,
- eventName: ContractEvents,
- indexFilterValues: IndexedFilterValues,
- abi: Web3.ContractAbi,
- callback: EventCallback<ArgsType>,
- ): string {
- const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi);
- if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
- this._startBlockAndLogStream();
- }
- const filterToken = filterUtils.generateUUID();
- this._filters[filterToken] = filter;
- this._filterCallbacks[filterToken] = callback as EventCallback<ContractEventArgs>;
- return filterToken;
- }
- protected async _getLogsAsync<ArgsType extends ContractEventArgs>(
- address: string,
- eventName: ContractEvents,
- blockRange: BlockRange,
- indexFilterValues: IndexedFilterValues,
- abi: Web3.ContractAbi,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, blockRange);
- const logs = await this._web3Wrapper.getLogsAsync(filter);
- const logsWithDecodedArguments = _.map(logs, this._tryToDecodeLogOrNoop.bind(this));
- return logsWithDecodedArguments;
- }
- protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
- log: Web3.LogEntry,
- ): LogWithDecodedArgs<ArgsType> | RawLog {
- if (_.isUndefined(this._abiDecoder)) {
- throw new Error(InternalZeroExError.NoAbiDecoder);
- }
- const logWithDecodedArgs = this._abiDecoder.tryToDecodeLogOrNoop(log);
- return logWithDecodedArgs;
- }
- protected async _instantiateContractIfExistsAsync(
- artifact: Artifact,
- addressIfExists?: string,
- ): Promise<Web3.ContractInstance> {
- let contractAddress: string;
- if (_.isUndefined(addressIfExists)) {
- if (_.isUndefined(artifact.networks[this._networkId])) {
- throw new Error(ZeroExError.ContractNotDeployedOnNetwork);
- }
- contractAddress = artifact.networks[this._networkId].address.toLowerCase();
- } else {
- contractAddress = addressIfExists;
- }
- const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(contractAddress);
- if (!doesContractExist) {
- throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contract_name]);
- }
- const contractInstance = this._web3Wrapper.getContractInstance(artifact.abi, contractAddress);
- return contractInstance;
- }
- protected _getContractAddress(artifact: Artifact, addressIfExists?: string): string {
- if (_.isUndefined(addressIfExists)) {
- const contractAddress = artifact.networks[this._networkId].address;
- if (_.isUndefined(contractAddress)) {
- throw new Error(ZeroExError.ExchangeContractDoesNotExist);
- }
- return contractAddress;
- } else {
- return addressIfExists;
- }
- }
- private _onLogStateChanged<ArgsType extends ContractEventArgs>(isRemoved: boolean, log: Web3.LogEntry): void {
- _.forEach(this._filters, (filter: Web3.FilterObject, filterToken: string) => {
- if (filterUtils.matchesFilter(log, filter)) {
- const decodedLog = this._tryToDecodeLogOrNoop(log) as LogWithDecodedArgs<ArgsType>;
- const logEvent = {
- log: decodedLog,
- isRemoved,
- };
- this._filterCallbacks[filterToken](null, logEvent);
- }
- });
- }
- private _startBlockAndLogStream(): void {
- if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
- throw new Error(ZeroExError.SubscriptionAlreadyPresent);
- }
- this._blockAndLogStreamerIfExists = new BlockAndLogStreamer(
- this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper),
- this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper),
- );
- const catchAllLogFilter = {};
- this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter);
- this._blockAndLogStreamInterval = intervalUtils.setAsyncExcludingInterval(
- this._reconcileBlockAsync.bind(this),
- constants.DEFAULT_BLOCK_POLLING_INTERVAL,
- this._onReconcileBlockError.bind(this),
- );
- let isRemoved = false;
- this._onLogAddedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogAdded(
- this._onLogStateChanged.bind(this, isRemoved),
- );
- isRemoved = true;
- this._onLogRemovedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogRemoved(
- this._onLogStateChanged.bind(this, isRemoved),
- );
- }
- private _onReconcileBlockError(err: Error): void {
- const filterTokens = _.keys(this._filterCallbacks);
- _.each(filterTokens, filterToken => {
- this._unsubscribe(filterToken, err);
- });
- }
- private _setNetworkId(networkId: number): void {
- this._networkId = networkId;
- }
- private _stopBlockAndLogStream(): void {
- if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
- throw new Error(ZeroExError.SubscriptionNotFound);
- }
- this._blockAndLogStreamerIfExists.unsubscribeFromOnLogAdded(this._onLogAddedSubscriptionToken as string);
- this._blockAndLogStreamerIfExists.unsubscribeFromOnLogRemoved(this._onLogRemovedSubscriptionToken as string);
- intervalUtils.clearAsyncExcludingInterval(this._blockAndLogStreamInterval);
- delete this._blockAndLogStreamerIfExists;
- }
- private async _reconcileBlockAsync(): Promise<void> {
- const latestBlock = await this._web3Wrapper.getBlockAsync(BlockParamLiteral.Latest);
- // We need to coerce to Block type cause Web3.Block includes types for mempool blocks
- if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
- // If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined
- await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlock as any) as Block);
- }
- }
+ protected _web3Wrapper: Web3Wrapper;
+ private _networkId: number;
+ private _abiDecoder?: AbiDecoder;
+ private _blockAndLogStreamerIfExists: BlockAndLogStreamer | undefined;
+ private _blockAndLogStreamInterval: NodeJS.Timer;
+ private _filters: { [filterToken: string]: Web3.FilterObject };
+ private _filterCallbacks: {
+ [filterToken: string]: EventCallback<ContractEventArgs>;
+ };
+ private _onLogAddedSubscriptionToken: string | undefined;
+ private _onLogRemovedSubscriptionToken: string | undefined;
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder?: AbiDecoder) {
+ this._web3Wrapper = web3Wrapper;
+ this._networkId = networkId;
+ this._abiDecoder = abiDecoder;
+ this._filters = {};
+ this._filterCallbacks = {};
+ this._blockAndLogStreamerIfExists = undefined;
+ this._onLogAddedSubscriptionToken = undefined;
+ this._onLogRemovedSubscriptionToken = undefined;
+ }
+ protected unsubscribeAll(): void {
+ const filterTokens = _.keys(this._filterCallbacks);
+ _.each(filterTokens, filterToken => {
+ this._unsubscribe(filterToken);
+ });
+ }
+ protected _unsubscribe(filterToken: string, err?: Error): void {
+ if (_.isUndefined(this._filters[filterToken])) {
+ throw new Error(ZeroExError.SubscriptionNotFound);
+ }
+ if (!_.isUndefined(err)) {
+ const callback = this._filterCallbacks[filterToken];
+ callback(err, undefined);
+ }
+ delete this._filters[filterToken];
+ delete this._filterCallbacks[filterToken];
+ if (_.isEmpty(this._filters)) {
+ this._stopBlockAndLogStream();
+ }
+ }
+ protected _subscribe<ArgsType extends ContractEventArgs>(
+ address: string,
+ eventName: ContractEvents,
+ indexFilterValues: IndexedFilterValues,
+ abi: Web3.ContractAbi,
+ callback: EventCallback<ArgsType>,
+ ): string {
+ const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi);
+ if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
+ this._startBlockAndLogStream();
+ }
+ const filterToken = filterUtils.generateUUID();
+ this._filters[filterToken] = filter;
+ this._filterCallbacks[filterToken] = callback as EventCallback<ContractEventArgs>;
+ return filterToken;
+ }
+ protected async _getLogsAsync<ArgsType extends ContractEventArgs>(
+ address: string,
+ eventName: ContractEvents,
+ blockRange: BlockRange,
+ indexFilterValues: IndexedFilterValues,
+ abi: Web3.ContractAbi,
+ ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
+ const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, blockRange);
+ const logs = await this._web3Wrapper.getLogsAsync(filter);
+ const logsWithDecodedArguments = _.map(logs, this._tryToDecodeLogOrNoop.bind(this));
+ return logsWithDecodedArguments;
+ }
+ protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
+ log: Web3.LogEntry,
+ ): LogWithDecodedArgs<ArgsType> | RawLog {
+ if (_.isUndefined(this._abiDecoder)) {
+ throw new Error(InternalZeroExError.NoAbiDecoder);
+ }
+ const logWithDecodedArgs = this._abiDecoder.tryToDecodeLogOrNoop(log);
+ return logWithDecodedArgs;
+ }
+ protected async _instantiateContractIfExistsAsync(
+ artifact: Artifact,
+ addressIfExists?: string,
+ ): Promise<Web3.ContractInstance> {
+ let contractAddress: string;
+ if (_.isUndefined(addressIfExists)) {
+ if (_.isUndefined(artifact.networks[this._networkId])) {
+ throw new Error(ZeroExError.ContractNotDeployedOnNetwork);
+ }
+ contractAddress = artifact.networks[this._networkId].address.toLowerCase();
+ } else {
+ contractAddress = addressIfExists;
+ }
+ const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(contractAddress);
+ if (!doesContractExist) {
+ throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contract_name]);
+ }
+ const contractInstance = this._web3Wrapper.getContractInstance(artifact.abi, contractAddress);
+ return contractInstance;
+ }
+ protected _getContractAddress(artifact: Artifact, addressIfExists?: string): string {
+ if (_.isUndefined(addressIfExists)) {
+ const contractAddress = artifact.networks[this._networkId].address;
+ if (_.isUndefined(contractAddress)) {
+ throw new Error(ZeroExError.ExchangeContractDoesNotExist);
+ }
+ return contractAddress;
+ } else {
+ return addressIfExists;
+ }
+ }
+ private _onLogStateChanged<ArgsType extends ContractEventArgs>(isRemoved: boolean, log: Web3.LogEntry): void {
+ _.forEach(this._filters, (filter: Web3.FilterObject, filterToken: string) => {
+ if (filterUtils.matchesFilter(log, filter)) {
+ const decodedLog = this._tryToDecodeLogOrNoop(log) as LogWithDecodedArgs<ArgsType>;
+ const logEvent = {
+ log: decodedLog,
+ isRemoved,
+ };
+ this._filterCallbacks[filterToken](null, logEvent);
+ }
+ });
+ }
+ private _startBlockAndLogStream(): void {
+ if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
+ throw new Error(ZeroExError.SubscriptionAlreadyPresent);
+ }
+ this._blockAndLogStreamerIfExists = new BlockAndLogStreamer(
+ this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper),
+ this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper),
+ );
+ const catchAllLogFilter = {};
+ this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter);
+ this._blockAndLogStreamInterval = intervalUtils.setAsyncExcludingInterval(
+ this._reconcileBlockAsync.bind(this),
+ constants.DEFAULT_BLOCK_POLLING_INTERVAL,
+ this._onReconcileBlockError.bind(this),
+ );
+ let isRemoved = false;
+ this._onLogAddedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogAdded(
+ this._onLogStateChanged.bind(this, isRemoved),
+ );
+ isRemoved = true;
+ this._onLogRemovedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogRemoved(
+ this._onLogStateChanged.bind(this, isRemoved),
+ );
+ }
+ private _onReconcileBlockError(err: Error): void {
+ const filterTokens = _.keys(this._filterCallbacks);
+ _.each(filterTokens, filterToken => {
+ this._unsubscribe(filterToken, err);
+ });
+ }
+ private _setNetworkId(networkId: number): void {
+ this._networkId = networkId;
+ }
+ private _stopBlockAndLogStream(): void {
+ if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
+ throw new Error(ZeroExError.SubscriptionNotFound);
+ }
+ this._blockAndLogStreamerIfExists.unsubscribeFromOnLogAdded(this._onLogAddedSubscriptionToken as string);
+ this._blockAndLogStreamerIfExists.unsubscribeFromOnLogRemoved(this._onLogRemovedSubscriptionToken as string);
+ intervalUtils.clearAsyncExcludingInterval(this._blockAndLogStreamInterval);
+ delete this._blockAndLogStreamerIfExists;
+ }
+ private async _reconcileBlockAsync(): Promise<void> {
+ const latestBlock = await this._web3Wrapper.getBlockAsync(BlockParamLiteral.Latest);
+ // We need to coerce to Block type cause Web3.Block includes types for mempool blocks
+ if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
+ // If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined
+ await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlock as any) as Block);
+ }
+ }
}
diff --git a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts
index b03571636..d412971be 100644
--- a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts
@@ -5,14 +5,14 @@ import * as _ from 'lodash';
import { artifacts } from '../artifacts';
import {
- BlockRange,
- EtherTokenContractEventArgs,
- EtherTokenEvents,
- EventCallback,
- IndexedFilterValues,
- LogWithDecodedArgs,
- TransactionOpts,
- ZeroExError,
+ BlockRange,
+ EtherTokenContractEventArgs,
+ EtherTokenEvents,
+ EventCallback,
+ IndexedFilterValues,
+ LogWithDecodedArgs,
+ TransactionOpts,
+ ZeroExError,
} from '../types';
import { AbiDecoder } from '../utils/abi_decoder';
import { assert } from '../utils/assert';
@@ -26,159 +26,159 @@ import { TokenWrapper } from './token_wrapper';
* The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back.
*/
export class EtherTokenWrapper extends ContractWrapper {
- private _etherTokenContractsByAddress: {
- [address: string]: EtherTokenContract;
- } = {};
- private _tokenWrapper: TokenWrapper;
- constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder: AbiDecoder, tokenWrapper: TokenWrapper) {
- super(web3Wrapper, networkId, abiDecoder);
- this._tokenWrapper = tokenWrapper;
- }
- /**
- * Deposit ETH into the Wrapped ETH smart contract and issues the equivalent number of wrapped ETH tokens
- * to the depositor address. These wrapped ETH tokens can be used in 0x trades and are redeemable for 1-to-1
- * for ETH.
- * @param etherTokenAddress EtherToken address you wish to deposit into.
- * @param amountInWei Amount of ETH in Wei the caller wishes to deposit.
- * @param depositor The hex encoded user Ethereum address that would like to make the deposit.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async depositAsync(
- etherTokenAddress: string,
- amountInWei: BigNumber,
- depositor: string,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- assert.isValidBaseUnitAmount('amountInWei', amountInWei);
- await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper);
+ private _etherTokenContractsByAddress: {
+ [address: string]: EtherTokenContract;
+ } = {};
+ private _tokenWrapper: TokenWrapper;
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder: AbiDecoder, tokenWrapper: TokenWrapper) {
+ super(web3Wrapper, networkId, abiDecoder);
+ this._tokenWrapper = tokenWrapper;
+ }
+ /**
+ * Deposit ETH into the Wrapped ETH smart contract and issues the equivalent number of wrapped ETH tokens
+ * to the depositor address. These wrapped ETH tokens can be used in 0x trades and are redeemable for 1-to-1
+ * for ETH.
+ * @param etherTokenAddress EtherToken address you wish to deposit into.
+ * @param amountInWei Amount of ETH in Wei the caller wishes to deposit.
+ * @param depositor The hex encoded user Ethereum address that would like to make the deposit.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async depositAsync(
+ etherTokenAddress: string,
+ amountInWei: BigNumber,
+ depositor: string,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isValidBaseUnitAmount('amountInWei', amountInWei);
+ await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper);
- const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(depositor);
- assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.InsufficientEthBalanceForDeposit);
+ const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(depositor);
+ assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.InsufficientEthBalanceForDeposit);
- const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress);
- const txHash = await wethContract.deposit.sendTransactionAsync({
- from: depositor,
- value: amountInWei,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
- return txHash;
- }
- /**
- * Withdraw ETH to the withdrawer's address from the wrapped ETH smart contract in exchange for the
- * equivalent number of wrapped ETH tokens.
- * @param etherTokenAddress EtherToken address you wish to withdraw from.
- * @param amountInWei Amount of ETH in Wei the caller wishes to withdraw.
- * @param withdrawer The hex encoded user Ethereum address that would like to make the withdrawl.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async withdrawAsync(
- etherTokenAddress: string,
- amountInWei: BigNumber,
- withdrawer: string,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- assert.isValidBaseUnitAmount('amountInWei', amountInWei);
- await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper);
+ const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress);
+ const txHash = await wethContract.deposit.sendTransactionAsync({
+ from: depositor,
+ value: amountInWei,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ });
+ return txHash;
+ }
+ /**
+ * Withdraw ETH to the withdrawer's address from the wrapped ETH smart contract in exchange for the
+ * equivalent number of wrapped ETH tokens.
+ * @param etherTokenAddress EtherToken address you wish to withdraw from.
+ * @param amountInWei Amount of ETH in Wei the caller wishes to withdraw.
+ * @param withdrawer The hex encoded user Ethereum address that would like to make the withdrawl.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async withdrawAsync(
+ etherTokenAddress: string,
+ amountInWei: BigNumber,
+ withdrawer: string,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isValidBaseUnitAmount('amountInWei', amountInWei);
+ await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper);
- const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(etherTokenAddress, withdrawer);
- assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.InsufficientWEthBalanceForWithdrawal);
+ const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(etherTokenAddress, withdrawer);
+ assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.InsufficientWEthBalanceForWithdrawal);
- const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress);
- const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, {
- from: withdrawer,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
- return txHash;
- }
- /**
- * Gets historical logs without creating a subscription
- * @param etherTokenAddress An address of the ether token that emmited the logs.
- * @param eventName The ether token contract event you would like to subscribe to.
- * @param blockRange Block range to get logs from.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
- * @return Array of logs that match the parameters
- */
- public async getLogsAsync<ArgsType extends EtherTokenContractEventArgs>(
- etherTokenAddress: string,
- eventName: EtherTokenEvents,
- blockRange: BlockRange,
- indexFilterValues: IndexedFilterValues,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
- assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
- assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- const logs = await this._getLogsAsync<ArgsType>(
- etherTokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- artifacts.EtherTokenArtifact.abi,
- );
- return logs;
- }
- /**
- * Subscribe to an event type emitted by the Token contract.
- * @param etherTokenAddress The hex encoded address where the ether token is deployed.
- * @param eventName The ether token contract event you would like to subscribe to.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
- * @param callback Callback that gets called when a log is added/removed
- * @return Subscription token used later to unsubscribe
- */
- public subscribe<ArgsType extends EtherTokenContractEventArgs>(
- etherTokenAddress: string,
- eventName: EtherTokenEvents,
- indexFilterValues: IndexedFilterValues,
- callback: EventCallback<ArgsType>,
- ): string {
- assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
- assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- assert.isFunction('callback', callback);
- const subscriptionToken = this._subscribe<ArgsType>(
- etherTokenAddress,
- eventName,
- indexFilterValues,
- artifacts.EtherTokenArtifact.abi,
- callback,
- );
- return subscriptionToken;
- }
- /**
- * Cancel a subscription
- * @param subscriptionToken Subscription token returned by `subscribe()`
- */
- public unsubscribe(subscriptionToken: string): void {
- this._unsubscribe(subscriptionToken);
- }
- /**
- * Cancels all existing subscriptions
- */
- public unsubscribeAll(): void {
- super.unsubscribeAll();
- }
- private _invalidateContractInstance(): void {
- this.unsubscribeAll();
- this._etherTokenContractsByAddress = {};
- }
- private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<EtherTokenContract> {
- let etherTokenContract = this._etherTokenContractsByAddress[etherTokenAddress];
- if (!_.isUndefined(etherTokenContract)) {
- return etherTokenContract;
- }
- const web3ContractInstance = await this._instantiateContractIfExistsAsync(
- artifacts.EtherTokenArtifact,
- etherTokenAddress,
- );
- const contractInstance = new EtherTokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
- etherTokenContract = contractInstance;
- this._etherTokenContractsByAddress[etherTokenAddress] = etherTokenContract;
- return etherTokenContract;
- }
+ const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress);
+ const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, {
+ from: withdrawer,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ });
+ return txHash;
+ }
+ /**
+ * Gets historical logs without creating a subscription
+ * @param etherTokenAddress An address of the ether token that emmited the logs.
+ * @param eventName The ether token contract event you would like to subscribe to.
+ * @param blockRange Block range to get logs from.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
+ * @return Array of logs that match the parameters
+ */
+ public async getLogsAsync<ArgsType extends EtherTokenContractEventArgs>(
+ etherTokenAddress: string,
+ eventName: EtherTokenEvents,
+ blockRange: BlockRange,
+ indexFilterValues: IndexedFilterValues,
+ ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
+ assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
+ assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
+ assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ const logs = await this._getLogsAsync<ArgsType>(
+ etherTokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ artifacts.EtherTokenArtifact.abi,
+ );
+ return logs;
+ }
+ /**
+ * Subscribe to an event type emitted by the Token contract.
+ * @param etherTokenAddress The hex encoded address where the ether token is deployed.
+ * @param eventName The ether token contract event you would like to subscribe to.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
+ * @param callback Callback that gets called when a log is added/removed
+ * @return Subscription token used later to unsubscribe
+ */
+ public subscribe<ArgsType extends EtherTokenContractEventArgs>(
+ etherTokenAddress: string,
+ eventName: EtherTokenEvents,
+ indexFilterValues: IndexedFilterValues,
+ callback: EventCallback<ArgsType>,
+ ): string {
+ assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
+ assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ assert.isFunction('callback', callback);
+ const subscriptionToken = this._subscribe<ArgsType>(
+ etherTokenAddress,
+ eventName,
+ indexFilterValues,
+ artifacts.EtherTokenArtifact.abi,
+ callback,
+ );
+ return subscriptionToken;
+ }
+ /**
+ * Cancel a subscription
+ * @param subscriptionToken Subscription token returned by `subscribe()`
+ */
+ public unsubscribe(subscriptionToken: string): void {
+ this._unsubscribe(subscriptionToken);
+ }
+ /**
+ * Cancels all existing subscriptions
+ */
+ public unsubscribeAll(): void {
+ super.unsubscribeAll();
+ }
+ private _invalidateContractInstance(): void {
+ this.unsubscribeAll();
+ this._etherTokenContractsByAddress = {};
+ }
+ private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<EtherTokenContract> {
+ let etherTokenContract = this._etherTokenContractsByAddress[etherTokenAddress];
+ if (!_.isUndefined(etherTokenContract)) {
+ return etherTokenContract;
+ }
+ const web3ContractInstance = await this._instantiateContractIfExistsAsync(
+ artifacts.EtherTokenArtifact,
+ etherTokenAddress,
+ );
+ const contractInstance = new EtherTokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
+ etherTokenContract = contractInstance;
+ this._etherTokenContractsByAddress[etherTokenAddress] = etherTokenContract;
+ return etherTokenContract;
+ }
}
diff --git a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
index 2b6117729..668893d45 100644
--- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
@@ -6,27 +6,27 @@ import * as Web3 from 'web3';
import { artifacts } from '../artifacts';
import {
- BlockParamLiteral,
- BlockRange,
- DecodedLogArgs,
- ECSignature,
- EventCallback,
- ExchangeContractErrCodes,
- ExchangeContractErrs,
- ExchangeContractEventArgs,
- ExchangeEvents,
- IndexedFilterValues,
- LogErrorContractEventArgs,
- LogWithDecodedArgs,
- MethodOpts,
- Order,
- OrderAddresses,
- OrderCancellationRequest,
- OrderFillRequest,
- OrderTransactionOpts,
- OrderValues,
- SignedOrder,
- ValidateOrderFillableOpts,
+ BlockParamLiteral,
+ BlockRange,
+ DecodedLogArgs,
+ ECSignature,
+ EventCallback,
+ ExchangeContractErrCodes,
+ ExchangeContractErrs,
+ ExchangeContractEventArgs,
+ ExchangeEvents,
+ IndexedFilterValues,
+ LogErrorContractEventArgs,
+ LogWithDecodedArgs,
+ MethodOpts,
+ Order,
+ OrderAddresses,
+ OrderCancellationRequest,
+ OrderFillRequest,
+ OrderTransactionOpts,
+ OrderValues,
+ SignedOrder,
+ ValidateOrderFillableOpts,
} from '../types';
import { AbiDecoder } from '../utils/abi_decoder';
import { assert } from '../utils/assert';
@@ -42,7 +42,7 @@ import { TokenWrapper } from './token_wrapper';
const SHOULD_VALIDATE_BY_DEFAULT = true;
interface ExchangeContractErrCodesToMsgs {
- [exchangeContractErrCodes: number]: string;
+ [exchangeContractErrCodes: number]: string;
}
/**
@@ -50,864 +50,864 @@ interface ExchangeContractErrCodesToMsgs {
* events of the 0x Exchange smart contract.
*/
export class ExchangeWrapper extends ContractWrapper {
- private _exchangeContractIfExists?: ExchangeContract;
- private _orderValidationUtils: OrderValidationUtils;
- private _tokenWrapper: TokenWrapper;
- private _exchangeContractErrCodesToMsg: ExchangeContractErrCodesToMsgs = {
- [ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
- [ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
- [ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
- [ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
- [ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.OrderFillRoundingError,
- [ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FillBalanceAllowanceError,
- };
- private _contractAddressIfExists?: string;
- private _zrxContractAddressIfExists?: string;
- private static _getOrderAddressesAndValues(order: Order): [OrderAddresses, OrderValues] {
- const orderAddresses: OrderAddresses = [
- order.maker,
- order.taker,
- order.makerTokenAddress,
- order.takerTokenAddress,
- order.feeRecipient,
- ];
- const orderValues: OrderValues = [
- order.makerTokenAmount,
- order.takerTokenAmount,
- order.makerFee,
- order.takerFee,
- order.expirationUnixTimestampSec,
- order.salt,
- ];
- return [orderAddresses, orderValues];
- }
- constructor(
- web3Wrapper: Web3Wrapper,
- networkId: number,
- abiDecoder: AbiDecoder,
- tokenWrapper: TokenWrapper,
- contractAddressIfExists?: string,
- zrxContractAddressIfExists?: string,
- ) {
- super(web3Wrapper, networkId, abiDecoder);
- this._tokenWrapper = tokenWrapper;
- this._orderValidationUtils = new OrderValidationUtils(this);
- this._contractAddressIfExists = contractAddressIfExists;
- this._zrxContractAddressIfExists = zrxContractAddressIfExists;
- }
- /**
- * Returns the unavailable takerAmount of an order. Unavailable amount is defined as the total
- * amount that has been filled or cancelled. The remaining takerAmount can be calculated by
- * subtracting the unavailable amount from the total order takerAmount.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the
- * unavailable takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has either been filled or cancelled.
- */
- public async getUnavailableTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
+ private _exchangeContractIfExists?: ExchangeContract;
+ private _orderValidationUtils: OrderValidationUtils;
+ private _tokenWrapper: TokenWrapper;
+ private _exchangeContractErrCodesToMsg: ExchangeContractErrCodesToMsgs = {
+ [ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
+ [ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
+ [ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
+ [ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
+ [ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.OrderFillRoundingError,
+ [ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FillBalanceAllowanceError,
+ };
+ private _contractAddressIfExists?: string;
+ private _zrxContractAddressIfExists?: string;
+ private static _getOrderAddressesAndValues(order: Order): [OrderAddresses, OrderValues] {
+ const orderAddresses: OrderAddresses = [
+ order.maker,
+ order.taker,
+ order.makerTokenAddress,
+ order.takerTokenAddress,
+ order.feeRecipient,
+ ];
+ const orderValues: OrderValues = [
+ order.makerTokenAmount,
+ order.takerTokenAmount,
+ order.makerFee,
+ order.takerFee,
+ order.expirationUnixTimestampSec,
+ order.salt,
+ ];
+ return [orderAddresses, orderValues];
+ }
+ constructor(
+ web3Wrapper: Web3Wrapper,
+ networkId: number,
+ abiDecoder: AbiDecoder,
+ tokenWrapper: TokenWrapper,
+ contractAddressIfExists?: string,
+ zrxContractAddressIfExists?: string,
+ ) {
+ super(web3Wrapper, networkId, abiDecoder);
+ this._tokenWrapper = tokenWrapper;
+ this._orderValidationUtils = new OrderValidationUtils(this);
+ this._contractAddressIfExists = contractAddressIfExists;
+ this._zrxContractAddressIfExists = zrxContractAddressIfExists;
+ }
+ /**
+ * Returns the unavailable takerAmount of an order. Unavailable amount is defined as the total
+ * amount that has been filled or cancelled. The remaining takerAmount can be calculated by
+ * subtracting the unavailable amount from the total order takerAmount.
+ * @param orderHash The hex encoded orderHash for which you would like to retrieve the
+ * unavailable takerAmount.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The amount of the order (in taker tokens) that has either been filled or cancelled.
+ */
+ public async getUnavailableTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
+ assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync(
- orderHash,
- defaultBlock,
- );
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount);
- return unavailableTakerTokenAmount;
- }
- /**
- * Retrieve the takerAmount of an order that has already been filled.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the filled takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has already been filled.
- */
- public async getFilledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
+ const exchangeContract = await this._getExchangeContractAsync();
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync(
+ orderHash,
+ defaultBlock,
+ );
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount);
+ return unavailableTakerTokenAmount;
+ }
+ /**
+ * Retrieve the takerAmount of an order that has already been filled.
+ * @param orderHash The hex encoded orderHash for which you would like to retrieve the filled takerAmount.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The amount of the order (in taker tokens) that has already been filled.
+ */
+ public async getFilledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
+ assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- fillAmountInBaseUnits = new BigNumber(fillAmountInBaseUnits);
- return fillAmountInBaseUnits;
- }
- /**
- * Retrieve the takerAmount of an order that has been cancelled.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the
- * cancelled takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has been cancelled.
- */
- public async getCancelledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
+ const exchangeContract = await this._getExchangeContractAsync();
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, defaultBlock);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ fillAmountInBaseUnits = new BigNumber(fillAmountInBaseUnits);
+ return fillAmountInBaseUnits;
+ }
+ /**
+ * Retrieve the takerAmount of an order that has been cancelled.
+ * @param orderHash The hex encoded orderHash for which you would like to retrieve the
+ * cancelled takerAmount.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The amount of the order (in taker tokens) that has been cancelled.
+ */
+ public async getCancelledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
+ assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- cancelledAmountInBaseUnits = new BigNumber(cancelledAmountInBaseUnits);
- return cancelledAmountInBaseUnits;
- }
- /**
- * Fills a signed order with an amount denominated in baseUnits of the taker token.
- * Since the order in which transactions are included in the next block is indeterminate, race-conditions
- * could arise where a users balance or allowance changes before the fillOrder executes. Because of this,
- * we allow you to specify `shouldThrowOnInsufficientBalanceOrAllowance`.
- * If false, the smart contract will not throw if the parties
- * do not have sufficient balances/allowances, preserving gas costs. Setting it to true forgoes this check
- * and causes the smart contract to throw (using all the gas supplied) instead.
- * @param signedOrder An object that conforms to the SignedOrder interface.
- * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that
- * you wish to fill.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
- * if upon execution the tokens cannot be transferred.
- * @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.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrderAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const exchangeContract = await this._getExchangeContractAsync();
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, defaultBlock);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ cancelledAmountInBaseUnits = new BigNumber(cancelledAmountInBaseUnits);
+ return cancelledAmountInBaseUnits;
+ }
+ /**
+ * Fills a signed order with an amount denominated in baseUnits of the taker token.
+ * Since the order in which transactions are included in the next block is indeterminate, race-conditions
+ * could arise where a users balance or allowance changes before the fillOrder executes. Because of this,
+ * we allow you to specify `shouldThrowOnInsufficientBalanceOrAllowance`.
+ * If false, the smart contract will not throw if the parties
+ * do not have sufficient balances/allowances, preserving gas costs. Setting it to true forgoes this check
+ * and causes the smart contract to throw (using all the gas supplied) instead.
+ * @param signedOrder An object that conforms to the SignedOrder interface.
+ * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that
+ * you wish to fill.
+ * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
+ * if upon execution the tokens cannot be transferred.
+ * @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.
+ * @param orderTransactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async fillOrderAsync(
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean,
+ takerAddress: string,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
+ const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
- const txHash: string = await exchangeInstance.fillOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: takerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount.
- * If the fill amount is reached - it succeeds and does not fill the rest of the orders.
- * If fill amount is not reached - it fills as much of the fill amount as possible and succeeds.
- * @param signedOrders The array of signedOrders that you would like to fill until
- * takerTokenFillAmount is reached.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw if
- * upon execution any of the tokens cannot be transferred.
- * If set to false, the call will continue to fill subsequent
- * signedOrders even when some cannot be filled.
- * @param takerAddress The user Ethereum address who would like to fill these
- * orders. Must be available via the supplied Web3.Provider
- * passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrdersUpToAsync(
- signedOrders: SignedOrder[],
- fillTakerTokenAmount: BigNumber,
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
- const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress);
- assert.hasAtMostOneUniqueValue(
- takerTokenAddresses,
- ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed,
- );
- const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress);
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const txHash: string = await exchangeInstance.fillOrder.sendTransactionAsync(
+ orderAddresses,
+ orderValues,
+ fillTakerTokenAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ signedOrder.ecSignature.v,
+ signedOrder.ecSignature.r,
+ signedOrder.ecSignature.s,
+ {
+ from: takerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount.
+ * If the fill amount is reached - it succeeds and does not fill the rest of the orders.
+ * If fill amount is not reached - it fills as much of the fill amount as possible and succeeds.
+ * @param signedOrders The array of signedOrders that you would like to fill until
+ * takerTokenFillAmount is reached.
+ * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
+ * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw if
+ * upon execution any of the tokens cannot be transferred.
+ * If set to false, the call will continue to fill subsequent
+ * signedOrders even when some cannot be filled.
+ * @param takerAddress The user Ethereum address who would like to fill these
+ * orders. Must be available via the supplied Web3.Provider
+ * passed to 0x.js.
+ * @param orderTransactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async fillOrdersUpToAsync(
+ signedOrders: SignedOrder[],
+ fillTakerTokenAmount: BigNumber,
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean,
+ takerAddress: string,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
+ const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress);
+ assert.hasAtMostOneUniqueValue(
+ takerTokenAddresses,
+ ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed,
+ );
+ const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress);
+ assert.hasAtMostOneUniqueValue(
+ exchangeContractAddresses,
+ ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
+ );
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- let filledTakerTokenAmount = new BigNumber(0);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- for (const signedOrder of signedOrders) {
- const singleFilledTakerTokenAmount = await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount.minus(filledTakerTokenAmount),
- takerAddress,
- zrxTokenAddress,
- );
- filledTakerTokenAmount = filledTakerTokenAmount.plus(singleFilledTakerTokenAmount);
- }
- }
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ let filledTakerTokenAmount = new BigNumber(0);
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ for (const signedOrder of signedOrders) {
+ const singleFilledTakerTokenAmount = await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount.minus(filledTakerTokenAmount),
+ takerAddress,
+ zrxTokenAddress,
+ );
+ filledTakerTokenAmount = filledTakerTokenAmount.plus(singleFilledTakerTokenAmount);
+ }
+ }
- if (_.isEmpty(signedOrders)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
+ if (_.isEmpty(signedOrders)) {
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ }
- const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => {
- return [
- ...ExchangeWrapper._getOrderAddressesAndValues(signedOrder),
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddressesArray, orderValuesArray, vArray, rArray, sArray] = _.unzip<any>(
- orderAddressesValuesAndSignatureArray,
- );
+ const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => {
+ return [
+ ...ExchangeWrapper._getOrderAddressesAndValues(signedOrder),
+ signedOrder.ecSignature.v,
+ signedOrder.ecSignature.r,
+ signedOrder.ecSignature.s,
+ ];
+ });
+ // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
+ const [orderAddressesArray, orderValuesArray, vArray, rArray, sArray] = _.unzip<any>(
+ orderAddressesValuesAndSignatureArray,
+ );
- const exchangeInstance = await this._getExchangeContractAsync();
- const txHash = await exchangeInstance.fillOrdersUpTo.sendTransactionAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: takerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of fillOrderAsync.
- * Executes multiple fills atomically in a single transaction.
- * If shouldThrowOnInsufficientBalanceOrAllowance is set to false, it will continue filling subsequent orders even
- * when earlier ones fail.
- * When shouldThrowOnInsufficientBalanceOrAllowance is set to true, if any fill fails, the entire batch fails.
- * @param orderFillRequests An array of objects that conform to the
- * OrderFillRequest interface.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
- * if upon execution any of the tokens cannot be
- * transferred. If set to false, the call will continue to
- * fill subsequent signedOrders even when some
- * cannot be filled.
- * @param takerAddress The user Ethereum address who would like to fill
- * these orders. Must be available via the supplied
- * Web3.Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchFillOrdersAsync(
- orderFillRequests: OrderFillRequest[],
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
- const exchangeContractAddresses = _.map(
- orderFillRequests,
- orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- for (const orderFillRequest of orderFillRequests) {
- await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- orderFillRequest.signedOrder,
- orderFillRequest.takerTokenFillAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
- }
- if (_.isEmpty(orderFillRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const txHash = await exchangeInstance.fillOrdersUpTo.sendTransactionAsync(
+ orderAddressesArray,
+ orderValuesArray,
+ fillTakerTokenAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ vArray,
+ rArray,
+ sArray,
+ {
+ from: takerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Batch version of fillOrderAsync.
+ * Executes multiple fills atomically in a single transaction.
+ * If shouldThrowOnInsufficientBalanceOrAllowance is set to false, it will continue filling subsequent orders even
+ * when earlier ones fail.
+ * When shouldThrowOnInsufficientBalanceOrAllowance is set to true, if any fill fails, the entire batch fails.
+ * @param orderFillRequests An array of objects that conform to the
+ * OrderFillRequest interface.
+ * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
+ * if upon execution any of the tokens cannot be
+ * transferred. If set to false, the call will continue to
+ * fill subsequent signedOrders even when some
+ * cannot be filled.
+ * @param takerAddress The user Ethereum address who would like to fill
+ * these orders. Must be available via the supplied
+ * Web3.Provider passed to 0x.js.
+ * @param orderTransactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async batchFillOrdersAsync(
+ orderFillRequests: OrderFillRequest[],
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean,
+ takerAddress: string,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
+ const exchangeContractAddresses = _.map(
+ orderFillRequests,
+ orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
+ );
+ assert.hasAtMostOneUniqueValue(
+ exchangeContractAddresses,
+ ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
+ );
+ assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ for (const orderFillRequest of orderFillRequests) {
+ await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ orderFillRequest.signedOrder,
+ orderFillRequest.takerTokenFillAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
+ }
+ if (_.isEmpty(orderFillRequests)) {
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ }
- const orderAddressesValuesAmountsAndSignatureArray = _.map(orderFillRequests, orderFillRequest => {
- return [
- ...ExchangeWrapper._getOrderAddressesAndValues(orderFillRequest.signedOrder),
- orderFillRequest.takerTokenFillAmount,
- orderFillRequest.signedOrder.ecSignature.v,
- orderFillRequest.signedOrder.ecSignature.r,
- orderFillRequest.signedOrder.ecSignature.s,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddressesArray, orderValuesArray, fillTakerTokenAmounts, vArray, rArray, sArray] = _.unzip<any>(
- orderAddressesValuesAmountsAndSignatureArray,
- );
+ const orderAddressesValuesAmountsAndSignatureArray = _.map(orderFillRequests, orderFillRequest => {
+ return [
+ ...ExchangeWrapper._getOrderAddressesAndValues(orderFillRequest.signedOrder),
+ orderFillRequest.takerTokenFillAmount,
+ orderFillRequest.signedOrder.ecSignature.v,
+ orderFillRequest.signedOrder.ecSignature.r,
+ orderFillRequest.signedOrder.ecSignature.s,
+ ];
+ });
+ // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
+ const [orderAddressesArray, orderValuesArray, fillTakerTokenAmounts, vArray, rArray, sArray] = _.unzip<any>(
+ orderAddressesValuesAmountsAndSignatureArray,
+ );
- const exchangeInstance = await this._getExchangeContractAsync();
- const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmounts,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: takerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
- * the fill order is abandoned.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @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.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrKillOrderAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync(
+ orderAddressesArray,
+ orderValuesArray,
+ fillTakerTokenAmounts,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ vArray,
+ rArray,
+ sArray,
+ {
+ from: takerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
+ * the fill order is abandoned.
+ * @param signedOrder An object that conforms to the SignedOrder interface. The
+ * signedOrder you wish to fill.
+ * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
+ * @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.
+ * @param orderTransactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async fillOrKillOrderAsync(
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const exchangeInstance = await this._getExchangeContractAsync();
+ const exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
- const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: takerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically
- * filled (each to the specified fillAmount) or aborted.
- * @param orderFillRequests An array of objects that conform to the OrderFillRequest interface.
- * @param takerAddress The user Ethereum address who would like to fill there orders.
- * Must be available via the supplied Web3.Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchFillOrKillAsync(
- orderFillRequests: OrderFillRequest[],
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
- const exchangeContractAddresses = _.map(
- orderFillRequests,
- orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- if (_.isEmpty(orderFillRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
- const exchangeInstance = await this._getExchangeContractAsync();
+ const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
+ const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync(
+ orderAddresses,
+ orderValues,
+ fillTakerTokenAmount,
+ signedOrder.ecSignature.v,
+ signedOrder.ecSignature.r,
+ signedOrder.ecSignature.s,
+ {
+ from: takerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically
+ * filled (each to the specified fillAmount) or aborted.
+ * @param orderFillRequests An array of objects that conform to the OrderFillRequest interface.
+ * @param takerAddress The user Ethereum address who would like to fill there orders.
+ * Must be available via the supplied Web3.Provider passed to 0x.js.
+ * @param orderTransactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async batchFillOrKillAsync(
+ orderFillRequests: OrderFillRequest[],
+ takerAddress: string,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
+ const exchangeContractAddresses = _.map(
+ orderFillRequests,
+ orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
+ );
+ assert.hasAtMostOneUniqueValue(
+ exchangeContractAddresses,
+ ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
+ );
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ if (_.isEmpty(orderFillRequests)) {
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ }
+ const exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- for (const orderFillRequest of orderFillRequests) {
- await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- orderFillRequest.signedOrder,
- orderFillRequest.takerTokenFillAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
- }
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ for (const orderFillRequest of orderFillRequests) {
+ await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ orderFillRequest.signedOrder,
+ orderFillRequest.takerTokenFillAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
+ }
- const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillRequests, request => {
- return [
- ...ExchangeWrapper._getOrderAddressesAndValues(request.signedOrder),
- request.takerTokenFillAmount,
- request.signedOrder.ecSignature.v,
- request.signedOrder.ecSignature.r,
- request.signedOrder.ecSignature.s,
- ];
- });
+ const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillRequests, request => {
+ return [
+ ...ExchangeWrapper._getOrderAddressesAndValues(request.signedOrder),
+ request.takerTokenFillAmount,
+ request.signedOrder.ecSignature.v,
+ request.signedOrder.ecSignature.r,
+ request.signedOrder.ecSignature.s,
+ ];
+ });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = _.unzip<any>(
- orderAddressesValuesAndTakerTokenFillAmounts,
- );
- const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmounts,
- vParams,
- rParams,
- sParams,
- {
- from: takerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Cancel a given fill amount of an order. Cancellations are cumulative.
- * @param order An object that conforms to the Order or SignedOrder interface.
- * The order you would like to cancel.
- * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
- * @param transactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async cancelOrderAsync(
- order: Order | SignedOrder,
- cancelTakerTokenAmount: BigNumber,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount);
- await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper);
+ // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
+ const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = _.unzip<any>(
+ orderAddressesValuesAndTakerTokenFillAmounts,
+ );
+ const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync(
+ orderAddresses,
+ orderValues,
+ fillTakerTokenAmounts,
+ vParams,
+ rParams,
+ sParams,
+ {
+ from: takerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Cancel a given fill amount of an order. Cancellations are cumulative.
+ * @param order An object that conforms to the Order or SignedOrder interface.
+ * The order you would like to cancel.
+ * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
+ * @param transactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async cancelOrderAsync(
+ order: Order | SignedOrder,
+ cancelTakerTokenAmount: BigNumber,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('order', order, schemas.orderSchema);
+ assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount);
+ await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper);
- const exchangeInstance = await this._getExchangeContractAsync();
+ const exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const orderHash = utils.getOrderHashHex(order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- order,
- cancelTakerTokenAmount,
- unavailableTakerTokenAmount,
- );
- }
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ const orderHash = utils.getOrderHashHex(order);
+ const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
+ OrderValidationUtils.validateCancelOrderThrowIfInvalid(
+ order,
+ cancelTakerTokenAmount,
+ unavailableTakerTokenAmount,
+ );
+ }
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
- const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmount,
- {
- from: order.maker,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction.
- * All orders must be from the same maker.
- * @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest
- * interface.
- * @param transactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchCancelOrdersAsync(
- orderCancellationRequests: OrderCancellationRequest[],
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema(
- 'orderCancellationRequests',
- orderCancellationRequests,
- schemas.orderCancellationRequestsSchema,
- );
- const exchangeContractAddresses = _.map(
- orderCancellationRequests,
- orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker);
- assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
- const maker = makers[0];
- await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper);
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- for (const orderCancellationRequest of orderCancellationRequests) {
- const orderHash = utils.getOrderHashHex(orderCancellationRequest.order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- orderCancellationRequest.order,
- orderCancellationRequest.takerTokenCancelAmount,
- unavailableTakerTokenAmount,
- );
- }
- }
- if (_.isEmpty(orderCancellationRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
- const exchangeInstance = await this._getExchangeContractAsync();
- const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => {
- return [
- ...ExchangeWrapper._getOrderAddressesAndValues(cancellationRequest.order),
- cancellationRequest.takerTokenCancelAmount,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddresses, orderValues, cancelTakerTokenAmounts] = _.unzip<any>(
- orderAddressesValuesAndTakerTokenCancelAmounts,
- );
- const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmounts,
- {
- from: maker,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Subscribe to an event type emitted by the Exchange contract.
- * @param eventName The exchange contract event you would like to subscribe to.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
- * @param callback Callback that gets called when a log is added/removed
- * @return Subscription token used later to unsubscribe
- */
- public subscribe<ArgsType extends ExchangeContractEventArgs>(
- eventName: ExchangeEvents,
- indexFilterValues: IndexedFilterValues,
- callback: EventCallback<ArgsType>,
- ): string {
- assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- assert.isFunction('callback', callback);
- const exchangeContractAddress = this.getContractAddress();
- const subscriptionToken = this._subscribe<ArgsType>(
- exchangeContractAddress,
- eventName,
- indexFilterValues,
- artifacts.ExchangeArtifact.abi,
- callback,
- );
- return subscriptionToken;
- }
- /**
- * Cancel a subscription
- * @param subscriptionToken Subscription token returned by `subscribe()`
- */
- public unsubscribe(subscriptionToken: string): void {
- this._unsubscribe(subscriptionToken);
- }
- /**
- * Cancels all existing subscriptions
- */
- public unsubscribeAll(): void {
- super.unsubscribeAll();
- }
- /**
- * Gets historical logs without creating a subscription
- * @param eventName The exchange contract event you would like to subscribe to.
- * @param blockRange Block range to get logs from.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
- * @return Array of logs that match the parameters
- */
- public async getLogsAsync<ArgsType extends ExchangeContractEventArgs>(
- eventName: ExchangeEvents,
- blockRange: BlockRange,
- indexFilterValues: IndexedFilterValues,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
- assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- const exchangeContractAddress = this.getContractAddress();
- const logs = await this._getLogsAsync<ArgsType>(
- exchangeContractAddress,
- eventName,
- blockRange,
- indexFilterValues,
- artifacts.ExchangeArtifact.abi,
- );
- return logs;
- }
- /**
- * Retrieves the Ethereum address of the Exchange contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the Exchange contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.ExchangeArtifact, this._contractAddressIfExists);
- return contractAddress;
- }
- /**
- * Checks if order is still fillable and throws an error otherwise. Useful for orderbook
- * pruning where you want to remove stale orders without knowing who the taker will be.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to validate.
- * @param opts An object that conforms to the ValidateOrderFillableOpts
- * interface. Allows specifying a specific fillTakerTokenAmount
- * to validate for.
- */
- public async validateOrderFillableOrThrowAsync(
- signedOrder: SignedOrder,
- opts?: ValidateOrderFillableOpts,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined;
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- await this._orderValidationUtils.validateOrderFillableOrThrowAsync(
- exchangeTradeEmulator,
- signedOrder,
- zrxTokenAddress,
- expectedFillTakerTokenAmount,
- );
- }
- /**
- * Checks if order fill will succeed and throws an error otherwise.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @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.
- */
- public async validateFillOrderThrowIfInvalidAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
- /**
- * Checks if cancelling a given order will succeed and throws an informative error if it won't.
- * @param order An object that conforms to the Order or SignedOrder interface.
- * The order you would like to cancel.
- * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
- */
- public async validateCancelOrderThrowIfInvalidAsync(
- order: Order,
- cancelTakerTokenAmount: BigNumber,
- ): Promise<void> {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount);
- const orderHash = utils.getOrderHashHex(order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- order,
- cancelTakerTokenAmount,
- unavailableTakerTokenAmount,
- );
- }
- /**
- * Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @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.
- */
- public async validateFillOrKillOrderThrowIfInvalidAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
- /**
- * Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing:
- * `(fillTakerTokenAmount * makerTokenAmount) / takerTokenAmount`.
- * 0x Protocol does not accept any trades that result in large rounding errors. This means that tokens with few or
- * no decimals can only be filled in quantities and ratios that avoid large rounding errors.
- * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that you wish to fill.
- * @param takerTokenAmount The order size on the taker side
- * @param makerTokenAmount The order size on the maker side
- */
- public async isRoundingErrorAsync(
- fillTakerTokenAmount: BigNumber,
- takerTokenAmount: BigNumber,
- makerTokenAmount: BigNumber,
- ): Promise<boolean> {
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isValidBaseUnitAmount('takerTokenAmount', takerTokenAmount);
- assert.isValidBaseUnitAmount('makerTokenAmount', makerTokenAmount);
- const exchangeInstance = await this._getExchangeContractAsync();
- const isRoundingError = await exchangeInstance.isRoundingError.callAsync(
- fillTakerTokenAmount,
- takerTokenAmount,
- makerTokenAmount,
- );
- return isRoundingError;
- }
- /**
- * Checks if logs contain LogError, which is emmited by Exchange contract on transaction failure.
- * @param logs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync`
- */
- public throwLogErrorsAsErrors(logs: Array<LogWithDecodedArgs<DecodedLogArgs> | Web3.LogEntry>): void {
- const errLog = _.find(logs, {
- event: ExchangeEvents.LogError,
- }) as LogWithDecodedArgs<LogErrorContractEventArgs> | undefined;
- if (!_.isUndefined(errLog)) {
- const logArgs = errLog.args;
- const errCode = logArgs.errorId.toNumber();
- const errMessage = this._exchangeContractErrCodesToMsg[errCode];
- throw new Error(errMessage);
- }
- }
- /**
- * Returns the ZRX token address used by the exchange contract.
- * @return Address of ZRX token
- */
- public getZRXTokenAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.ZRXArtifact, this._zrxContractAddressIfExists);
- return contractAddress;
- }
- private _invalidateContractInstances(): void {
- this.unsubscribeAll();
- delete this._exchangeContractIfExists;
- }
- private async _isValidSignatureUsingContractCallAsync(
- dataHex: string,
- ecSignature: ECSignature,
- signerAddressHex: string,
- ): Promise<boolean> {
- assert.isHexString('dataHex', dataHex);
- assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema);
- assert.isETHAddressHex('signerAddressHex', signerAddressHex);
+ const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
+ const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync(
+ orderAddresses,
+ orderValues,
+ cancelTakerTokenAmount,
+ {
+ from: order.maker,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction.
+ * All orders must be from the same maker.
+ * @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest
+ * interface.
+ * @param transactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async batchCancelOrdersAsync(
+ orderCancellationRequests: OrderCancellationRequest[],
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema(
+ 'orderCancellationRequests',
+ orderCancellationRequests,
+ schemas.orderCancellationRequestsSchema,
+ );
+ const exchangeContractAddresses = _.map(
+ orderCancellationRequests,
+ orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress,
+ );
+ assert.hasAtMostOneUniqueValue(
+ exchangeContractAddresses,
+ ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
+ );
+ const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker);
+ assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
+ const maker = makers[0];
+ await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper);
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ for (const orderCancellationRequest of orderCancellationRequests) {
+ const orderHash = utils.getOrderHashHex(orderCancellationRequest.order);
+ const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
+ OrderValidationUtils.validateCancelOrderThrowIfInvalid(
+ orderCancellationRequest.order,
+ orderCancellationRequest.takerTokenCancelAmount,
+ unavailableTakerTokenAmount,
+ );
+ }
+ }
+ if (_.isEmpty(orderCancellationRequests)) {
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ }
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => {
+ return [
+ ...ExchangeWrapper._getOrderAddressesAndValues(cancellationRequest.order),
+ cancellationRequest.takerTokenCancelAmount,
+ ];
+ });
+ // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
+ const [orderAddresses, orderValues, cancelTakerTokenAmounts] = _.unzip<any>(
+ orderAddressesValuesAndTakerTokenCancelAmounts,
+ );
+ const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync(
+ orderAddresses,
+ orderValues,
+ cancelTakerTokenAmounts,
+ {
+ from: maker,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Subscribe to an event type emitted by the Exchange contract.
+ * @param eventName The exchange contract event you would like to subscribe to.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
+ * @param callback Callback that gets called when a log is added/removed
+ * @return Subscription token used later to unsubscribe
+ */
+ public subscribe<ArgsType extends ExchangeContractEventArgs>(
+ eventName: ExchangeEvents,
+ indexFilterValues: IndexedFilterValues,
+ callback: EventCallback<ArgsType>,
+ ): string {
+ assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ assert.isFunction('callback', callback);
+ const exchangeContractAddress = this.getContractAddress();
+ const subscriptionToken = this._subscribe<ArgsType>(
+ exchangeContractAddress,
+ eventName,
+ indexFilterValues,
+ artifacts.ExchangeArtifact.abi,
+ callback,
+ );
+ return subscriptionToken;
+ }
+ /**
+ * Cancel a subscription
+ * @param subscriptionToken Subscription token returned by `subscribe()`
+ */
+ public unsubscribe(subscriptionToken: string): void {
+ this._unsubscribe(subscriptionToken);
+ }
+ /**
+ * Cancels all existing subscriptions
+ */
+ public unsubscribeAll(): void {
+ super.unsubscribeAll();
+ }
+ /**
+ * Gets historical logs without creating a subscription
+ * @param eventName The exchange contract event you would like to subscribe to.
+ * @param blockRange Block range to get logs from.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
+ * @return Array of logs that match the parameters
+ */
+ public async getLogsAsync<ArgsType extends ExchangeContractEventArgs>(
+ eventName: ExchangeEvents,
+ blockRange: BlockRange,
+ indexFilterValues: IndexedFilterValues,
+ ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
+ assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
+ assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ const exchangeContractAddress = this.getContractAddress();
+ const logs = await this._getLogsAsync<ArgsType>(
+ exchangeContractAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ artifacts.ExchangeArtifact.abi,
+ );
+ return logs;
+ }
+ /**
+ * Retrieves the Ethereum address of the Exchange contract deployed on the network
+ * that the user-passed web3 provider is connected to.
+ * @returns The Ethereum address of the Exchange contract being used.
+ */
+ public getContractAddress(): string {
+ const contractAddress = this._getContractAddress(artifacts.ExchangeArtifact, this._contractAddressIfExists);
+ return contractAddress;
+ }
+ /**
+ * Checks if order is still fillable and throws an error otherwise. Useful for orderbook
+ * pruning where you want to remove stale orders without knowing who the taker will be.
+ * @param signedOrder An object that conforms to the SignedOrder interface. The
+ * signedOrder you wish to validate.
+ * @param opts An object that conforms to the ValidateOrderFillableOpts
+ * interface. Allows specifying a specific fillTakerTokenAmount
+ * to validate for.
+ */
+ public async validateOrderFillableOrThrowAsync(
+ signedOrder: SignedOrder,
+ opts?: ValidateOrderFillableOpts,
+ ): Promise<void> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined;
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ await this._orderValidationUtils.validateOrderFillableOrThrowAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ zrxTokenAddress,
+ expectedFillTakerTokenAmount,
+ );
+ }
+ /**
+ * Checks if order fill will succeed and throws an error otherwise.
+ * @param signedOrder An object that conforms to the SignedOrder interface. The
+ * signedOrder you wish to fill.
+ * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
+ * @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.
+ */
+ public async validateFillOrderThrowIfInvalidAsync(
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ ): Promise<void> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
+ /**
+ * Checks if cancelling a given order will succeed and throws an informative error if it won't.
+ * @param order An object that conforms to the Order or SignedOrder interface.
+ * The order you would like to cancel.
+ * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
+ */
+ public async validateCancelOrderThrowIfInvalidAsync(
+ order: Order,
+ cancelTakerTokenAmount: BigNumber,
+ ): Promise<void> {
+ assert.doesConformToSchema('order', order, schemas.orderSchema);
+ assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount);
+ const orderHash = utils.getOrderHashHex(order);
+ const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
+ OrderValidationUtils.validateCancelOrderThrowIfInvalid(
+ order,
+ cancelTakerTokenAmount,
+ unavailableTakerTokenAmount,
+ );
+ }
+ /**
+ * Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't.
+ * @param signedOrder An object that conforms to the SignedOrder interface. The
+ * signedOrder you wish to fill.
+ * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
+ * @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.
+ */
+ public async validateFillOrKillOrderThrowIfInvalidAsync(
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ ): Promise<void> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
+ /**
+ * Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing:
+ * `(fillTakerTokenAmount * makerTokenAmount) / takerTokenAmount`.
+ * 0x Protocol does not accept any trades that result in large rounding errors. This means that tokens with few or
+ * no decimals can only be filled in quantities and ratios that avoid large rounding errors.
+ * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that you wish to fill.
+ * @param takerTokenAmount The order size on the taker side
+ * @param makerTokenAmount The order size on the maker side
+ */
+ public async isRoundingErrorAsync(
+ fillTakerTokenAmount: BigNumber,
+ takerTokenAmount: BigNumber,
+ makerTokenAmount: BigNumber,
+ ): Promise<boolean> {
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ assert.isValidBaseUnitAmount('takerTokenAmount', takerTokenAmount);
+ assert.isValidBaseUnitAmount('makerTokenAmount', makerTokenAmount);
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const isRoundingError = await exchangeInstance.isRoundingError.callAsync(
+ fillTakerTokenAmount,
+ takerTokenAmount,
+ makerTokenAmount,
+ );
+ return isRoundingError;
+ }
+ /**
+ * Checks if logs contain LogError, which is emmited by Exchange contract on transaction failure.
+ * @param logs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync`
+ */
+ public throwLogErrorsAsErrors(logs: Array<LogWithDecodedArgs<DecodedLogArgs> | Web3.LogEntry>): void {
+ const errLog = _.find(logs, {
+ event: ExchangeEvents.LogError,
+ }) as LogWithDecodedArgs<LogErrorContractEventArgs> | undefined;
+ if (!_.isUndefined(errLog)) {
+ const logArgs = errLog.args;
+ const errCode = logArgs.errorId.toNumber();
+ const errMessage = this._exchangeContractErrCodesToMsg[errCode];
+ throw new Error(errMessage);
+ }
+ }
+ /**
+ * Returns the ZRX token address used by the exchange contract.
+ * @return Address of ZRX token
+ */
+ public getZRXTokenAddress(): string {
+ const contractAddress = this._getContractAddress(artifacts.ZRXArtifact, this._zrxContractAddressIfExists);
+ return contractAddress;
+ }
+ private _invalidateContractInstances(): void {
+ this.unsubscribeAll();
+ delete this._exchangeContractIfExists;
+ }
+ private async _isValidSignatureUsingContractCallAsync(
+ dataHex: string,
+ ecSignature: ECSignature,
+ signerAddressHex: string,
+ ): Promise<boolean> {
+ assert.isHexString('dataHex', dataHex);
+ assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema);
+ assert.isETHAddressHex('signerAddressHex', signerAddressHex);
- const exchangeInstance = await this._getExchangeContractAsync();
+ const exchangeInstance = await this._getExchangeContractAsync();
- const isValidSignature = await exchangeInstance.isValidSignature.callAsync(
- signerAddressHex,
- dataHex,
- ecSignature.v,
- ecSignature.r,
- ecSignature.s,
- );
- return isValidSignature;
- }
- private async _getOrderHashHexUsingContractCallAsync(order: Order | SignedOrder): Promise<string> {
- const exchangeInstance = await this._getExchangeContractAsync();
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
- const orderHashHex = await exchangeInstance.getOrderHash.callAsync(orderAddresses, orderValues);
- return orderHashHex;
- }
- private async _getExchangeContractAsync(): Promise<ExchangeContract> {
- if (!_.isUndefined(this._exchangeContractIfExists)) {
- return this._exchangeContractIfExists;
- }
- const web3ContractInstance = await this._instantiateContractIfExistsAsync(
- artifacts.ExchangeArtifact,
- this._contractAddressIfExists,
- );
- const contractInstance = new ExchangeContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
- this._exchangeContractIfExists = contractInstance;
- return this._exchangeContractIfExists;
- }
- private async _getTokenTransferProxyAddressAsync(): Promise<string> {
- const exchangeInstance = await this._getExchangeContractAsync();
- const tokenTransferProxyAddress = await exchangeInstance.TOKEN_TRANSFER_PROXY_CONTRACT.callAsync();
- const tokenTransferProxyAddressLowerCase = tokenTransferProxyAddress.toLowerCase();
- return tokenTransferProxyAddressLowerCase;
- }
+ const isValidSignature = await exchangeInstance.isValidSignature.callAsync(
+ signerAddressHex,
+ dataHex,
+ ecSignature.v,
+ ecSignature.r,
+ ecSignature.s,
+ );
+ return isValidSignature;
+ }
+ private async _getOrderHashHexUsingContractCallAsync(order: Order | SignedOrder): Promise<string> {
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
+ const orderHashHex = await exchangeInstance.getOrderHash.callAsync(orderAddresses, orderValues);
+ return orderHashHex;
+ }
+ private async _getExchangeContractAsync(): Promise<ExchangeContract> {
+ if (!_.isUndefined(this._exchangeContractIfExists)) {
+ return this._exchangeContractIfExists;
+ }
+ const web3ContractInstance = await this._instantiateContractIfExistsAsync(
+ artifacts.ExchangeArtifact,
+ this._contractAddressIfExists,
+ );
+ const contractInstance = new ExchangeContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
+ this._exchangeContractIfExists = contractInstance;
+ return this._exchangeContractIfExists;
+ }
+ private async _getTokenTransferProxyAddressAsync(): Promise<string> {
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const tokenTransferProxyAddress = await exchangeInstance.TOKEN_TRANSFER_PROXY_CONTRACT.callAsync();
+ const tokenTransferProxyAddressLowerCase = tokenTransferProxyAddress.toLowerCase();
+ return tokenTransferProxyAddressLowerCase;
+ }
} // tslint:disable:max-file-line-count
diff --git a/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts
index f54aaf0f8..bce48a590 100644
--- a/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts
@@ -13,117 +13,117 @@ import { TokenRegistryContract } from './generated/token_registry';
* This class includes all the functionality related to interacting with the 0x Token Registry smart contract.
*/
export class TokenRegistryWrapper extends ContractWrapper {
- private _tokenRegistryContractIfExists?: TokenRegistryContract;
- private _contractAddressIfExists?: string;
- private static _createTokenFromMetadata(metadata: TokenMetadata): Token | undefined {
- if (metadata[0] === constants.NULL_ADDRESS) {
- return undefined;
- }
- const token = {
- address: metadata[0],
- name: metadata[1],
- symbol: metadata[2],
- decimals: metadata[3].toNumber(),
- };
- return token;
- }
- constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
- super(web3Wrapper, networkId);
- this._contractAddressIfExists = contractAddressIfExists;
- }
- /**
- * Retrieves all the tokens currently listed in the Token Registry smart contract
- * @return An array of objects that conform to the Token interface.
- */
- public async getTokensAsync(): Promise<Token[]> {
- const addresses = await this.getTokenAddressesAsync();
- const tokenPromises: Array<Promise<Token | undefined>> = _.map(addresses, async (address: string) =>
- this.getTokenIfExistsAsync(address),
- );
- const tokens = await Promise.all(tokenPromises);
- return tokens as Token[];
- }
- /**
- * Retrieves all the addresses of the tokens currently listed in the Token Registry smart contract
- * @return An array of token addresses.
- */
- public async getTokenAddressesAsync(): Promise<string[]> {
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addresses = await tokenRegistryContract.getTokenAddresses.callAsync();
- return addresses;
- }
- /**
- * Retrieves a token by address currently listed in the Token Registry smart contract
- * @return An object that conforms to the Token interface or undefined if token not found.
- */
- public async getTokenIfExistsAsync(address: string): Promise<Token | undefined> {
- assert.isETHAddressHex('address', address);
+ private _tokenRegistryContractIfExists?: TokenRegistryContract;
+ private _contractAddressIfExists?: string;
+ private static _createTokenFromMetadata(metadata: TokenMetadata): Token | undefined {
+ if (metadata[0] === constants.NULL_ADDRESS) {
+ return undefined;
+ }
+ const token = {
+ address: metadata[0],
+ name: metadata[1],
+ symbol: metadata[2],
+ decimals: metadata[3].toNumber(),
+ };
+ return token;
+ }
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
+ super(web3Wrapper, networkId);
+ this._contractAddressIfExists = contractAddressIfExists;
+ }
+ /**
+ * Retrieves all the tokens currently listed in the Token Registry smart contract
+ * @return An array of objects that conform to the Token interface.
+ */
+ public async getTokensAsync(): Promise<Token[]> {
+ const addresses = await this.getTokenAddressesAsync();
+ const tokenPromises: Array<Promise<Token | undefined>> = _.map(addresses, async (address: string) =>
+ this.getTokenIfExistsAsync(address),
+ );
+ const tokens = await Promise.all(tokenPromises);
+ return tokens as Token[];
+ }
+ /**
+ * Retrieves all the addresses of the tokens currently listed in the Token Registry smart contract
+ * @return An array of token addresses.
+ */
+ public async getTokenAddressesAsync(): Promise<string[]> {
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const addresses = await tokenRegistryContract.getTokenAddresses.callAsync();
+ return addresses;
+ }
+ /**
+ * Retrieves a token by address currently listed in the Token Registry smart contract
+ * @return An object that conforms to the Token interface or undefined if token not found.
+ */
+ public async getTokenIfExistsAsync(address: string): Promise<Token | undefined> {
+ assert.isETHAddressHex('address', address);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(address);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise<string | undefined> {
- assert.isString('symbol', symbol);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addressIfExists = await tokenRegistryContract.getTokenAddressBySymbol.callAsync(symbol);
- if (addressIfExists === constants.NULL_ADDRESS) {
- return undefined;
- }
- return addressIfExists;
- }
- public async getTokenAddressByNameIfExistsAsync(name: string): Promise<string | undefined> {
- assert.isString('name', name);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addressIfExists = await tokenRegistryContract.getTokenAddressByName.callAsync(name);
- if (addressIfExists === constants.NULL_ADDRESS) {
- return undefined;
- }
- return addressIfExists;
- }
- public async getTokenBySymbolIfExistsAsync(symbol: string): Promise<Token | undefined> {
- assert.isString('symbol', symbol);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenBySymbol.callAsync(symbol);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- public async getTokenByNameIfExistsAsync(name: string): Promise<Token | undefined> {
- assert.isString('name', name);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenByName.callAsync(name);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- /**
- * Retrieves the Ethereum address of the TokenRegistry contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the TokenRegistry contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(
- artifacts.TokenRegistryArtifact,
- this._contractAddressIfExists,
- );
- return contractAddress;
- }
- private _invalidateContractInstance(): void {
- delete this._tokenRegistryContractIfExists;
- }
- private async _getTokenRegistryContractAsync(): Promise<TokenRegistryContract> {
- if (!_.isUndefined(this._tokenRegistryContractIfExists)) {
- return this._tokenRegistryContractIfExists;
- }
- const web3ContractInstance = await this._instantiateContractIfExistsAsync(
- artifacts.TokenRegistryArtifact,
- this._contractAddressIfExists,
- );
- const contractInstance = new TokenRegistryContract(
- web3ContractInstance,
- this._web3Wrapper.getContractDefaults(),
- );
- this._tokenRegistryContractIfExists = contractInstance;
- return this._tokenRegistryContractIfExists;
- }
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(address);
+ const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
+ return token;
+ }
+ public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise<string | undefined> {
+ assert.isString('symbol', symbol);
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const addressIfExists = await tokenRegistryContract.getTokenAddressBySymbol.callAsync(symbol);
+ if (addressIfExists === constants.NULL_ADDRESS) {
+ return undefined;
+ }
+ return addressIfExists;
+ }
+ public async getTokenAddressByNameIfExistsAsync(name: string): Promise<string | undefined> {
+ assert.isString('name', name);
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const addressIfExists = await tokenRegistryContract.getTokenAddressByName.callAsync(name);
+ if (addressIfExists === constants.NULL_ADDRESS) {
+ return undefined;
+ }
+ return addressIfExists;
+ }
+ public async getTokenBySymbolIfExistsAsync(symbol: string): Promise<Token | undefined> {
+ assert.isString('symbol', symbol);
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const metadata = await tokenRegistryContract.getTokenBySymbol.callAsync(symbol);
+ const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
+ return token;
+ }
+ public async getTokenByNameIfExistsAsync(name: string): Promise<Token | undefined> {
+ assert.isString('name', name);
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const metadata = await tokenRegistryContract.getTokenByName.callAsync(name);
+ const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
+ return token;
+ }
+ /**
+ * Retrieves the Ethereum address of the TokenRegistry contract deployed on the network
+ * that the user-passed web3 provider is connected to.
+ * @returns The Ethereum address of the TokenRegistry contract being used.
+ */
+ public getContractAddress(): string {
+ const contractAddress = this._getContractAddress(
+ artifacts.TokenRegistryArtifact,
+ this._contractAddressIfExists,
+ );
+ return contractAddress;
+ }
+ private _invalidateContractInstance(): void {
+ delete this._tokenRegistryContractIfExists;
+ }
+ private async _getTokenRegistryContractAsync(): Promise<TokenRegistryContract> {
+ if (!_.isUndefined(this._tokenRegistryContractIfExists)) {
+ return this._tokenRegistryContractIfExists;
+ }
+ const web3ContractInstance = await this._instantiateContractIfExistsAsync(
+ artifacts.TokenRegistryArtifact,
+ this._contractAddressIfExists,
+ );
+ const contractInstance = new TokenRegistryContract(
+ web3ContractInstance,
+ this._web3Wrapper.getContractDefaults(),
+ );
+ this._tokenRegistryContractIfExists = contractInstance;
+ return this._tokenRegistryContractIfExists;
+ }
}
diff --git a/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts
index f5d9d108a..635588fed 100644
--- a/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts
@@ -10,59 +10,59 @@ import { TokenTransferProxyContract } from './generated/token_transfer_proxy';
* This class includes the functionality related to interacting with the TokenTransferProxy contract.
*/
export class TokenTransferProxyWrapper extends ContractWrapper {
- private _tokenTransferProxyContractIfExists?: TokenTransferProxyContract;
- private _contractAddressIfExists?: string;
- constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
- super(web3Wrapper, networkId);
- this._contractAddressIfExists = contractAddressIfExists;
- }
- /**
- * Check if the Exchange contract address is authorized by the TokenTransferProxy contract.
- * @param exchangeContractAddress The hex encoded address of the Exchange contract to call.
- * @return Whether the exchangeContractAddress is authorized.
- */
- public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
- const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
- const isAuthorized = await tokenTransferProxyContractInstance.authorized.callAsync(exchangeContractAddress);
- return isAuthorized;
- }
- /**
- * Get the list of all Exchange contract addresses authorized by the TokenTransferProxy contract.
- * @return The list of authorized addresses.
- */
- public async getAuthorizedAddressesAsync(): Promise<string[]> {
- const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
- const authorizedAddresses = await tokenTransferProxyContractInstance.getAuthorizedAddresses.callAsync();
- return authorizedAddresses;
- }
- /**
- * Retrieves the Ethereum address of the TokenTransferProxy contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the TokenTransferProxy contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(
- artifacts.TokenTransferProxyArtifact,
- this._contractAddressIfExists,
- );
- return contractAddress;
- }
- private _invalidateContractInstance(): void {
- delete this._tokenTransferProxyContractIfExists;
- }
- private async _getTokenTransferProxyContractAsync(): Promise<TokenTransferProxyContract> {
- if (!_.isUndefined(this._tokenTransferProxyContractIfExists)) {
- return this._tokenTransferProxyContractIfExists;
- }
- const web3ContractInstance = await this._instantiateContractIfExistsAsync(
- artifacts.TokenTransferProxyArtifact,
- this._contractAddressIfExists,
- );
- const contractInstance = new TokenTransferProxyContract(
- web3ContractInstance,
- this._web3Wrapper.getContractDefaults(),
- );
- this._tokenTransferProxyContractIfExists = contractInstance;
- return this._tokenTransferProxyContractIfExists;
- }
+ private _tokenTransferProxyContractIfExists?: TokenTransferProxyContract;
+ private _contractAddressIfExists?: string;
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
+ super(web3Wrapper, networkId);
+ this._contractAddressIfExists = contractAddressIfExists;
+ }
+ /**
+ * Check if the Exchange contract address is authorized by the TokenTransferProxy contract.
+ * @param exchangeContractAddress The hex encoded address of the Exchange contract to call.
+ * @return Whether the exchangeContractAddress is authorized.
+ */
+ public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
+ const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
+ const isAuthorized = await tokenTransferProxyContractInstance.authorized.callAsync(exchangeContractAddress);
+ return isAuthorized;
+ }
+ /**
+ * Get the list of all Exchange contract addresses authorized by the TokenTransferProxy contract.
+ * @return The list of authorized addresses.
+ */
+ public async getAuthorizedAddressesAsync(): Promise<string[]> {
+ const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
+ const authorizedAddresses = await tokenTransferProxyContractInstance.getAuthorizedAddresses.callAsync();
+ return authorizedAddresses;
+ }
+ /**
+ * Retrieves the Ethereum address of the TokenTransferProxy contract deployed on the network
+ * that the user-passed web3 provider is connected to.
+ * @returns The Ethereum address of the TokenTransferProxy contract being used.
+ */
+ public getContractAddress(): string {
+ const contractAddress = this._getContractAddress(
+ artifacts.TokenTransferProxyArtifact,
+ this._contractAddressIfExists,
+ );
+ return contractAddress;
+ }
+ private _invalidateContractInstance(): void {
+ delete this._tokenTransferProxyContractIfExists;
+ }
+ private async _getTokenTransferProxyContractAsync(): Promise<TokenTransferProxyContract> {
+ if (!_.isUndefined(this._tokenTransferProxyContractIfExists)) {
+ return this._tokenTransferProxyContractIfExists;
+ }
+ const web3ContractInstance = await this._instantiateContractIfExistsAsync(
+ artifacts.TokenTransferProxyArtifact,
+ this._contractAddressIfExists,
+ );
+ const contractInstance = new TokenTransferProxyContract(
+ web3ContractInstance,
+ this._web3Wrapper.getContractDefaults(),
+ );
+ this._tokenTransferProxyContractIfExists = contractInstance;
+ return this._tokenTransferProxyContractIfExists;
+ }
}
diff --git a/packages/0x.js/src/contract_wrappers/token_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_wrapper.ts
index 7943f4a60..366cabd9e 100644
--- a/packages/0x.js/src/contract_wrappers/token_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/token_wrapper.ts
@@ -5,15 +5,15 @@ import * as _ from 'lodash';
import { artifacts } from '../artifacts';
import {
- BlockRange,
- EventCallback,
- IndexedFilterValues,
- LogWithDecodedArgs,
- MethodOpts,
- TokenContractEventArgs,
- TokenEvents,
- TransactionOpts,
- ZeroExError,
+ BlockRange,
+ EventCallback,
+ IndexedFilterValues,
+ LogWithDecodedArgs,
+ MethodOpts,
+ TokenContractEventArgs,
+ TokenEvents,
+ TransactionOpts,
+ ZeroExError,
} from '../types';
import { AbiDecoder } from '../utils/abi_decoder';
import { assert } from '../utils/assert';
@@ -29,367 +29,367 @@ import { TokenTransferProxyWrapper } from './token_transfer_proxy_wrapper';
* to the 0x Proxy smart contract.
*/
export class TokenWrapper extends ContractWrapper {
- public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
- private _tokenContractsByAddress: { [address: string]: TokenContract };
- private _tokenTransferProxyWrapper: TokenTransferProxyWrapper;
- constructor(
- web3Wrapper: Web3Wrapper,
- networkId: number,
- abiDecoder: AbiDecoder,
- tokenTransferProxyWrapper: TokenTransferProxyWrapper,
- ) {
- super(web3Wrapper, networkId, abiDecoder);
- this._tokenContractsByAddress = {};
- this._tokenTransferProxyWrapper = tokenTransferProxyWrapper;
- }
- /**
- * Retrieves an owner's ERC20 token balance.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address whose balance you would like to check.
- * @param methodOpts Optional arguments this method accepts.
- * @return The owner's ERC20 token balance in base units.
- */
- public async getBalanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- methodOpts?: MethodOpts,
- ): Promise<BigNumber> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
+ public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
+ private _tokenContractsByAddress: { [address: string]: TokenContract };
+ private _tokenTransferProxyWrapper: TokenTransferProxyWrapper;
+ constructor(
+ web3Wrapper: Web3Wrapper,
+ networkId: number,
+ abiDecoder: AbiDecoder,
+ tokenTransferProxyWrapper: TokenTransferProxyWrapper,
+ ) {
+ super(web3Wrapper, networkId, abiDecoder);
+ this._tokenContractsByAddress = {};
+ this._tokenTransferProxyWrapper = tokenTransferProxyWrapper;
+ }
+ /**
+ * Retrieves an owner's ERC20 token balance.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address whose balance you would like to check.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The owner's ERC20 token balance in base units.
+ */
+ public async getBalanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ methodOpts?: MethodOpts,
+ ): Promise<BigNumber> {
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
- const tokenContract = await this._getTokenContractAsync(tokenAddress);
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let balance = await tokenContract.balanceOf.callAsync(ownerAddress, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- balance = new BigNumber(balance);
- return balance;
- }
- /**
- * Sets the spender's allowance to a specified number of baseUnits on behalf of the owner address.
- * Equivalent to the ERC20 spec method `approve`.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance
- * for spenderAddress.
- * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance.
- * @param amountInBaseUnits The allowance amount you would like to set.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async setAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- spenderAddress: string,
- amountInBaseUnits: BigNumber,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper);
- assert.isETHAddressHex('spenderAddress', spenderAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
+ const tokenContract = await this._getTokenContractAsync(tokenAddress);
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ let balance = await tokenContract.balanceOf.callAsync(ownerAddress, defaultBlock);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ balance = new BigNumber(balance);
+ return balance;
+ }
+ /**
+ * Sets the spender's allowance to a specified number of baseUnits on behalf of the owner address.
+ * Equivalent to the ERC20 spec method `approve`.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance
+ * for spenderAddress.
+ * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance.
+ * @param amountInBaseUnits The allowance amount you would like to set.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ spenderAddress: string,
+ amountInBaseUnits: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper);
+ assert.isETHAddressHex('spenderAddress', spenderAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
- const tokenContract = await this._getTokenContractAsync(tokenAddress);
- const txHash = await tokenContract.approve.sendTransactionAsync(spenderAddress, amountInBaseUnits, {
- from: ownerAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
- return txHash;
- }
- /**
- * Sets the spender's allowance to an unlimited number of baseUnits on behalf of the owner address.
- * Equivalent to the ERC20 spec method `approve`.
- * Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating
- * allowances set to the max amount (e.g ZRX, WETH)
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance
- * for spenderAddress.
- * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async setUnlimitedAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- spenderAddress: string,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- const txHash = await this.setAllowanceAsync(
- tokenAddress,
- ownerAddress,
- spenderAddress,
- this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
- txOpts,
- );
- return txHash;
- }
- /**
- * Retrieves the owners allowance in baseUnits set to the spender's address.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address whose allowance to spenderAddress
- * you would like to retrieve.
- * @param spenderAddress The hex encoded user Ethereum address who can spend the allowance you are fetching.
- * @param methodOpts Optional arguments this method accepts.
- */
- public async getAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- spenderAddress: string,
- methodOpts?: MethodOpts,
- ): Promise<BigNumber> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
+ const tokenContract = await this._getTokenContractAsync(tokenAddress);
+ const txHash = await tokenContract.approve.sendTransactionAsync(spenderAddress, amountInBaseUnits, {
+ from: ownerAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ });
+ return txHash;
+ }
+ /**
+ * Sets the spender's allowance to an unlimited number of baseUnits on behalf of the owner address.
+ * Equivalent to the ERC20 spec method `approve`.
+ * Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating
+ * allowances set to the max amount (e.g ZRX, WETH)
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance
+ * for spenderAddress.
+ * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setUnlimitedAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ spenderAddress: string,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ const txHash = await this.setAllowanceAsync(
+ tokenAddress,
+ ownerAddress,
+ spenderAddress,
+ this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
+ txOpts,
+ );
+ return txHash;
+ }
+ /**
+ * Retrieves the owners allowance in baseUnits set to the spender's address.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address whose allowance to spenderAddress
+ * you would like to retrieve.
+ * @param spenderAddress The hex encoded user Ethereum address who can spend the allowance you are fetching.
+ * @param methodOpts Optional arguments this method accepts.
+ */
+ public async getAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ spenderAddress: string,
+ methodOpts?: MethodOpts,
+ ): Promise<BigNumber> {
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
- const tokenContract = await this._getTokenContractAsync(tokenAddress);
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let allowanceInBaseUnits = await tokenContract.allowance.callAsync(ownerAddress, spenderAddress, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- allowanceInBaseUnits = new BigNumber(allowanceInBaseUnits);
- return allowanceInBaseUnits;
- }
- /**
- * Retrieves the owner's allowance in baseUnits set to the 0x proxy contract.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address whose proxy contract allowance we are retrieving.
- * @param methodOpts Optional arguments this method accepts.
- */
- public async getProxyAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- methodOpts?: MethodOpts,
- ): Promise<BigNumber> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
+ const tokenContract = await this._getTokenContractAsync(tokenAddress);
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ let allowanceInBaseUnits = await tokenContract.allowance.callAsync(ownerAddress, spenderAddress, defaultBlock);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ allowanceInBaseUnits = new BigNumber(allowanceInBaseUnits);
+ return allowanceInBaseUnits;
+ }
+ /**
+ * Retrieves the owner's allowance in baseUnits set to the 0x proxy contract.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address whose proxy contract allowance we are retrieving.
+ * @param methodOpts Optional arguments this method accepts.
+ */
+ public async getProxyAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ methodOpts?: MethodOpts,
+ ): Promise<BigNumber> {
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
- const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
- const allowanceInBaseUnits = await this.getAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, methodOpts);
- return allowanceInBaseUnits;
- }
- /**
- * Sets the 0x proxy contract's allowance to a specified number of a tokens' baseUnits on behalf
- * of an owner address.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance
- * for the Proxy contract.
- * @param amountInBaseUnits The allowance amount specified in baseUnits.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async setProxyAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- amountInBaseUnits: BigNumber,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
+ const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
+ const allowanceInBaseUnits = await this.getAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, methodOpts);
+ return allowanceInBaseUnits;
+ }
+ /**
+ * Sets the 0x proxy contract's allowance to a specified number of a tokens' baseUnits on behalf
+ * of an owner address.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance
+ * for the Proxy contract.
+ * @param amountInBaseUnits The allowance amount specified in baseUnits.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setProxyAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ amountInBaseUnits: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
- const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
- const txHash = await this.setAllowanceAsync(
- tokenAddress,
- ownerAddress,
- proxyAddress,
- amountInBaseUnits,
- txOpts,
- );
- return txHash;
- }
- /**
- * Sets the 0x proxy contract's allowance to a unlimited number of a tokens' baseUnits on behalf
- * of an owner address.
- * Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating
- * allowances set to the max amount (e.g ZRX, WETH)
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance
- * for the Proxy contract.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async setUnlimitedProxyAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- const txHash = await this.setProxyAllowanceAsync(
- tokenAddress,
- ownerAddress,
- this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
- txOpts,
- );
- return txHash;
- }
- /**
- * Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param fromAddress The hex encoded user Ethereum address that will send the funds.
- * @param toAddress The hex encoded user Ethereum address that will receive the funds.
- * @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async transferAsync(
- tokenAddress: string,
- fromAddress: string,
- toAddress: string,
- amountInBaseUnits: BigNumber,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper);
- assert.isETHAddressHex('toAddress', toAddress);
- assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
+ const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
+ const txHash = await this.setAllowanceAsync(
+ tokenAddress,
+ ownerAddress,
+ proxyAddress,
+ amountInBaseUnits,
+ txOpts,
+ );
+ return txHash;
+ }
+ /**
+ * Sets the 0x proxy contract's allowance to a unlimited number of a tokens' baseUnits on behalf
+ * of an owner address.
+ * Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating
+ * allowances set to the max amount (e.g ZRX, WETH)
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance
+ * for the Proxy contract.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setUnlimitedProxyAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ const txHash = await this.setProxyAllowanceAsync(
+ tokenAddress,
+ ownerAddress,
+ this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
+ txOpts,
+ );
+ return txHash;
+ }
+ /**
+ * Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param fromAddress The hex encoded user Ethereum address that will send the funds.
+ * @param toAddress The hex encoded user Ethereum address that will receive the funds.
+ * @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async transferAsync(
+ tokenAddress: string,
+ fromAddress: string,
+ toAddress: string,
+ amountInBaseUnits: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper);
+ assert.isETHAddressHex('toAddress', toAddress);
+ assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
- const tokenContract = await this._getTokenContractAsync(tokenAddress);
+ const tokenContract = await this._getTokenContractAsync(tokenAddress);
- const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress);
- if (fromAddressBalance.lessThan(amountInBaseUnits)) {
- throw new Error(ZeroExError.InsufficientBalanceForTransfer);
- }
+ const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress);
+ if (fromAddressBalance.lessThan(amountInBaseUnits)) {
+ throw new Error(ZeroExError.InsufficientBalanceForTransfer);
+ }
- const txHash = await tokenContract.transfer.sendTransactionAsync(toAddress, amountInBaseUnits, {
- from: fromAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
- return txHash;
- }
- /**
- * Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
- * Requires the fromAddress to have sufficient funds and to have approved an allowance of
- * `amountInBaseUnits` to `senderAddress`.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param fromAddress The hex encoded user Ethereum address whose funds are being sent.
- * @param toAddress The hex encoded user Ethereum address that will receive the funds.
- * @param senderAddress The hex encoded user Ethereum address whose initiates the fund transfer. The
- * `fromAddress` must have set an allowance to the `senderAddress`
- * before this call.
- * @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async transferFromAsync(
- tokenAddress: string,
- fromAddress: string,
- toAddress: string,
- senderAddress: string,
- amountInBaseUnits: BigNumber,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.isETHAddressHex('fromAddress', fromAddress);
- assert.isETHAddressHex('toAddress', toAddress);
- await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper);
- assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
+ const txHash = await tokenContract.transfer.sendTransactionAsync(toAddress, amountInBaseUnits, {
+ from: fromAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ });
+ return txHash;
+ }
+ /**
+ * Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
+ * Requires the fromAddress to have sufficient funds and to have approved an allowance of
+ * `amountInBaseUnits` to `senderAddress`.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param fromAddress The hex encoded user Ethereum address whose funds are being sent.
+ * @param toAddress The hex encoded user Ethereum address that will receive the funds.
+ * @param senderAddress The hex encoded user Ethereum address whose initiates the fund transfer. The
+ * `fromAddress` must have set an allowance to the `senderAddress`
+ * before this call.
+ * @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async transferFromAsync(
+ tokenAddress: string,
+ fromAddress: string,
+ toAddress: string,
+ senderAddress: string,
+ amountInBaseUnits: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isETHAddressHex('fromAddress', fromAddress);
+ assert.isETHAddressHex('toAddress', toAddress);
+ await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper);
+ assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
- const tokenContract = await this._getTokenContractAsync(tokenAddress);
+ const tokenContract = await this._getTokenContractAsync(tokenAddress);
- const fromAddressAllowance = await this.getAllowanceAsync(tokenAddress, fromAddress, senderAddress);
- if (fromAddressAllowance.lessThan(amountInBaseUnits)) {
- throw new Error(ZeroExError.InsufficientAllowanceForTransfer);
- }
+ const fromAddressAllowance = await this.getAllowanceAsync(tokenAddress, fromAddress, senderAddress);
+ if (fromAddressAllowance.lessThan(amountInBaseUnits)) {
+ throw new Error(ZeroExError.InsufficientAllowanceForTransfer);
+ }
- const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress);
- if (fromAddressBalance.lessThan(amountInBaseUnits)) {
- throw new Error(ZeroExError.InsufficientBalanceForTransfer);
- }
+ const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress);
+ if (fromAddressBalance.lessThan(amountInBaseUnits)) {
+ throw new Error(ZeroExError.InsufficientBalanceForTransfer);
+ }
- const txHash = await tokenContract.transferFrom.sendTransactionAsync(
- fromAddress,
- toAddress,
- amountInBaseUnits,
- {
- from: senderAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Subscribe to an event type emitted by the Token contract.
- * @param tokenAddress The hex encoded address where the ERC20 token is deployed.
- * @param eventName The token contract event you would like to subscribe to.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
- * @param callback Callback that gets called when a log is added/removed
- * @return Subscription token used later to unsubscribe
- */
- public subscribe<ArgsType extends TokenContractEventArgs>(
- tokenAddress: string,
- eventName: TokenEvents,
- indexFilterValues: IndexedFilterValues,
- callback: EventCallback<ArgsType>,
- ): string {
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- assert.isFunction('callback', callback);
- const subscriptionToken = this._subscribe<ArgsType>(
- tokenAddress,
- eventName,
- indexFilterValues,
- artifacts.TokenArtifact.abi,
- callback,
- );
- return subscriptionToken;
- }
- /**
- * Cancel a subscription
- * @param subscriptionToken Subscription token returned by `subscribe()`
- */
- public unsubscribe(subscriptionToken: string): void {
- this._unsubscribe(subscriptionToken);
- }
- /**
- * Cancels all existing subscriptions
- */
- public unsubscribeAll(): void {
- super.unsubscribeAll();
- }
- /**
- * Gets historical logs without creating a subscription
- * @param tokenAddress An address of the token that emmited the logs.
- * @param eventName The token contract event you would like to subscribe to.
- * @param blockRange Block range to get logs from.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
- * @return Array of logs that match the parameters
- */
- public async getLogsAsync<ArgsType extends TokenContractEventArgs>(
- tokenAddress: string,
- eventName: TokenEvents,
- blockRange: BlockRange,
- indexFilterValues: IndexedFilterValues,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
- assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- const logs = await this._getLogsAsync<ArgsType>(
- tokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- artifacts.TokenArtifact.abi,
- );
- return logs;
- }
- private _invalidateContractInstances(): void {
- this.unsubscribeAll();
- this._tokenContractsByAddress = {};
- }
- private async _getTokenContractAsync(tokenAddress: string): Promise<TokenContract> {
- let tokenContract = this._tokenContractsByAddress[tokenAddress];
- if (!_.isUndefined(tokenContract)) {
- return tokenContract;
- }
- const web3ContractInstance = await this._instantiateContractIfExistsAsync(
- artifacts.TokenArtifact,
- tokenAddress,
- );
- const contractInstance = new TokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
- tokenContract = contractInstance;
- this._tokenContractsByAddress[tokenAddress] = tokenContract;
- return tokenContract;
- }
+ const txHash = await tokenContract.transferFrom.sendTransactionAsync(
+ fromAddress,
+ toAddress,
+ amountInBaseUnits,
+ {
+ from: senderAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Subscribe to an event type emitted by the Token contract.
+ * @param tokenAddress The hex encoded address where the ERC20 token is deployed.
+ * @param eventName The token contract event you would like to subscribe to.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
+ * @param callback Callback that gets called when a log is added/removed
+ * @return Subscription token used later to unsubscribe
+ */
+ public subscribe<ArgsType extends TokenContractEventArgs>(
+ tokenAddress: string,
+ eventName: TokenEvents,
+ indexFilterValues: IndexedFilterValues,
+ callback: EventCallback<ArgsType>,
+ ): string {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ assert.isFunction('callback', callback);
+ const subscriptionToken = this._subscribe<ArgsType>(
+ tokenAddress,
+ eventName,
+ indexFilterValues,
+ artifacts.TokenArtifact.abi,
+ callback,
+ );
+ return subscriptionToken;
+ }
+ /**
+ * Cancel a subscription
+ * @param subscriptionToken Subscription token returned by `subscribe()`
+ */
+ public unsubscribe(subscriptionToken: string): void {
+ this._unsubscribe(subscriptionToken);
+ }
+ /**
+ * Cancels all existing subscriptions
+ */
+ public unsubscribeAll(): void {
+ super.unsubscribeAll();
+ }
+ /**
+ * Gets historical logs without creating a subscription
+ * @param tokenAddress An address of the token that emmited the logs.
+ * @param eventName The token contract event you would like to subscribe to.
+ * @param blockRange Block range to get logs from.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
+ * @return Array of logs that match the parameters
+ */
+ public async getLogsAsync<ArgsType extends TokenContractEventArgs>(
+ tokenAddress: string,
+ eventName: TokenEvents,
+ blockRange: BlockRange,
+ indexFilterValues: IndexedFilterValues,
+ ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
+ assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ const logs = await this._getLogsAsync<ArgsType>(
+ tokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ artifacts.TokenArtifact.abi,
+ );
+ return logs;
+ }
+ private _invalidateContractInstances(): void {
+ this.unsubscribeAll();
+ this._tokenContractsByAddress = {};
+ }
+ private async _getTokenContractAsync(tokenAddress: string): Promise<TokenContract> {
+ let tokenContract = this._tokenContractsByAddress[tokenAddress];
+ if (!_.isUndefined(tokenContract)) {
+ return tokenContract;
+ }
+ const web3ContractInstance = await this._instantiateContractIfExistsAsync(
+ artifacts.TokenArtifact,
+ tokenAddress,
+ );
+ const contractInstance = new TokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
+ tokenContract = contractInstance;
+ this._tokenContractsByAddress[tokenAddress] = tokenContract;
+ return tokenContract;
+ }
}
diff --git a/packages/0x.js/src/globals.d.ts b/packages/0x.js/src/globals.d.ts
index 4f4932b6e..9a53949b4 100644
--- a/packages/0x.js/src/globals.d.ts
+++ b/packages/0x.js/src/globals.d.ts
@@ -10,50 +10,50 @@ declare module 'web3-provider-engine/subproviders/rpc';
// disallow `namespace`, we disable tslint for the following.
/* tslint:disable */
declare namespace Chai {
- interface Assertion {
- bignumber: Assertion;
- // HACK: In order to comply with chai-as-promised we make eventually a `PromisedAssertion` not an `Assertion`
- eventually: PromisedAssertion;
- }
+ interface Assertion {
+ bignumber: Assertion;
+ // HACK: In order to comply with chai-as-promised we make eventually a `PromisedAssertion` not an `Assertion`
+ eventually: PromisedAssertion;
+ }
}
/* tslint:enable */
declare module '*.json' {
- const json: any;
- /* tslint:disable */
- export default json;
- /* tslint:enable */
+ const json: any;
+ /* tslint:disable */
+ export default json;
+ /* tslint:enable */
}
declare module 'ethereumjs-abi' {
- const soliditySHA3: (argTypes: string[], args: any[]) => Buffer;
+ const soliditySHA3: (argTypes: string[], args: any[]) => Buffer;
}
// truffle-hdwallet-provider declarations
declare module 'truffle-hdwallet-provider' {
- import * as Web3 from 'web3';
- class HDWalletProvider implements Web3.Provider {
- constructor(mnemonic: string, rpcUrl: string);
- public sendAsync(
- payload: Web3.JSONRPCRequestPayload,
- callback: (err: Error, result: Web3.JSONRPCResponsePayload) => void,
- ): void;
- }
- export = HDWalletProvider;
+ import * as Web3 from 'web3';
+ class HDWalletProvider implements Web3.Provider {
+ constructor(mnemonic: string, rpcUrl: string);
+ public sendAsync(
+ payload: Web3.JSONRPCRequestPayload,
+ callback: (err: Error, result: Web3.JSONRPCResponsePayload) => void,
+ ): void;
+ }
+ export = HDWalletProvider;
}
// abi-decoder declarations
interface DecodedLogArg {}
interface DecodedLog {
- name: string;
- events: DecodedLogArg[];
+ name: string;
+ events: DecodedLogArg[];
}
declare module 'abi-decoder' {
- import * as Web3 from 'web3';
- const addABI: (abi: Web3.AbiDefinition) => void;
- const decodeLogs: (logs: Web3.LogEntry[]) => DecodedLog[];
+ import * as Web3 from 'web3';
+ const addABI: (abi: Web3.AbiDefinition) => void;
+ const decodeLogs: (logs: Web3.LogEntry[]) => DecodedLog[];
}
declare module 'web3/lib/solidity/coder' {
- const decodeParams: (types: string[], data: string) => any[];
+ const decodeParams: (types: string[], data: string) => any[];
}
diff --git a/packages/0x.js/src/globalsAugment.d.ts b/packages/0x.js/src/globalsAugment.d.ts
index 3e2f2216b..c2f200ab8 100644
--- a/packages/0x.js/src/globalsAugment.d.ts
+++ b/packages/0x.js/src/globalsAugment.d.ts
@@ -3,21 +3,21 @@ import { BigNumber } from '@0xproject/utils';
// HACK: This module overrides the Chai namespace so that we can use BigNumber types inside.
// Source: https://github.com/Microsoft/TypeScript/issues/7352#issuecomment-191547232
declare global {
- // HACK: In order to merge the bignumber declaration added by chai-bignumber to the chai Assertion
- // interface we must use `namespace` as the Chai definitelyTyped definition does. Since we otherwise
- // disallow `namespace`, we disable tslint for the following.
- /* tslint:disable */
- namespace Chai {
- interface NumberComparer {
- (value: number | BigNumber, message?: string): Assertion;
- }
- interface NumericComparison {
- greaterThan: NumberComparer;
- }
- }
- /* tslint:enable */
- interface DecodedLogArg {
- name: string;
- value: string | BigNumber;
- }
+ // HACK: In order to merge the bignumber declaration added by chai-bignumber to the chai Assertion
+ // interface we must use `namespace` as the Chai definitelyTyped definition does. Since we otherwise
+ // disallow `namespace`, we disable tslint for the following.
+ /* tslint:disable */
+ namespace Chai {
+ interface NumberComparer {
+ (value: number | BigNumber, message?: string): Assertion;
+ }
+ interface NumericComparison {
+ greaterThan: NumberComparer;
+ }
+ }
+ /* tslint:enable */
+ interface DecodedLogArg {
+ name: string;
+ value: string | BigNumber;
+ }
}
diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts
index 599c3a2b0..ad5ef8534 100644
--- a/packages/0x.js/src/index.ts
+++ b/packages/0x.js/src/index.ts
@@ -1,50 +1,50 @@
export { ZeroEx } from './0x';
export {
- Order,
- BlockParamLiteral,
- SignedOrder,
- ECSignature,
- ZeroExError,
- EventCallback,
- ExchangeContractErrs,
- ContractEvent,
- Token,
- ExchangeEvents,
- TokenEvents,
- IndexedFilterValues,
- BlockRange,
- BlockParam,
- OrderCancellationRequest,
- OrderFillRequest,
- LogErrorContractEventArgs,
- LogCancelContractEventArgs,
- LogFillContractEventArgs,
- ExchangeContractEventArgs,
- TransferContractEventArgs,
- ApprovalContractEventArgs,
- TokenContractEventArgs,
- EtherTokenContractEventArgs,
- WithdrawalContractEventArgs,
- DepositContractEventArgs,
- ContractEventArgs,
- ContractEventArg,
- Web3Provider,
- ZeroExConfig,
- EtherTokenEvents,
- TransactionReceiptWithDecodedLogs,
- LogWithDecodedArgs,
- MethodOpts,
- OrderTransactionOpts,
- TransactionOpts,
- FilterObject,
- LogEvent,
- DecodedLogEvent,
- EventWatcherCallback,
- OnOrderStateChangeCallback,
- OrderStateValid,
- OrderStateInvalid,
- OrderState,
+ Order,
+ BlockParamLiteral,
+ SignedOrder,
+ ECSignature,
+ ZeroExError,
+ EventCallback,
+ ExchangeContractErrs,
+ ContractEvent,
+ Token,
+ ExchangeEvents,
+ TokenEvents,
+ IndexedFilterValues,
+ BlockRange,
+ BlockParam,
+ OrderCancellationRequest,
+ OrderFillRequest,
+ LogErrorContractEventArgs,
+ LogCancelContractEventArgs,
+ LogFillContractEventArgs,
+ ExchangeContractEventArgs,
+ TransferContractEventArgs,
+ ApprovalContractEventArgs,
+ TokenContractEventArgs,
+ EtherTokenContractEventArgs,
+ WithdrawalContractEventArgs,
+ DepositContractEventArgs,
+ ContractEventArgs,
+ ContractEventArg,
+ Web3Provider,
+ ZeroExConfig,
+ EtherTokenEvents,
+ TransactionReceiptWithDecodedLogs,
+ LogWithDecodedArgs,
+ MethodOpts,
+ OrderTransactionOpts,
+ TransactionOpts,
+ FilterObject,
+ LogEvent,
+ DecodedLogEvent,
+ EventWatcherCallback,
+ OnOrderStateChangeCallback,
+ OrderStateValid,
+ OrderStateInvalid,
+ OrderState,
} from './types';
export { TransactionReceipt } from '@0xproject/types';
diff --git a/packages/0x.js/src/order_watcher/event_watcher.ts b/packages/0x.js/src/order_watcher/event_watcher.ts
index 5d05bfb60..3e3cd978d 100644
--- a/packages/0x.js/src/order_watcher/event_watcher.ts
+++ b/packages/0x.js/src/order_watcher/event_watcher.ts
@@ -9,8 +9,8 @@ import { assert } from '../utils/assert';
const DEFAULT_EVENT_POLLING_INTERVAL_MS = 200;
enum LogEventState {
- Removed,
- Added,
+ Removed,
+ Added,
}
/*
@@ -18,76 +18,76 @@ enum LogEventState {
* depth.
*/
export class EventWatcher {
- private _web3Wrapper: Web3Wrapper;
- private _pollingIntervalMs: number;
- private _intervalIdIfExists?: NodeJS.Timer;
- private _lastEvents: Web3.LogEntry[] = [];
- constructor(web3Wrapper: Web3Wrapper, pollingIntervalIfExistsMs: undefined | number) {
- this._web3Wrapper = web3Wrapper;
- this._pollingIntervalMs = _.isUndefined(pollingIntervalIfExistsMs)
- ? DEFAULT_EVENT_POLLING_INTERVAL_MS
- : pollingIntervalIfExistsMs;
- }
- public subscribe(callback: EventWatcherCallback): void {
- assert.isFunction('callback', callback);
- if (!_.isUndefined(this._intervalIdIfExists)) {
- throw new Error(ZeroExError.SubscriptionAlreadyPresent);
- }
- this._intervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
- this._pollForBlockchainEventsAsync.bind(this, callback),
- this._pollingIntervalMs,
- (err: Error) => {
- this.unsubscribe();
- callback(err);
- },
- );
- }
- public unsubscribe(): void {
- this._lastEvents = [];
- if (!_.isUndefined(this._intervalIdIfExists)) {
- intervalUtils.clearAsyncExcludingInterval(this._intervalIdIfExists);
- delete this._intervalIdIfExists;
- }
- }
- private async _pollForBlockchainEventsAsync(callback: EventWatcherCallback): Promise<void> {
- const pendingEvents = await this._getEventsAsync();
- if (_.isUndefined(pendingEvents)) {
- // HACK: This should never happen, but happens frequently on CI due to a ganache bug
- return;
- }
- if (pendingEvents.length === 0) {
- // HACK: Sometimes when node rebuilds the pending block we get back the empty result.
- // We don't want to emit a lot of removal events and bring them back after a couple of miliseconds,
- // that's why we just ignore those cases.
- return;
- }
- const removedEvents = _.differenceBy(this._lastEvents, pendingEvents, JSON.stringify);
- const newEvents = _.differenceBy(pendingEvents, this._lastEvents, JSON.stringify);
- await this._emitDifferencesAsync(removedEvents, LogEventState.Removed, callback);
- await this._emitDifferencesAsync(newEvents, LogEventState.Added, callback);
- this._lastEvents = pendingEvents;
- }
- private async _getEventsAsync(): Promise<Web3.LogEntry[]> {
- const eventFilter = {
- fromBlock: BlockParamLiteral.Pending,
- toBlock: BlockParamLiteral.Pending,
- };
- const events = await this._web3Wrapper.getLogsAsync(eventFilter);
- return events;
- }
- private async _emitDifferencesAsync(
- logs: Web3.LogEntry[],
- logEventState: LogEventState,
- callback: EventWatcherCallback,
- ): Promise<void> {
- for (const log of logs) {
- const logEvent = {
- removed: logEventState === LogEventState.Removed,
- ...log,
- };
- if (!_.isUndefined(this._intervalIdIfExists)) {
- callback(null, logEvent);
- }
- }
- }
+ private _web3Wrapper: Web3Wrapper;
+ private _pollingIntervalMs: number;
+ private _intervalIdIfExists?: NodeJS.Timer;
+ private _lastEvents: Web3.LogEntry[] = [];
+ constructor(web3Wrapper: Web3Wrapper, pollingIntervalIfExistsMs: undefined | number) {
+ this._web3Wrapper = web3Wrapper;
+ this._pollingIntervalMs = _.isUndefined(pollingIntervalIfExistsMs)
+ ? DEFAULT_EVENT_POLLING_INTERVAL_MS
+ : pollingIntervalIfExistsMs;
+ }
+ public subscribe(callback: EventWatcherCallback): void {
+ assert.isFunction('callback', callback);
+ if (!_.isUndefined(this._intervalIdIfExists)) {
+ throw new Error(ZeroExError.SubscriptionAlreadyPresent);
+ }
+ this._intervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
+ this._pollForBlockchainEventsAsync.bind(this, callback),
+ this._pollingIntervalMs,
+ (err: Error) => {
+ this.unsubscribe();
+ callback(err);
+ },
+ );
+ }
+ public unsubscribe(): void {
+ this._lastEvents = [];
+ if (!_.isUndefined(this._intervalIdIfExists)) {
+ intervalUtils.clearAsyncExcludingInterval(this._intervalIdIfExists);
+ delete this._intervalIdIfExists;
+ }
+ }
+ private async _pollForBlockchainEventsAsync(callback: EventWatcherCallback): Promise<void> {
+ const pendingEvents = await this._getEventsAsync();
+ if (_.isUndefined(pendingEvents)) {
+ // HACK: This should never happen, but happens frequently on CI due to a ganache bug
+ return;
+ }
+ if (pendingEvents.length === 0) {
+ // HACK: Sometimes when node rebuilds the pending block we get back the empty result.
+ // We don't want to emit a lot of removal events and bring them back after a couple of miliseconds,
+ // that's why we just ignore those cases.
+ return;
+ }
+ const removedEvents = _.differenceBy(this._lastEvents, pendingEvents, JSON.stringify);
+ const newEvents = _.differenceBy(pendingEvents, this._lastEvents, JSON.stringify);
+ await this._emitDifferencesAsync(removedEvents, LogEventState.Removed, callback);
+ await this._emitDifferencesAsync(newEvents, LogEventState.Added, callback);
+ this._lastEvents = pendingEvents;
+ }
+ private async _getEventsAsync(): Promise<Web3.LogEntry[]> {
+ const eventFilter = {
+ fromBlock: BlockParamLiteral.Pending,
+ toBlock: BlockParamLiteral.Pending,
+ };
+ const events = await this._web3Wrapper.getLogsAsync(eventFilter);
+ return events;
+ }
+ private async _emitDifferencesAsync(
+ logs: Web3.LogEntry[],
+ logEventState: LogEventState,
+ callback: EventWatcherCallback,
+ ): Promise<void> {
+ for (const log of logs) {
+ const logEvent = {
+ removed: logEventState === LogEventState.Removed,
+ ...log,
+ };
+ if (!_.isUndefined(this._intervalIdIfExists)) {
+ callback(null, logEvent);
+ }
+ }
+ }
}
diff --git a/packages/0x.js/src/order_watcher/expiration_watcher.ts b/packages/0x.js/src/order_watcher/expiration_watcher.ts
index 00b62162d..a08de94c0 100644
--- a/packages/0x.js/src/order_watcher/expiration_watcher.ts
+++ b/packages/0x.js/src/order_watcher/expiration_watcher.ts
@@ -13,63 +13,63 @@ const DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS = 50;
* It stores them in a min heap by expiration time and checks for expired ones every `orderExpirationCheckingIntervalMs`
*/
export class ExpirationWatcher {
- private _orderHashByExpirationRBTree: RBTree<string>;
- private _expiration: { [orderHash: string]: BigNumber } = {};
- private _orderExpirationCheckingIntervalMs: number;
- private _expirationMarginMs: number;
- private _orderExpirationCheckingIntervalIdIfExists?: NodeJS.Timer;
- constructor(expirationMarginIfExistsMs?: number, orderExpirationCheckingIntervalIfExistsMs?: number) {
- this._expirationMarginMs = expirationMarginIfExistsMs || DEFAULT_EXPIRATION_MARGIN_MS;
- this._orderExpirationCheckingIntervalMs =
- expirationMarginIfExistsMs || DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS;
- const scoreFunction = (orderHash: string) => this._expiration[orderHash].toNumber();
- const comparator = (lhs: string, rhs: string) => scoreFunction(lhs) - scoreFunction(rhs);
- this._orderHashByExpirationRBTree = new RBTree(comparator);
- }
- public subscribe(callback: (orderHash: string) => void): void {
- if (!_.isUndefined(this._orderExpirationCheckingIntervalIdIfExists)) {
- throw new Error(ZeroExError.SubscriptionAlreadyPresent);
- }
- this._orderExpirationCheckingIntervalIdIfExists = intervalUtils.setInterval(
- this._pruneExpiredOrders.bind(this, callback),
- this._orderExpirationCheckingIntervalMs,
- _.noop, // _pruneExpiredOrders never throws
- );
- }
- public unsubscribe(): void {
- if (_.isUndefined(this._orderExpirationCheckingIntervalIdIfExists)) {
- throw new Error(ZeroExError.SubscriptionNotFound);
- }
- intervalUtils.clearInterval(this._orderExpirationCheckingIntervalIdIfExists);
- delete this._orderExpirationCheckingIntervalIdIfExists;
- }
- public addOrder(orderHash: string, expirationUnixTimestampMs: BigNumber): void {
- this._expiration[orderHash] = expirationUnixTimestampMs;
- this._orderHashByExpirationRBTree.insert(orderHash);
- }
- public removeOrder(orderHash: string): void {
- this._orderHashByExpirationRBTree.remove(orderHash);
- delete this._expiration[orderHash];
- }
- private _pruneExpiredOrders(callback: (orderHash: string) => void): void {
- const currentUnixTimestampMs = utils.getCurrentUnixTimestampMs();
- while (true) {
- const hasTrakedOrders = this._orderHashByExpirationRBTree.size === 0;
- if (hasTrakedOrders) {
- break;
- }
- const nextOrderHashToExpire = this._orderHashByExpirationRBTree.min();
- const hasNoExpiredOrders = this._expiration[nextOrderHashToExpire].greaterThan(
- currentUnixTimestampMs.plus(this._expirationMarginMs),
- );
- const isSubscriptionActive = _.isUndefined(this._orderExpirationCheckingIntervalIdIfExists);
- if (hasNoExpiredOrders || isSubscriptionActive) {
- break;
- }
- const orderHash = this._orderHashByExpirationRBTree.min();
- this._orderHashByExpirationRBTree.remove(orderHash);
- delete this._expiration[orderHash];
- callback(orderHash);
- }
- }
+ private _orderHashByExpirationRBTree: RBTree<string>;
+ private _expiration: { [orderHash: string]: BigNumber } = {};
+ private _orderExpirationCheckingIntervalMs: number;
+ private _expirationMarginMs: number;
+ private _orderExpirationCheckingIntervalIdIfExists?: NodeJS.Timer;
+ constructor(expirationMarginIfExistsMs?: number, orderExpirationCheckingIntervalIfExistsMs?: number) {
+ this._expirationMarginMs = expirationMarginIfExistsMs || DEFAULT_EXPIRATION_MARGIN_MS;
+ this._orderExpirationCheckingIntervalMs =
+ expirationMarginIfExistsMs || DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS;
+ const scoreFunction = (orderHash: string) => this._expiration[orderHash].toNumber();
+ const comparator = (lhs: string, rhs: string) => scoreFunction(lhs) - scoreFunction(rhs);
+ this._orderHashByExpirationRBTree = new RBTree(comparator);
+ }
+ public subscribe(callback: (orderHash: string) => void): void {
+ if (!_.isUndefined(this._orderExpirationCheckingIntervalIdIfExists)) {
+ throw new Error(ZeroExError.SubscriptionAlreadyPresent);
+ }
+ this._orderExpirationCheckingIntervalIdIfExists = intervalUtils.setInterval(
+ this._pruneExpiredOrders.bind(this, callback),
+ this._orderExpirationCheckingIntervalMs,
+ _.noop, // _pruneExpiredOrders never throws
+ );
+ }
+ public unsubscribe(): void {
+ if (_.isUndefined(this._orderExpirationCheckingIntervalIdIfExists)) {
+ throw new Error(ZeroExError.SubscriptionNotFound);
+ }
+ intervalUtils.clearInterval(this._orderExpirationCheckingIntervalIdIfExists);
+ delete this._orderExpirationCheckingIntervalIdIfExists;
+ }
+ public addOrder(orderHash: string, expirationUnixTimestampMs: BigNumber): void {
+ this._expiration[orderHash] = expirationUnixTimestampMs;
+ this._orderHashByExpirationRBTree.insert(orderHash);
+ }
+ public removeOrder(orderHash: string): void {
+ this._orderHashByExpirationRBTree.remove(orderHash);
+ delete this._expiration[orderHash];
+ }
+ private _pruneExpiredOrders(callback: (orderHash: string) => void): void {
+ const currentUnixTimestampMs = utils.getCurrentUnixTimestampMs();
+ while (true) {
+ const hasTrakedOrders = this._orderHashByExpirationRBTree.size === 0;
+ if (hasTrakedOrders) {
+ break;
+ }
+ const nextOrderHashToExpire = this._orderHashByExpirationRBTree.min();
+ const hasNoExpiredOrders = this._expiration[nextOrderHashToExpire].greaterThan(
+ currentUnixTimestampMs.plus(this._expirationMarginMs),
+ );
+ const isSubscriptionActive = _.isUndefined(this._orderExpirationCheckingIntervalIdIfExists);
+ if (hasNoExpiredOrders || isSubscriptionActive) {
+ break;
+ }
+ const orderHash = this._orderHashByExpirationRBTree.min();
+ this._orderHashByExpirationRBTree.remove(orderHash);
+ delete this._expiration[orderHash];
+ callback(orderHash);
+ }
+ }
}
diff --git a/packages/0x.js/src/order_watcher/order_state_watcher.ts b/packages/0x.js/src/order_watcher/order_state_watcher.ts
index 12ac60960..2c5da6b57 100644
--- a/packages/0x.js/src/order_watcher/order_state_watcher.ts
+++ b/packages/0x.js/src/order_watcher/order_state_watcher.ts
@@ -9,25 +9,25 @@ import { TokenWrapper } from '../contract_wrappers/token_wrapper';
import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store';
import { OrderFilledCancelledLazyStore } from '../stores/order_filled_cancelled_lazy_store';
import {
- ApprovalContractEventArgs,
- BlockParamLiteral,
- ContractEventArgs,
- DepositContractEventArgs,
- EtherTokenEvents,
- ExchangeContractErrs,
- ExchangeEvents,
- LogCancelContractEventArgs,
- LogEvent,
- LogFillContractEventArgs,
- LogWithDecodedArgs,
- OnOrderStateChangeCallback,
- OrderState,
- OrderStateWatcherConfig,
- SignedOrder,
- TokenEvents,
- TransferContractEventArgs,
- WithdrawalContractEventArgs,
- ZeroExError,
+ ApprovalContractEventArgs,
+ BlockParamLiteral,
+ ContractEventArgs,
+ DepositContractEventArgs,
+ EtherTokenEvents,
+ ExchangeContractErrs,
+ ExchangeEvents,
+ LogCancelContractEventArgs,
+ LogEvent,
+ LogFillContractEventArgs,
+ LogWithDecodedArgs,
+ OnOrderStateChangeCallback,
+ OrderState,
+ OrderStateWatcherConfig,
+ SignedOrder,
+ TokenEvents,
+ TransferContractEventArgs,
+ WithdrawalContractEventArgs,
+ ZeroExError,
} from '../types';
import { AbiDecoder } from '../utils/abi_decoder';
import { assert } from '../utils/assert';
@@ -38,17 +38,17 @@ import { EventWatcher } from './event_watcher';
import { ExpirationWatcher } from './expiration_watcher';
interface DependentOrderHashes {
- [makerAddress: string]: {
- [makerToken: string]: Set<string>;
- };
+ [makerAddress: string]: {
+ [makerToken: string]: Set<string>;
+ };
}
interface OrderByOrderHash {
- [orderHash: string]: SignedOrder;
+ [orderHash: string]: SignedOrder;
}
interface OrderStateByOrderHash {
- [orderHash: string]: OrderState;
+ [orderHash: string]: OrderState;
}
const DEFAULT_CLEANUP_JOB_INTERVAL_MS = 1000 * 60 * 60; // 1h
@@ -60,319 +60,319 @@ const DEFAULT_CLEANUP_JOB_INTERVAL_MS = 1000 * 60 * 60; // 1h
* the order should be deemed invalid.
*/
export class OrderStateWatcher {
- private _orderStateByOrderHashCache: OrderStateByOrderHash = {};
- private _orderByOrderHash: OrderByOrderHash = {};
- private _dependentOrderHashes: DependentOrderHashes = {};
- private _callbackIfExists?: OnOrderStateChangeCallback;
- private _eventWatcher: EventWatcher;
- private _web3Wrapper: Web3Wrapper;
- private _abiDecoder: AbiDecoder;
- private _expirationWatcher: ExpirationWatcher;
- private _orderStateUtils: OrderStateUtils;
- private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
- private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
- private _cleanupJobInterval: number;
- private _cleanupJobIntervalIdIfExists?: NodeJS.Timer;
- constructor(
- web3Wrapper: Web3Wrapper,
- abiDecoder: AbiDecoder,
- token: TokenWrapper,
- exchange: ExchangeWrapper,
- config?: OrderStateWatcherConfig,
- ) {
- this._abiDecoder = abiDecoder;
- this._web3Wrapper = web3Wrapper;
- const pollingIntervalIfExistsMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs;
- this._eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalIfExistsMs);
- this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- token,
- BlockParamLiteral.Pending,
- );
- this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange);
- this._orderStateUtils = new OrderStateUtils(
- this._balanceAndProxyAllowanceLazyStore,
- this._orderFilledCancelledLazyStore,
- );
- const orderExpirationCheckingIntervalMsIfExists = _.isUndefined(config)
- ? undefined
- : config.orderExpirationCheckingIntervalMs;
- const expirationMarginIfExistsMs = _.isUndefined(config) ? undefined : config.expirationMarginMs;
- this._expirationWatcher = new ExpirationWatcher(
- expirationMarginIfExistsMs,
- orderExpirationCheckingIntervalMsIfExists,
- );
- this._cleanupJobInterval =
- _.isUndefined(config) || _.isUndefined(config.cleanupJobIntervalMs)
- ? DEFAULT_CLEANUP_JOB_INTERVAL_MS
- : config.cleanupJobIntervalMs;
- }
- /**
- * Add an order to the orderStateWatcher. Before the order is added, it's
- * signature is verified.
- * @param signedOrder The order you wish to start watching.
- */
- public addOrder(signedOrder: SignedOrder): void {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- assert.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker);
- this._orderByOrderHash[orderHash] = signedOrder;
- this._addToDependentOrderHashes(signedOrder, orderHash);
- const expirationUnixTimestampMs = signedOrder.expirationUnixTimestampSec.times(1000);
- this._expirationWatcher.addOrder(orderHash, expirationUnixTimestampMs);
- }
- /**
- * Removes an order from the orderStateWatcher
- * @param orderHash The orderHash of the order you wish to stop watching.
- */
- public removeOrder(orderHash: string): void {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const signedOrder = this._orderByOrderHash[orderHash];
- if (_.isUndefined(signedOrder)) {
- return; // noop
- }
- delete this._orderByOrderHash[orderHash];
- delete this._orderStateByOrderHashCache[orderHash];
- const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
- const zrxTokenAddress = exchange.getZRXTokenAddress();
- this._removeFromDependentOrderHashes(signedOrder.maker, zrxTokenAddress, orderHash);
- this._removeFromDependentOrderHashes(signedOrder.maker, signedOrder.makerTokenAddress, orderHash);
- this._expirationWatcher.removeOrder(orderHash);
- }
- /**
- * Starts an orderStateWatcher subscription. The callback will be called every time a watched order's
- * backing blockchain state has changed. This is a call-to-action for the caller to re-validate the order.
- * @param callback Receives the orderHash of the order that should be re-validated, together
- * with all the order-relevant blockchain state needed to re-validate the order.
- */
- public subscribe(callback: OnOrderStateChangeCallback): void {
- assert.isFunction('callback', callback);
- if (!_.isUndefined(this._callbackIfExists)) {
- throw new Error(ZeroExError.SubscriptionAlreadyPresent);
- }
- this._callbackIfExists = callback;
- this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this));
- this._expirationWatcher.subscribe(this._onOrderExpired.bind(this));
- this._cleanupJobIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
- this._cleanupAsync.bind(this),
- this._cleanupJobInterval,
- (err: Error) => {
- this.unsubscribe();
- callback(err);
- },
- );
- }
- /**
- * Ends an orderStateWatcher subscription.
- */
- public unsubscribe(): void {
- if (_.isUndefined(this._callbackIfExists) || _.isUndefined(this._cleanupJobIntervalIdIfExists)) {
- throw new Error(ZeroExError.SubscriptionNotFound);
- }
- this._balanceAndProxyAllowanceLazyStore.deleteAll();
- this._orderFilledCancelledLazyStore.deleteAll();
- delete this._callbackIfExists;
- this._eventWatcher.unsubscribe();
- this._expirationWatcher.unsubscribe();
- intervalUtils.clearAsyncExcludingInterval(this._cleanupJobIntervalIdIfExists);
- }
- private async _cleanupAsync(): Promise<void> {
- for (const orderHash of _.keys(this._orderByOrderHash)) {
- this._cleanupOrderRelatedState(orderHash);
- await this._emitRevalidateOrdersAsync([orderHash]);
- }
- }
- private _cleanupOrderRelatedState(orderHash: string): void {
- const signedOrder = this._orderByOrderHash[orderHash];
+ private _orderStateByOrderHashCache: OrderStateByOrderHash = {};
+ private _orderByOrderHash: OrderByOrderHash = {};
+ private _dependentOrderHashes: DependentOrderHashes = {};
+ private _callbackIfExists?: OnOrderStateChangeCallback;
+ private _eventWatcher: EventWatcher;
+ private _web3Wrapper: Web3Wrapper;
+ private _abiDecoder: AbiDecoder;
+ private _expirationWatcher: ExpirationWatcher;
+ private _orderStateUtils: OrderStateUtils;
+ private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
+ private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
+ private _cleanupJobInterval: number;
+ private _cleanupJobIntervalIdIfExists?: NodeJS.Timer;
+ constructor(
+ web3Wrapper: Web3Wrapper,
+ abiDecoder: AbiDecoder,
+ token: TokenWrapper,
+ exchange: ExchangeWrapper,
+ config?: OrderStateWatcherConfig,
+ ) {
+ this._abiDecoder = abiDecoder;
+ this._web3Wrapper = web3Wrapper;
+ const pollingIntervalIfExistsMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs;
+ this._eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalIfExistsMs);
+ this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
+ token,
+ BlockParamLiteral.Pending,
+ );
+ this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange);
+ this._orderStateUtils = new OrderStateUtils(
+ this._balanceAndProxyAllowanceLazyStore,
+ this._orderFilledCancelledLazyStore,
+ );
+ const orderExpirationCheckingIntervalMsIfExists = _.isUndefined(config)
+ ? undefined
+ : config.orderExpirationCheckingIntervalMs;
+ const expirationMarginIfExistsMs = _.isUndefined(config) ? undefined : config.expirationMarginMs;
+ this._expirationWatcher = new ExpirationWatcher(
+ expirationMarginIfExistsMs,
+ orderExpirationCheckingIntervalMsIfExists,
+ );
+ this._cleanupJobInterval =
+ _.isUndefined(config) || _.isUndefined(config.cleanupJobIntervalMs)
+ ? DEFAULT_CLEANUP_JOB_INTERVAL_MS
+ : config.cleanupJobIntervalMs;
+ }
+ /**
+ * Add an order to the orderStateWatcher. Before the order is added, it's
+ * signature is verified.
+ * @param signedOrder The order you wish to start watching.
+ */
+ public addOrder(signedOrder: SignedOrder): void {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ assert.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker);
+ this._orderByOrderHash[orderHash] = signedOrder;
+ this._addToDependentOrderHashes(signedOrder, orderHash);
+ const expirationUnixTimestampMs = signedOrder.expirationUnixTimestampSec.times(1000);
+ this._expirationWatcher.addOrder(orderHash, expirationUnixTimestampMs);
+ }
+ /**
+ * Removes an order from the orderStateWatcher
+ * @param orderHash The orderHash of the order you wish to stop watching.
+ */
+ public removeOrder(orderHash: string): void {
+ assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
+ const signedOrder = this._orderByOrderHash[orderHash];
+ if (_.isUndefined(signedOrder)) {
+ return; // noop
+ }
+ delete this._orderByOrderHash[orderHash];
+ delete this._orderStateByOrderHashCache[orderHash];
+ const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
+ const zrxTokenAddress = exchange.getZRXTokenAddress();
+ this._removeFromDependentOrderHashes(signedOrder.maker, zrxTokenAddress, orderHash);
+ this._removeFromDependentOrderHashes(signedOrder.maker, signedOrder.makerTokenAddress, orderHash);
+ this._expirationWatcher.removeOrder(orderHash);
+ }
+ /**
+ * Starts an orderStateWatcher subscription. The callback will be called every time a watched order's
+ * backing blockchain state has changed. This is a call-to-action for the caller to re-validate the order.
+ * @param callback Receives the orderHash of the order that should be re-validated, together
+ * with all the order-relevant blockchain state needed to re-validate the order.
+ */
+ public subscribe(callback: OnOrderStateChangeCallback): void {
+ assert.isFunction('callback', callback);
+ if (!_.isUndefined(this._callbackIfExists)) {
+ throw new Error(ZeroExError.SubscriptionAlreadyPresent);
+ }
+ this._callbackIfExists = callback;
+ this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this));
+ this._expirationWatcher.subscribe(this._onOrderExpired.bind(this));
+ this._cleanupJobIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
+ this._cleanupAsync.bind(this),
+ this._cleanupJobInterval,
+ (err: Error) => {
+ this.unsubscribe();
+ callback(err);
+ },
+ );
+ }
+ /**
+ * Ends an orderStateWatcher subscription.
+ */
+ public unsubscribe(): void {
+ if (_.isUndefined(this._callbackIfExists) || _.isUndefined(this._cleanupJobIntervalIdIfExists)) {
+ throw new Error(ZeroExError.SubscriptionNotFound);
+ }
+ this._balanceAndProxyAllowanceLazyStore.deleteAll();
+ this._orderFilledCancelledLazyStore.deleteAll();
+ delete this._callbackIfExists;
+ this._eventWatcher.unsubscribe();
+ this._expirationWatcher.unsubscribe();
+ intervalUtils.clearAsyncExcludingInterval(this._cleanupJobIntervalIdIfExists);
+ }
+ private async _cleanupAsync(): Promise<void> {
+ for (const orderHash of _.keys(this._orderByOrderHash)) {
+ this._cleanupOrderRelatedState(orderHash);
+ await this._emitRevalidateOrdersAsync([orderHash]);
+ }
+ }
+ private _cleanupOrderRelatedState(orderHash: string): void {
+ const signedOrder = this._orderByOrderHash[orderHash];
- this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(orderHash);
- this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(orderHash);
+ this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(orderHash);
+ this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(orderHash);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.makerTokenAddress, signedOrder.maker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(signedOrder.makerTokenAddress, signedOrder.maker);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.takerTokenAddress, signedOrder.taker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(signedOrder.takerTokenAddress, signedOrder.taker);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.makerTokenAddress, signedOrder.maker);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(signedOrder.makerTokenAddress, signedOrder.maker);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.takerTokenAddress, signedOrder.taker);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(signedOrder.takerTokenAddress, signedOrder.taker);
- const zrxTokenAddress = this._getZRXTokenAddress();
- if (!signedOrder.makerFee.isZero()) {
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxTokenAddress, signedOrder.maker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxTokenAddress, signedOrder.maker);
- }
- if (!signedOrder.takerFee.isZero()) {
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxTokenAddress, signedOrder.taker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxTokenAddress, signedOrder.taker);
- }
- }
- private _onOrderExpired(orderHash: string): void {
- const orderState: OrderState = {
- isValid: false,
- orderHash,
- error: ExchangeContractErrs.OrderFillExpired,
- };
- if (!_.isUndefined(this._orderByOrderHash[orderHash])) {
- this.removeOrder(orderHash);
- if (!_.isUndefined(this._callbackIfExists)) {
- this._callbackIfExists(null, orderState);
- }
- }
- }
- private async _onEventWatcherCallbackAsync(err: Error | null, logIfExists?: LogEvent): Promise<void> {
- if (!_.isNull(err)) {
- if (!_.isUndefined(this._callbackIfExists)) {
- this._callbackIfExists(err);
- this.unsubscribe();
- }
- return;
- }
- const log = logIfExists as LogEvent; // At this moment we are sure that no error occured and log is defined.
- const maybeDecodedLog = this._abiDecoder.tryToDecodeLogOrNoop(log);
- const isLogDecoded = !_.isUndefined((maybeDecodedLog as LogWithDecodedArgs<any>).event);
- if (!isLogDecoded) {
- return; // noop
- }
- const decodedLog = maybeDecodedLog as LogWithDecodedArgs<ContractEventArgs>;
- let makerToken: string;
- let makerAddress: string;
- switch (decodedLog.event) {
- case TokenEvents.Approval: {
- // Invalidate cache
- const args = decodedLog.args as ApprovalContractEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner);
- // Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._owner;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
- await this._emitRevalidateOrdersAsync(orderHashes);
- }
- break;
- }
- case TokenEvents.Transfer: {
- // Invalidate cache
- const args = decodedLog.args as TransferContractEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._from);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._to);
- // Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._from;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
- await this._emitRevalidateOrdersAsync(orderHashes);
- }
- break;
- }
- case EtherTokenEvents.Deposit: {
- // Invalidate cache
- const args = decodedLog.args as DepositContractEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
- // Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._owner;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
- await this._emitRevalidateOrdersAsync(orderHashes);
- }
- break;
- }
- case EtherTokenEvents.Withdrawal: {
- // Invalidate cache
- const args = decodedLog.args as WithdrawalContractEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
- // Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._owner;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
- await this._emitRevalidateOrdersAsync(orderHashes);
- }
- break;
- }
- case ExchangeEvents.LogFill: {
- // Invalidate cache
- const args = decodedLog.args as LogFillContractEventArgs;
- this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash);
- // Revalidate orders
- const orderHash = args.orderHash;
- const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
- if (isOrderWatched) {
- await this._emitRevalidateOrdersAsync([orderHash]);
- }
- break;
- }
- case ExchangeEvents.LogCancel: {
- // Invalidate cache
- const args = decodedLog.args as LogCancelContractEventArgs;
- this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash);
- // Revalidate orders
- const orderHash = args.orderHash;
- const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
- if (isOrderWatched) {
- await this._emitRevalidateOrdersAsync([orderHash]);
- }
- break;
- }
- case ExchangeEvents.LogError:
- return; // noop
+ const zrxTokenAddress = this._getZRXTokenAddress();
+ if (!signedOrder.makerFee.isZero()) {
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxTokenAddress, signedOrder.maker);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxTokenAddress, signedOrder.maker);
+ }
+ if (!signedOrder.takerFee.isZero()) {
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxTokenAddress, signedOrder.taker);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxTokenAddress, signedOrder.taker);
+ }
+ }
+ private _onOrderExpired(orderHash: string): void {
+ const orderState: OrderState = {
+ isValid: false,
+ orderHash,
+ error: ExchangeContractErrs.OrderFillExpired,
+ };
+ if (!_.isUndefined(this._orderByOrderHash[orderHash])) {
+ this.removeOrder(orderHash);
+ if (!_.isUndefined(this._callbackIfExists)) {
+ this._callbackIfExists(null, orderState);
+ }
+ }
+ }
+ private async _onEventWatcherCallbackAsync(err: Error | null, logIfExists?: LogEvent): Promise<void> {
+ if (!_.isNull(err)) {
+ if (!_.isUndefined(this._callbackIfExists)) {
+ this._callbackIfExists(err);
+ this.unsubscribe();
+ }
+ return;
+ }
+ const log = logIfExists as LogEvent; // At this moment we are sure that no error occured and log is defined.
+ const maybeDecodedLog = this._abiDecoder.tryToDecodeLogOrNoop(log);
+ const isLogDecoded = !_.isUndefined((maybeDecodedLog as LogWithDecodedArgs<any>).event);
+ if (!isLogDecoded) {
+ return; // noop
+ }
+ const decodedLog = maybeDecodedLog as LogWithDecodedArgs<ContractEventArgs>;
+ let makerToken: string;
+ let makerAddress: string;
+ switch (decodedLog.event) {
+ case TokenEvents.Approval: {
+ // Invalidate cache
+ const args = decodedLog.args as ApprovalContractEventArgs;
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner);
+ // Revalidate orders
+ makerToken = decodedLog.address;
+ makerAddress = args._owner;
+ if (
+ !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
+ !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
+ ) {
+ const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ }
+ break;
+ }
+ case TokenEvents.Transfer: {
+ // Invalidate cache
+ const args = decodedLog.args as TransferContractEventArgs;
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._from);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._to);
+ // Revalidate orders
+ makerToken = decodedLog.address;
+ makerAddress = args._from;
+ if (
+ !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
+ !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
+ ) {
+ const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ }
+ break;
+ }
+ case EtherTokenEvents.Deposit: {
+ // Invalidate cache
+ const args = decodedLog.args as DepositContractEventArgs;
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
+ // Revalidate orders
+ makerToken = decodedLog.address;
+ makerAddress = args._owner;
+ if (
+ !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
+ !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
+ ) {
+ const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ }
+ break;
+ }
+ case EtherTokenEvents.Withdrawal: {
+ // Invalidate cache
+ const args = decodedLog.args as WithdrawalContractEventArgs;
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
+ // Revalidate orders
+ makerToken = decodedLog.address;
+ makerAddress = args._owner;
+ if (
+ !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
+ !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
+ ) {
+ const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ }
+ break;
+ }
+ case ExchangeEvents.LogFill: {
+ // Invalidate cache
+ const args = decodedLog.args as LogFillContractEventArgs;
+ this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash);
+ // Revalidate orders
+ const orderHash = args.orderHash;
+ const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
+ if (isOrderWatched) {
+ await this._emitRevalidateOrdersAsync([orderHash]);
+ }
+ break;
+ }
+ case ExchangeEvents.LogCancel: {
+ // Invalidate cache
+ const args = decodedLog.args as LogCancelContractEventArgs;
+ this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash);
+ // Revalidate orders
+ const orderHash = args.orderHash;
+ const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
+ if (isOrderWatched) {
+ await this._emitRevalidateOrdersAsync([orderHash]);
+ }
+ break;
+ }
+ case ExchangeEvents.LogError:
+ return; // noop
- default:
- throw utils.spawnSwitchErr('decodedLog.event', decodedLog.event);
- }
- }
- private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise<void> {
- for (const orderHash of orderHashes) {
- const signedOrder = this._orderByOrderHash[orderHash];
- // Most of these calls will never reach the network because the data is fetched from stores
- // and only updated when cache is invalidated
- const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder);
- if (_.isUndefined(this._callbackIfExists)) {
- break; // Unsubscribe was called
- }
- if (_.isEqual(orderState, this._orderStateByOrderHashCache[orderHash])) {
- // Actual order state didn't change
- continue;
- } else {
- this._orderStateByOrderHashCache[orderHash] = orderState;
- }
- this._callbackIfExists(null, orderState);
- }
- }
- private _addToDependentOrderHashes(signedOrder: SignedOrder, orderHash: string): void {
- if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker])) {
- this._dependentOrderHashes[signedOrder.maker] = {};
- }
- if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress])) {
- this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress] = new Set();
- }
- this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress].add(orderHash);
- const zrxTokenAddress = this._getZRXTokenAddress();
- if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress])) {
- this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress] = new Set();
- }
- this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress].add(orderHash);
- }
- private _removeFromDependentOrderHashes(makerAddress: string, tokenAddress: string, orderHash: string) {
- this._dependentOrderHashes[makerAddress][tokenAddress].delete(orderHash);
- if (this._dependentOrderHashes[makerAddress][tokenAddress].size === 0) {
- delete this._dependentOrderHashes[makerAddress][tokenAddress];
- }
- if (_.isEmpty(this._dependentOrderHashes[makerAddress])) {
- delete this._dependentOrderHashes[makerAddress];
- }
- }
- private _getZRXTokenAddress(): string {
- const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
- const zrxTokenAddress = exchange.getZRXTokenAddress();
- return zrxTokenAddress;
- }
+ default:
+ throw utils.spawnSwitchErr('decodedLog.event', decodedLog.event);
+ }
+ }
+ private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise<void> {
+ for (const orderHash of orderHashes) {
+ const signedOrder = this._orderByOrderHash[orderHash];
+ // Most of these calls will never reach the network because the data is fetched from stores
+ // and only updated when cache is invalidated
+ const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder);
+ if (_.isUndefined(this._callbackIfExists)) {
+ break; // Unsubscribe was called
+ }
+ if (_.isEqual(orderState, this._orderStateByOrderHashCache[orderHash])) {
+ // Actual order state didn't change
+ continue;
+ } else {
+ this._orderStateByOrderHashCache[orderHash] = orderState;
+ }
+ this._callbackIfExists(null, orderState);
+ }
+ }
+ private _addToDependentOrderHashes(signedOrder: SignedOrder, orderHash: string): void {
+ if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker])) {
+ this._dependentOrderHashes[signedOrder.maker] = {};
+ }
+ if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress])) {
+ this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress] = new Set();
+ }
+ this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress].add(orderHash);
+ const zrxTokenAddress = this._getZRXTokenAddress();
+ if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress])) {
+ this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress] = new Set();
+ }
+ this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress].add(orderHash);
+ }
+ private _removeFromDependentOrderHashes(makerAddress: string, tokenAddress: string, orderHash: string) {
+ this._dependentOrderHashes[makerAddress][tokenAddress].delete(orderHash);
+ if (this._dependentOrderHashes[makerAddress][tokenAddress].size === 0) {
+ delete this._dependentOrderHashes[makerAddress][tokenAddress];
+ }
+ if (_.isEmpty(this._dependentOrderHashes[makerAddress])) {
+ delete this._dependentOrderHashes[makerAddress];
+ }
+ }
+ private _getZRXTokenAddress(): string {
+ const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
+ const zrxTokenAddress = exchange.getZRXTokenAddress();
+ return zrxTokenAddress;
+ }
}
diff --git a/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts b/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts
index 20b09d606..e010092af 100644
--- a/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts
+++ b/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts
@@ -3,94 +3,94 @@ import { BigNumber } from '@0xproject/utils';
import { SignedOrder } from '../types';
export class RemainingFillableCalculator {
- private _signedOrder: SignedOrder;
- private _isMakerTokenZRX: boolean;
- // Transferrable Amount is the minimum of Approval and Balance
- private _transferrableMakerTokenAmount: BigNumber;
- private _transferrableMakerFeeTokenAmount: BigNumber;
- private _remainingMakerTokenAmount: BigNumber;
- private _remainingMakerFeeAmount: BigNumber;
- constructor(
- signedOrder: SignedOrder,
- isMakerTokenZRX: boolean,
- transferrableMakerTokenAmount: BigNumber,
- transferrableMakerFeeTokenAmount: BigNumber,
- remainingMakerTokenAmount: BigNumber,
- ) {
- this._signedOrder = signedOrder;
- this._isMakerTokenZRX = isMakerTokenZRX;
- this._transferrableMakerTokenAmount = transferrableMakerTokenAmount;
- this._transferrableMakerFeeTokenAmount = transferrableMakerFeeTokenAmount;
- this._remainingMakerTokenAmount = remainingMakerTokenAmount;
- this._remainingMakerFeeAmount = remainingMakerTokenAmount
- .times(signedOrder.makerFee)
- .dividedToIntegerBy(signedOrder.makerTokenAmount);
- }
- public computeRemainingMakerFillable(): BigNumber {
- if (this._hasSufficientFundsForFeeAndTransferAmount()) {
- return this._remainingMakerTokenAmount;
- }
- if (this._signedOrder.makerFee.isZero()) {
- return BigNumber.min(this._remainingMakerTokenAmount, this._transferrableMakerTokenAmount);
- }
- return this._calculatePartiallyFillableMakerTokenAmount();
- }
- public computeRemainingTakerFillable(): BigNumber {
- return this.computeRemainingMakerFillable()
- .times(this._signedOrder.takerTokenAmount)
- .dividedToIntegerBy(this._signedOrder.makerTokenAmount);
- }
- private _hasSufficientFundsForFeeAndTransferAmount(): boolean {
- if (this._isMakerTokenZRX) {
- const totalZRXTransferAmountRequired = this._remainingMakerTokenAmount.plus(this._remainingMakerFeeAmount);
- const hasSufficientFunds = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
- totalZRXTransferAmountRequired,
- );
- return hasSufficientFunds;
- } else {
- const hasSufficientFundsForTransferAmount = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
- this._remainingMakerTokenAmount,
- );
- const hasSufficientFundsForFeeAmount = this._transferrableMakerFeeTokenAmount.greaterThanOrEqualTo(
- this._remainingMakerFeeAmount,
- );
- const hasSufficientFunds = hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount;
- return hasSufficientFunds;
- }
- }
- private _calculatePartiallyFillableMakerTokenAmount(): BigNumber {
- // Given an order for 200 wei for 2 ZRXwei fee, find 100 wei for 1 ZRXwei. Order ratio is then 100:1
- const orderToFeeRatio = this._signedOrder.makerTokenAmount.dividedBy(this._signedOrder.makerFee);
- // The number of times the maker can fill the order, if each fill only required the transfer of a single
- // baseUnit of fee tokens.
- // 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,
- this._remainingMakerFeeAmount,
- );
- // 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.
- let fillableTimesInMakerTokenUnits = this._transferrableMakerTokenAmount.dividedBy(orderToFeeRatio);
- if (this._isMakerTokenZRX) {
- // If ZRX is the maker token, the Fee and the Maker amount need to be removed from the same pool;
- // 200 ZRXwei for 2ZRXwei fee can only be filled once (need 202 ZRXwei)
- const totalZRXTokenPooled = this._transferrableMakerTokenAmount;
- // 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
- fillableTimesInMakerTokenUnits = totalZRXTokenPooled.dividedBy(orderToFeeRatio.plus(new BigNumber(1)));
- }
- // 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.
- const partiallyFillableMakerTokenAmount = fillableTimesInMakerTokenUnits
- .times(this._signedOrder.makerTokenAmount)
- .dividedToIntegerBy(this._signedOrder.makerFee);
- const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits
- .times(this._signedOrder.makerTokenAmount)
- .dividedToIntegerBy(this._signedOrder.makerFee);
- const partiallyFillableAmount = BigNumber.min(
- partiallyFillableMakerTokenAmount,
- partiallyFillableFeeTokenAmount,
- );
- return partiallyFillableAmount;
- }
+ private _signedOrder: SignedOrder;
+ private _isMakerTokenZRX: boolean;
+ // Transferrable Amount is the minimum of Approval and Balance
+ private _transferrableMakerTokenAmount: BigNumber;
+ private _transferrableMakerFeeTokenAmount: BigNumber;
+ private _remainingMakerTokenAmount: BigNumber;
+ private _remainingMakerFeeAmount: BigNumber;
+ constructor(
+ signedOrder: SignedOrder,
+ isMakerTokenZRX: boolean,
+ transferrableMakerTokenAmount: BigNumber,
+ transferrableMakerFeeTokenAmount: BigNumber,
+ remainingMakerTokenAmount: BigNumber,
+ ) {
+ this._signedOrder = signedOrder;
+ this._isMakerTokenZRX = isMakerTokenZRX;
+ this._transferrableMakerTokenAmount = transferrableMakerTokenAmount;
+ this._transferrableMakerFeeTokenAmount = transferrableMakerFeeTokenAmount;
+ this._remainingMakerTokenAmount = remainingMakerTokenAmount;
+ this._remainingMakerFeeAmount = remainingMakerTokenAmount
+ .times(signedOrder.makerFee)
+ .dividedToIntegerBy(signedOrder.makerTokenAmount);
+ }
+ public computeRemainingMakerFillable(): BigNumber {
+ if (this._hasSufficientFundsForFeeAndTransferAmount()) {
+ return this._remainingMakerTokenAmount;
+ }
+ if (this._signedOrder.makerFee.isZero()) {
+ return BigNumber.min(this._remainingMakerTokenAmount, this._transferrableMakerTokenAmount);
+ }
+ return this._calculatePartiallyFillableMakerTokenAmount();
+ }
+ public computeRemainingTakerFillable(): BigNumber {
+ return this.computeRemainingMakerFillable()
+ .times(this._signedOrder.takerTokenAmount)
+ .dividedToIntegerBy(this._signedOrder.makerTokenAmount);
+ }
+ private _hasSufficientFundsForFeeAndTransferAmount(): boolean {
+ if (this._isMakerTokenZRX) {
+ const totalZRXTransferAmountRequired = this._remainingMakerTokenAmount.plus(this._remainingMakerFeeAmount);
+ const hasSufficientFunds = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
+ totalZRXTransferAmountRequired,
+ );
+ return hasSufficientFunds;
+ } else {
+ const hasSufficientFundsForTransferAmount = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
+ this._remainingMakerTokenAmount,
+ );
+ const hasSufficientFundsForFeeAmount = this._transferrableMakerFeeTokenAmount.greaterThanOrEqualTo(
+ this._remainingMakerFeeAmount,
+ );
+ const hasSufficientFunds = hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount;
+ return hasSufficientFunds;
+ }
+ }
+ private _calculatePartiallyFillableMakerTokenAmount(): BigNumber {
+ // Given an order for 200 wei for 2 ZRXwei fee, find 100 wei for 1 ZRXwei. Order ratio is then 100:1
+ const orderToFeeRatio = this._signedOrder.makerTokenAmount.dividedBy(this._signedOrder.makerFee);
+ // The number of times the maker can fill the order, if each fill only required the transfer of a single
+ // baseUnit of fee tokens.
+ // 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,
+ this._remainingMakerFeeAmount,
+ );
+ // 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.
+ let fillableTimesInMakerTokenUnits = this._transferrableMakerTokenAmount.dividedBy(orderToFeeRatio);
+ if (this._isMakerTokenZRX) {
+ // If ZRX is the maker token, the Fee and the Maker amount need to be removed from the same pool;
+ // 200 ZRXwei for 2ZRXwei fee can only be filled once (need 202 ZRXwei)
+ const totalZRXTokenPooled = this._transferrableMakerTokenAmount;
+ // 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
+ fillableTimesInMakerTokenUnits = totalZRXTokenPooled.dividedBy(orderToFeeRatio.plus(new BigNumber(1)));
+ }
+ // 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.
+ const partiallyFillableMakerTokenAmount = fillableTimesInMakerTokenUnits
+ .times(this._signedOrder.makerTokenAmount)
+ .dividedToIntegerBy(this._signedOrder.makerFee);
+ const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits
+ .times(this._signedOrder.makerTokenAmount)
+ .dividedToIntegerBy(this._signedOrder.makerFee);
+ const partiallyFillableAmount = BigNumber.min(
+ partiallyFillableMakerTokenAmount,
+ partiallyFillableFeeTokenAmount,
+ );
+ return partiallyFillableAmount;
+ }
}
diff --git a/packages/0x.js/src/schemas/zero_ex_config_schema.ts b/packages/0x.js/src/schemas/zero_ex_config_schema.ts
index 546b1c2d0..657b24c58 100644
--- a/packages/0x.js/src/schemas/zero_ex_config_schema.ts
+++ b/packages/0x.js/src/schemas/zero_ex_config_schema.ts
@@ -1,27 +1,27 @@
export const zeroExConfigSchema = {
- id: '/ZeroExConfig',
- properties: {
- networkId: {
- type: 'number',
- minimum: 0,
- },
- gasPrice: { $ref: '/Number' },
- exchangeContractAddress: { $ref: '/Address' },
- tokenRegistryContractAddress: { $ref: '/Address' },
- orderWatcherConfig: {
- type: 'object',
- properties: {
- pollingIntervalMs: {
- type: 'number',
- minimum: 0,
- },
- numConfirmations: {
- type: 'number',
- minimum: 0,
- },
- },
- },
- },
- type: 'object',
- required: ['networkId'],
+ id: '/ZeroExConfig',
+ properties: {
+ networkId: {
+ type: 'number',
+ minimum: 0,
+ },
+ gasPrice: { $ref: '/Number' },
+ exchangeContractAddress: { $ref: '/Address' },
+ tokenRegistryContractAddress: { $ref: '/Address' },
+ orderWatcherConfig: {
+ type: 'object',
+ properties: {
+ pollingIntervalMs: {
+ type: 'number',
+ minimum: 0,
+ },
+ numConfirmations: {
+ type: 'number',
+ minimum: 0,
+ },
+ },
+ },
+ },
+ type: 'object',
+ required: ['networkId'],
};
diff --git a/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts b/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts
index 33feea105..379afa4bc 100644
--- a/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts
+++ b/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts
@@ -8,79 +8,79 @@ import { BlockParamLiteral } from '../types';
* Copy on read store for balances/proxyAllowances of tokens/accounts
*/
export class BalanceAndProxyAllowanceLazyStore {
- private _token: TokenWrapper;
- private _defaultBlock: BlockParamLiteral;
- private _balance: {
- [tokenAddress: string]: {
- [userAddress: string]: BigNumber;
- };
- };
- private _proxyAllowance: {
- [tokenAddress: string]: {
- [userAddress: string]: BigNumber;
- };
- };
- constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) {
- this._token = token;
- this._defaultBlock = defaultBlock;
- this._balance = {};
- this._proxyAllowance = {};
- }
- public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
- if (_.isUndefined(this._balance[tokenAddress]) || _.isUndefined(this._balance[tokenAddress][userAddress])) {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const balance = await this._token.getBalanceAsync(tokenAddress, userAddress, methodOpts);
- this.setBalance(tokenAddress, userAddress, balance);
- }
- const cachedBalance = this._balance[tokenAddress][userAddress];
- return cachedBalance;
- }
- public setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void {
- if (_.isUndefined(this._balance[tokenAddress])) {
- this._balance[tokenAddress] = {};
- }
- this._balance[tokenAddress][userAddress] = balance;
- }
- public deleteBalance(tokenAddress: string, userAddress: string): void {
- if (!_.isUndefined(this._balance[tokenAddress])) {
- delete this._balance[tokenAddress][userAddress];
- if (_.isEmpty(this._balance[tokenAddress])) {
- delete this._balance[tokenAddress];
- }
- }
- }
- public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
- if (
- _.isUndefined(this._proxyAllowance[tokenAddress]) ||
- _.isUndefined(this._proxyAllowance[tokenAddress][userAddress])
- ) {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const proxyAllowance = await this._token.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts);
- this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance);
- }
- const cachedProxyAllowance = this._proxyAllowance[tokenAddress][userAddress];
- return cachedProxyAllowance;
- }
- public setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void {
- if (_.isUndefined(this._proxyAllowance[tokenAddress])) {
- this._proxyAllowance[tokenAddress] = {};
- }
- this._proxyAllowance[tokenAddress][userAddress] = proxyAllowance;
- }
- public deleteProxyAllowance(tokenAddress: string, userAddress: string): void {
- if (!_.isUndefined(this._proxyAllowance[tokenAddress])) {
- delete this._proxyAllowance[tokenAddress][userAddress];
- if (_.isEmpty(this._proxyAllowance[tokenAddress])) {
- delete this._proxyAllowance[tokenAddress];
- }
- }
- }
- public deleteAll(): void {
- this._balance = {};
- this._proxyAllowance = {};
- }
+ private _token: TokenWrapper;
+ private _defaultBlock: BlockParamLiteral;
+ private _balance: {
+ [tokenAddress: string]: {
+ [userAddress: string]: BigNumber;
+ };
+ };
+ private _proxyAllowance: {
+ [tokenAddress: string]: {
+ [userAddress: string]: BigNumber;
+ };
+ };
+ constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) {
+ this._token = token;
+ this._defaultBlock = defaultBlock;
+ this._balance = {};
+ this._proxyAllowance = {};
+ }
+ public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
+ if (_.isUndefined(this._balance[tokenAddress]) || _.isUndefined(this._balance[tokenAddress][userAddress])) {
+ const methodOpts = {
+ defaultBlock: this._defaultBlock,
+ };
+ const balance = await this._token.getBalanceAsync(tokenAddress, userAddress, methodOpts);
+ this.setBalance(tokenAddress, userAddress, balance);
+ }
+ const cachedBalance = this._balance[tokenAddress][userAddress];
+ return cachedBalance;
+ }
+ public setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void {
+ if (_.isUndefined(this._balance[tokenAddress])) {
+ this._balance[tokenAddress] = {};
+ }
+ this._balance[tokenAddress][userAddress] = balance;
+ }
+ public deleteBalance(tokenAddress: string, userAddress: string): void {
+ if (!_.isUndefined(this._balance[tokenAddress])) {
+ delete this._balance[tokenAddress][userAddress];
+ if (_.isEmpty(this._balance[tokenAddress])) {
+ delete this._balance[tokenAddress];
+ }
+ }
+ }
+ public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
+ if (
+ _.isUndefined(this._proxyAllowance[tokenAddress]) ||
+ _.isUndefined(this._proxyAllowance[tokenAddress][userAddress])
+ ) {
+ const methodOpts = {
+ defaultBlock: this._defaultBlock,
+ };
+ const proxyAllowance = await this._token.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts);
+ this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance);
+ }
+ const cachedProxyAllowance = this._proxyAllowance[tokenAddress][userAddress];
+ return cachedProxyAllowance;
+ }
+ public setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void {
+ if (_.isUndefined(this._proxyAllowance[tokenAddress])) {
+ this._proxyAllowance[tokenAddress] = {};
+ }
+ this._proxyAllowance[tokenAddress][userAddress] = proxyAllowance;
+ }
+ public deleteProxyAllowance(tokenAddress: string, userAddress: string): void {
+ if (!_.isUndefined(this._proxyAllowance[tokenAddress])) {
+ delete this._proxyAllowance[tokenAddress][userAddress];
+ if (_.isEmpty(this._proxyAllowance[tokenAddress])) {
+ delete this._proxyAllowance[tokenAddress];
+ }
+ }
+ }
+ public deleteAll(): void {
+ this._balance = {};
+ this._proxyAllowance = {};
+ }
}
diff --git a/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts b/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts
index e22364c09..a99db0702 100644
--- a/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts
+++ b/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts
@@ -8,54 +8,54 @@ import { BlockParamLiteral } from '../types';
* Copy on read store for filled/cancelled taker amounts
*/
export class OrderFilledCancelledLazyStore {
- private _exchange: ExchangeWrapper;
- private _filledTakerAmount: {
- [orderHash: string]: BigNumber;
- };
- private _cancelledTakerAmount: {
- [orderHash: string]: BigNumber;
- };
- constructor(exchange: ExchangeWrapper) {
- this._exchange = exchange;
- this._filledTakerAmount = {};
- this._cancelledTakerAmount = {};
- }
- public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- if (_.isUndefined(this._filledTakerAmount[orderHash])) {
- const methodOpts = {
- defaultBlock: BlockParamLiteral.Pending,
- };
- const filledTakerAmount = await this._exchange.getFilledTakerAmountAsync(orderHash, methodOpts);
- this.setFilledTakerAmount(orderHash, filledTakerAmount);
- }
- const cachedFilled = this._filledTakerAmount[orderHash];
- return cachedFilled;
- }
- public setFilledTakerAmount(orderHash: string, filledTakerAmount: BigNumber): void {
- this._filledTakerAmount[orderHash] = filledTakerAmount;
- }
- public deleteFilledTakerAmount(orderHash: string): void {
- delete this._filledTakerAmount[orderHash];
- }
- public async getCancelledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- if (_.isUndefined(this._cancelledTakerAmount[orderHash])) {
- const methodOpts = {
- defaultBlock: BlockParamLiteral.Pending,
- };
- const cancelledTakerAmount = await this._exchange.getCancelledTakerAmountAsync(orderHash, methodOpts);
- this.setCancelledTakerAmount(orderHash, cancelledTakerAmount);
- }
- const cachedCancelled = this._cancelledTakerAmount[orderHash];
- return cachedCancelled;
- }
- public setCancelledTakerAmount(orderHash: string, cancelledTakerAmount: BigNumber): void {
- this._cancelledTakerAmount[orderHash] = cancelledTakerAmount;
- }
- public deleteCancelledTakerAmount(orderHash: string): void {
- delete this._cancelledTakerAmount[orderHash];
- }
- public deleteAll(): void {
- this._filledTakerAmount = {};
- this._cancelledTakerAmount = {};
- }
+ private _exchange: ExchangeWrapper;
+ private _filledTakerAmount: {
+ [orderHash: string]: BigNumber;
+ };
+ private _cancelledTakerAmount: {
+ [orderHash: string]: BigNumber;
+ };
+ constructor(exchange: ExchangeWrapper) {
+ this._exchange = exchange;
+ this._filledTakerAmount = {};
+ this._cancelledTakerAmount = {};
+ }
+ public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
+ if (_.isUndefined(this._filledTakerAmount[orderHash])) {
+ const methodOpts = {
+ defaultBlock: BlockParamLiteral.Pending,
+ };
+ const filledTakerAmount = await this._exchange.getFilledTakerAmountAsync(orderHash, methodOpts);
+ this.setFilledTakerAmount(orderHash, filledTakerAmount);
+ }
+ const cachedFilled = this._filledTakerAmount[orderHash];
+ return cachedFilled;
+ }
+ public setFilledTakerAmount(orderHash: string, filledTakerAmount: BigNumber): void {
+ this._filledTakerAmount[orderHash] = filledTakerAmount;
+ }
+ public deleteFilledTakerAmount(orderHash: string): void {
+ delete this._filledTakerAmount[orderHash];
+ }
+ public async getCancelledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
+ if (_.isUndefined(this._cancelledTakerAmount[orderHash])) {
+ const methodOpts = {
+ defaultBlock: BlockParamLiteral.Pending,
+ };
+ const cancelledTakerAmount = await this._exchange.getCancelledTakerAmountAsync(orderHash, methodOpts);
+ this.setCancelledTakerAmount(orderHash, cancelledTakerAmount);
+ }
+ const cachedCancelled = this._cancelledTakerAmount[orderHash];
+ return cachedCancelled;
+ }
+ public setCancelledTakerAmount(orderHash: string, cancelledTakerAmount: BigNumber): void {
+ this._cancelledTakerAmount[orderHash] = cancelledTakerAmount;
+ }
+ public deleteCancelledTakerAmount(orderHash: string): void {
+ delete this._cancelledTakerAmount[orderHash];
+ }
+ public deleteAll(): void {
+ this._filledTakerAmount = {};
+ this._cancelledTakerAmount = {};
+ }
}
diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts
index 3c93910e9..7eda4bea6 100644
--- a/packages/0x.js/src/types.ts
+++ b/packages/0x.js/src/types.ts
@@ -3,41 +3,41 @@ import { BigNumber } from '@0xproject/utils';
import * as Web3 from 'web3';
export enum ZeroExError {
- ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST',
- ZRXContractDoesNotExist = 'ZRX_CONTRACT_DOES_NOT_EXIST',
- EtherTokenContractDoesNotExist = 'ETHER_TOKEN_CONTRACT_DOES_NOT_EXIST',
- TokenTransferProxyContractDoesNotExist = 'TOKEN_TRANSFER_PROXY_CONTRACT_DOES_NOT_EXIST',
- TokenRegistryContractDoesNotExist = 'TOKEN_REGISTRY_CONTRACT_DOES_NOT_EXIST',
- TokenContractDoesNotExist = 'TOKEN_CONTRACT_DOES_NOT_EXIST',
- UnhandledError = 'UNHANDLED_ERROR',
- UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
- InvalidSignature = 'INVALID_SIGNATURE',
- ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
- InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
- InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
- InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
- InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
- InvalidJump = 'INVALID_JUMP',
- OutOfGas = 'OUT_OF_GAS',
- NoNetworkId = 'NO_NETWORK_ID',
- SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND',
- SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT',
- TransactionMiningTimeout = 'TRANSACTION_MINING_TIMEOUT',
+ ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST',
+ ZRXContractDoesNotExist = 'ZRX_CONTRACT_DOES_NOT_EXIST',
+ EtherTokenContractDoesNotExist = 'ETHER_TOKEN_CONTRACT_DOES_NOT_EXIST',
+ TokenTransferProxyContractDoesNotExist = 'TOKEN_TRANSFER_PROXY_CONTRACT_DOES_NOT_EXIST',
+ TokenRegistryContractDoesNotExist = 'TOKEN_REGISTRY_CONTRACT_DOES_NOT_EXIST',
+ TokenContractDoesNotExist = 'TOKEN_CONTRACT_DOES_NOT_EXIST',
+ UnhandledError = 'UNHANDLED_ERROR',
+ UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
+ InvalidSignature = 'INVALID_SIGNATURE',
+ ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
+ InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
+ InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
+ InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
+ InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
+ InvalidJump = 'INVALID_JUMP',
+ OutOfGas = 'OUT_OF_GAS',
+ NoNetworkId = 'NO_NETWORK_ID',
+ SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND',
+ SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT',
+ TransactionMiningTimeout = 'TRANSACTION_MINING_TIMEOUT',
}
export enum InternalZeroExError {
- NoAbiDecoder = 'NO_ABI_DECODER',
- ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY',
- WethNotInTokenRegistry = 'WETH_NOT_IN_TOKEN_REGISTRY',
+ NoAbiDecoder = 'NO_ABI_DECODER',
+ ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY',
+ WethNotInTokenRegistry = 'WETH_NOT_IN_TOKEN_REGISTRY',
}
/**
* Elliptic Curve signature
*/
export interface ECSignature {
- v: number;
- r: string;
- s: string;
+ v: number;
+ r: string;
+ s: string;
}
export type OrderAddresses = [string, string, string, string, string];
@@ -46,214 +46,214 @@ export type OrderValues = [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber
export type LogEvent = Web3.LogEntryEvent;
export interface DecodedLogEvent<ArgsType> {
- isRemoved: boolean;
- log: LogWithDecodedArgs<ArgsType>;
+ isRemoved: boolean;
+ log: LogWithDecodedArgs<ArgsType>;
}
export type EventCallback<ArgsType> = (err: null | Error, log?: DecodedLogEvent<ArgsType>) => void;
export type EventWatcherCallback = (err: null | Error, log?: LogEvent) => void;
export enum SolidityTypes {
- Address = 'address',
- Uint256 = 'uint256',
- Uint8 = 'uint8',
- Uint = 'uint',
+ Address = 'address',
+ Uint256 = 'uint256',
+ Uint8 = 'uint8',
+ Uint = 'uint',
}
export enum ExchangeContractErrCodes {
- ERROR_FILL_EXPIRED, // Order has already expired
- ERROR_FILL_NO_VALUE, // Order has already been fully filled or cancelled
- ERROR_FILL_TRUNCATION, // Rounding error too large
- ERROR_FILL_BALANCE_ALLOWANCE, // Insufficient balance or allowance for token transfer
- ERROR_CANCEL_EXPIRED, // Order has already expired
- ERROR_CANCEL_NO_VALUE, // Order has already been fully filled or cancelled
+ ERROR_FILL_EXPIRED, // Order has already expired
+ ERROR_FILL_NO_VALUE, // Order has already been fully filled or cancelled
+ ERROR_FILL_TRUNCATION, // Rounding error too large
+ ERROR_FILL_BALANCE_ALLOWANCE, // Insufficient balance or allowance for token transfer
+ ERROR_CANCEL_EXPIRED, // Order has already expired
+ ERROR_CANCEL_NO_VALUE, // Order has already been fully filled or cancelled
}
export enum ExchangeContractErrs {
- OrderFillExpired = 'ORDER_FILL_EXPIRED',
- OrderCancelExpired = 'ORDER_CANCEL_EXPIRED',
- OrderCancelAmountZero = 'ORDER_CANCEL_AMOUNT_ZERO',
- OrderAlreadyCancelledOrFilled = 'ORDER_ALREADY_CANCELLED_OR_FILLED',
- OrderFillAmountZero = 'ORDER_FILL_AMOUNT_ZERO',
- OrderRemainingFillAmountZero = 'ORDER_REMAINING_FILL_AMOUNT_ZERO',
- OrderFillRoundingError = 'ORDER_FILL_ROUNDING_ERROR',
- FillBalanceAllowanceError = 'FILL_BALANCE_ALLOWANCE_ERROR',
- InsufficientTakerBalance = 'INSUFFICIENT_TAKER_BALANCE',
- InsufficientTakerAllowance = 'INSUFFICIENT_TAKER_ALLOWANCE',
- InsufficientMakerBalance = 'INSUFFICIENT_MAKER_BALANCE',
- InsufficientMakerAllowance = 'INSUFFICIENT_MAKER_ALLOWANCE',
- InsufficientTakerFeeBalance = 'INSUFFICIENT_TAKER_FEE_BALANCE',
- InsufficientTakerFeeAllowance = 'INSUFFICIENT_TAKER_FEE_ALLOWANCE',
- InsufficientMakerFeeBalance = 'INSUFFICIENT_MAKER_FEE_BALANCE',
- InsufficientMakerFeeAllowance = 'INSUFFICIENT_MAKER_FEE_ALLOWANCE',
- TransactionSenderIsNotFillOrderTaker = 'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER',
- MultipleMakersInSingleCancelBatchDisallowed = 'MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH_DISALLOWED',
- InsufficientRemainingFillAmount = 'INSUFFICIENT_REMAINING_FILL_AMOUNT',
- MultipleTakerTokensInFillUpToDisallowed = 'MULTIPLE_TAKER_TOKENS_IN_FILL_UP_TO_DISALLOWED',
- BatchOrdersMustHaveSameExchangeAddress = 'BATCH_ORDERS_MUST_HAVE_SAME_EXCHANGE_ADDRESS',
- BatchOrdersMustHaveAtLeastOneItem = 'BATCH_ORDERS_MUST_HAVE_AT_LEAST_ONE_ITEM',
+ OrderFillExpired = 'ORDER_FILL_EXPIRED',
+ OrderCancelExpired = 'ORDER_CANCEL_EXPIRED',
+ OrderCancelAmountZero = 'ORDER_CANCEL_AMOUNT_ZERO',
+ OrderAlreadyCancelledOrFilled = 'ORDER_ALREADY_CANCELLED_OR_FILLED',
+ OrderFillAmountZero = 'ORDER_FILL_AMOUNT_ZERO',
+ OrderRemainingFillAmountZero = 'ORDER_REMAINING_FILL_AMOUNT_ZERO',
+ OrderFillRoundingError = 'ORDER_FILL_ROUNDING_ERROR',
+ FillBalanceAllowanceError = 'FILL_BALANCE_ALLOWANCE_ERROR',
+ InsufficientTakerBalance = 'INSUFFICIENT_TAKER_BALANCE',
+ InsufficientTakerAllowance = 'INSUFFICIENT_TAKER_ALLOWANCE',
+ InsufficientMakerBalance = 'INSUFFICIENT_MAKER_BALANCE',
+ InsufficientMakerAllowance = 'INSUFFICIENT_MAKER_ALLOWANCE',
+ InsufficientTakerFeeBalance = 'INSUFFICIENT_TAKER_FEE_BALANCE',
+ InsufficientTakerFeeAllowance = 'INSUFFICIENT_TAKER_FEE_ALLOWANCE',
+ InsufficientMakerFeeBalance = 'INSUFFICIENT_MAKER_FEE_BALANCE',
+ InsufficientMakerFeeAllowance = 'INSUFFICIENT_MAKER_FEE_ALLOWANCE',
+ TransactionSenderIsNotFillOrderTaker = 'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER',
+ MultipleMakersInSingleCancelBatchDisallowed = 'MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH_DISALLOWED',
+ InsufficientRemainingFillAmount = 'INSUFFICIENT_REMAINING_FILL_AMOUNT',
+ MultipleTakerTokensInFillUpToDisallowed = 'MULTIPLE_TAKER_TOKENS_IN_FILL_UP_TO_DISALLOWED',
+ BatchOrdersMustHaveSameExchangeAddress = 'BATCH_ORDERS_MUST_HAVE_SAME_EXCHANGE_ADDRESS',
+ BatchOrdersMustHaveAtLeastOneItem = 'BATCH_ORDERS_MUST_HAVE_AT_LEAST_ONE_ITEM',
}
export type RawLog = Web3.LogEntry;
export interface ContractEvent {
- logIndex: number;
- transactionIndex: number;
- transactionHash: string;
- blockHash: string;
- blockNumber: number;
- address: string;
- type: string;
- event: string;
- args: ContractEventArgs;
+ logIndex: number;
+ transactionIndex: number;
+ transactionHash: string;
+ blockHash: string;
+ blockNumber: number;
+ address: string;
+ type: string;
+ event: string;
+ args: ContractEventArgs;
}
export interface LogFillContractEventArgs {
- maker: string;
- taker: string;
- feeRecipient: string;
- makerToken: string;
- takerToken: string;
- filledMakerTokenAmount: BigNumber;
- filledTakerTokenAmount: BigNumber;
- paidMakerFee: BigNumber;
- paidTakerFee: BigNumber;
- tokens: string;
- orderHash: string;
+ maker: string;
+ taker: string;
+ feeRecipient: string;
+ makerToken: string;
+ takerToken: string;
+ filledMakerTokenAmount: BigNumber;
+ filledTakerTokenAmount: BigNumber;
+ paidMakerFee: BigNumber;
+ paidTakerFee: BigNumber;
+ tokens: string;
+ orderHash: string;
}
export interface LogCancelContractEventArgs {
- maker: string;
- feeRecipient: string;
- makerToken: string;
- takerToken: string;
- cancelledMakerTokenAmount: BigNumber;
- cancelledTakerTokenAmount: BigNumber;
- tokens: string;
- orderHash: string;
+ maker: string;
+ feeRecipient: string;
+ makerToken: string;
+ takerToken: string;
+ cancelledMakerTokenAmount: BigNumber;
+ cancelledTakerTokenAmount: BigNumber;
+ tokens: string;
+ orderHash: string;
}
export interface LogErrorContractEventArgs {
- errorId: BigNumber;
- orderHash: string;
+ errorId: BigNumber;
+ orderHash: string;
}
export type ExchangeContractEventArgs =
- | LogFillContractEventArgs
- | LogCancelContractEventArgs
- | LogErrorContractEventArgs;
+ | LogFillContractEventArgs
+ | LogCancelContractEventArgs
+ | LogErrorContractEventArgs;
export interface TransferContractEventArgs {
- _from: string;
- _to: string;
- _value: BigNumber;
+ _from: string;
+ _to: string;
+ _value: BigNumber;
}
export interface ApprovalContractEventArgs {
- _owner: string;
- _spender: string;
- _value: BigNumber;
+ _owner: string;
+ _spender: string;
+ _value: BigNumber;
}
export interface DepositContractEventArgs {
- _owner: string;
- _value: BigNumber;
+ _owner: string;
+ _value: BigNumber;
}
export interface WithdrawalContractEventArgs {
- _owner: string;
- _value: BigNumber;
+ _owner: string;
+ _value: BigNumber;
}
export type TokenContractEventArgs = TransferContractEventArgs | ApprovalContractEventArgs;
export type EtherTokenContractEventArgs =
- | TokenContractEventArgs
- | DepositContractEventArgs
- | WithdrawalContractEventArgs;
+ | TokenContractEventArgs
+ | DepositContractEventArgs
+ | WithdrawalContractEventArgs;
export type ContractEventArgs = ExchangeContractEventArgs | TokenContractEventArgs | EtherTokenContractEventArgs;
export type ContractEventArg = string | BigNumber;
export interface Order {
- maker: string;
- taker: string;
- makerFee: BigNumber;
- takerFee: BigNumber;
- makerTokenAmount: BigNumber;
- takerTokenAmount: BigNumber;
- makerTokenAddress: string;
- takerTokenAddress: string;
- salt: BigNumber;
- exchangeContractAddress: string;
- feeRecipient: string;
- expirationUnixTimestampSec: BigNumber;
+ maker: string;
+ taker: string;
+ makerFee: BigNumber;
+ takerFee: BigNumber;
+ makerTokenAmount: BigNumber;
+ takerTokenAmount: BigNumber;
+ makerTokenAddress: string;
+ takerTokenAddress: string;
+ salt: BigNumber;
+ exchangeContractAddress: string;
+ feeRecipient: string;
+ expirationUnixTimestampSec: BigNumber;
}
export interface SignedOrder extends Order {
- ecSignature: ECSignature;
+ ecSignature: ECSignature;
}
// [address, name, symbol, decimals, ipfsHash, swarmHash]
export type TokenMetadata = [string, string, string, BigNumber, string, string];
export interface Token {
- name: string;
- address: string;
- symbol: string;
- decimals: number;
+ name: string;
+ address: string;
+ symbol: string;
+ decimals: number;
}
export interface TxOpts {
- from: string;
- gas?: number;
- value?: BigNumber;
- gasPrice?: BigNumber;
+ from: string;
+ gas?: number;
+ value?: BigNumber;
+ gasPrice?: BigNumber;
}
export interface TokenAddressBySymbol {
- [symbol: string]: string;
+ [symbol: string]: string;
}
export enum ExchangeEvents {
- LogFill = 'LogFill',
- LogCancel = 'LogCancel',
- LogError = 'LogError',
+ LogFill = 'LogFill',
+ LogCancel = 'LogCancel',
+ LogError = 'LogError',
}
export enum TokenEvents {
- Transfer = 'Transfer',
- Approval = 'Approval',
+ Transfer = 'Transfer',
+ Approval = 'Approval',
}
export enum EtherTokenEvents {
- Transfer = 'Transfer',
- Approval = 'Approval',
- Deposit = 'Deposit',
- Withdrawal = 'Withdrawal',
+ Transfer = 'Transfer',
+ Approval = 'Approval',
+ Deposit = 'Deposit',
+ Withdrawal = 'Withdrawal',
}
export type ContractEvents = TokenEvents | ExchangeEvents | EtherTokenEvents;
export interface IndexedFilterValues {
- [index: string]: ContractEventArg;
+ [index: string]: ContractEventArg;
}
// Earliest is omitted by design. It is simply an alias for the `0` constant and
// is thus not very helpful. Moreover, this type is used in places that only accept
// `latest` or `pending`.
export enum BlockParamLiteral {
- Latest = 'latest',
- Pending = 'pending',
+ Latest = 'latest',
+ Pending = 'pending',
}
export type BlockParam = BlockParamLiteral | number;
export interface BlockRange {
- fromBlock: BlockParam;
- toBlock: BlockParam;
+ fromBlock: BlockParam;
+ toBlock: BlockParam;
}
export type DoneCallback = (err?: Error) => void;
export interface OrderCancellationRequest {
- order: Order | SignedOrder;
- takerTokenCancelAmount: BigNumber;
+ order: Order | SignedOrder;
+ takerTokenCancelAmount: BigNumber;
}
export interface OrderFillRequest {
- signedOrder: SignedOrder;
- takerTokenFillAmount: BigNumber;
+ signedOrder: SignedOrder;
+ takerTokenFillAmount: BigNumber;
}
export type AsyncMethod = (...args: any[]) => Promise<any>;
@@ -268,8 +268,8 @@ export type SyncMethod = (...args: any[]) => any;
export type Web3Provider = Web3.Provider;
export interface JSONRPCPayload {
- params: any[];
- method: string;
+ params: any[];
+ method: string;
}
/*
@@ -280,10 +280,10 @@ export interface JSONRPCPayload {
* cleanupJobIntervalMs: How often to run a cleanup job which revalidates all the orders. Defaults: 1h
*/
export interface OrderStateWatcherConfig {
- orderExpirationCheckingIntervalMs?: number;
- eventPollingIntervalMs?: number;
- expirationMarginMs?: number;
- cleanupJobIntervalMs?: number;
+ orderExpirationCheckingIntervalMs?: number;
+ eventPollingIntervalMs?: number;
+ expirationMarginMs?: number;
+ cleanupJobIntervalMs?: number;
}
/*
@@ -296,42 +296,42 @@ export interface OrderStateWatcherConfig {
* orderWatcherConfig: All the configs related to the orderWatcher
*/
export interface ZeroExConfig {
- networkId: number;
- gasPrice?: BigNumber;
- exchangeContractAddress?: string;
- zrxContractAddress?: string;
- tokenRegistryContractAddress?: string;
- tokenTransferProxyContractAddress?: string;
- orderWatcherConfig?: OrderStateWatcherConfig;
+ networkId: number;
+ gasPrice?: BigNumber;
+ exchangeContractAddress?: string;
+ zrxContractAddress?: string;
+ tokenRegistryContractAddress?: string;
+ tokenTransferProxyContractAddress?: string;
+ orderWatcherConfig?: OrderStateWatcherConfig;
}
export enum AbiType {
- Function = 'function',
- Constructor = 'constructor',
- Event = 'event',
- Fallback = 'fallback',
+ Function = 'function',
+ Constructor = 'constructor',
+ Event = 'event',
+ Fallback = 'fallback',
}
export interface DecodedLogArgs {
- [argName: string]: ContractEventArg;
+ [argName: string]: ContractEventArg;
}
export interface LogWithDecodedArgs<ArgsType> extends Web3.DecodedLogEntry<ArgsType> {}
export interface TransactionReceiptWithDecodedLogs extends TransactionReceipt {
- logs: Array<LogWithDecodedArgs<DecodedLogArgs> | Web3.LogEntry>;
+ logs: Array<LogWithDecodedArgs<DecodedLogArgs> | Web3.LogEntry>;
}
export type ArtifactContractName = 'ZRX' | 'TokenTransferProxy' | 'TokenRegistry' | 'Token' | 'Exchange' | 'EtherToken';
export interface Artifact {
- contract_name: ArtifactContractName;
- abi: Web3.ContractAbi;
- networks: {
- [networkId: number]: {
- address: string;
- };
- };
+ contract_name: ArtifactContractName;
+ abi: Web3.ContractAbi;
+ networks: {
+ [networkId: number]: {
+ address: string;
+ };
+ };
}
/*
@@ -341,7 +341,7 @@ export interface Artifact {
* allowance/balance to fill the entire remaining order amount.
*/
export interface ValidateOrderFillableOpts {
- expectedFillTakerTokenAmount?: BigNumber;
+ expectedFillTakerTokenAmount?: BigNumber;
}
/*
@@ -351,7 +351,7 @@ export interface ValidateOrderFillableOpts {
* flag when running Parity).
*/
export interface MethodOpts {
- defaultBlock?: Web3.BlockParam;
+ defaultBlock?: Web3.BlockParam;
}
/*
@@ -359,8 +359,8 @@ export interface MethodOpts {
* gasLimit: The amount of gas to send with a transaction
*/
export interface TransactionOpts {
- gasPrice?: BigNumber;
- gasLimit?: number;
+ gasPrice?: BigNumber;
+ gasLimit?: number;
}
/*
@@ -368,42 +368,42 @@ export interface TransactionOpts {
* broadcasting it. For example, order has a valid signature, maker has sufficient funds, etc. Default: true
*/
export interface OrderTransactionOpts extends TransactionOpts {
- shouldValidate?: boolean;
+ shouldValidate?: boolean;
}
export type FilterObject = Web3.FilterObject;
export enum TradeSide {
- Maker = 'maker',
- Taker = 'taker',
+ Maker = 'maker',
+ Taker = 'taker',
}
export enum TransferType {
- Trade = 'trade',
- Fee = 'fee',
+ Trade = 'trade',
+ Fee = 'fee',
}
export interface OrderRelevantState {
- makerBalance: BigNumber;
- makerProxyAllowance: BigNumber;
- makerFeeBalance: BigNumber;
- makerFeeProxyAllowance: BigNumber;
- filledTakerTokenAmount: BigNumber;
- cancelledTakerTokenAmount: BigNumber;
- remainingFillableMakerTokenAmount: BigNumber;
- remainingFillableTakerTokenAmount: BigNumber;
+ makerBalance: BigNumber;
+ makerProxyAllowance: BigNumber;
+ makerFeeBalance: BigNumber;
+ makerFeeProxyAllowance: BigNumber;
+ filledTakerTokenAmount: BigNumber;
+ cancelledTakerTokenAmount: BigNumber;
+ remainingFillableMakerTokenAmount: BigNumber;
+ remainingFillableTakerTokenAmount: BigNumber;
}
export interface OrderStateValid {
- isValid: true;
- orderHash: string;
- orderRelevantState: OrderRelevantState;
+ isValid: true;
+ orderHash: string;
+ orderRelevantState: OrderRelevantState;
}
export interface OrderStateInvalid {
- isValid: false;
- orderHash: string;
- error: ExchangeContractErrs;
+ isValid: false;
+ orderHash: string;
+ error: ExchangeContractErrs;
}
export type OrderState = OrderStateValid | OrderStateInvalid;
diff --git a/packages/0x.js/src/utils/abi_decoder.ts b/packages/0x.js/src/utils/abi_decoder.ts
index bbd2a0b1d..ace6dd165 100644
--- a/packages/0x.js/src/utils/abi_decoder.ts
+++ b/packages/0x.js/src/utils/abi_decoder.ts
@@ -6,67 +6,67 @@ import * as SolidityCoder from 'web3/lib/solidity/coder';
import { AbiType, ContractEventArgs, DecodedLogArgs, LogWithDecodedArgs, RawLog, SolidityTypes } from '../types';
export class AbiDecoder {
- private _savedABIs: Web3.AbiDefinition[] = [];
- private _methodIds: { [signatureHash: string]: Web3.EventAbi } = {};
- private static _padZeros(address: string) {
- let formatted = address;
- if (_.startsWith(formatted, '0x')) {
- formatted = formatted.slice(2);
- }
+ private _savedABIs: Web3.AbiDefinition[] = [];
+ private _methodIds: { [signatureHash: string]: Web3.EventAbi } = {};
+ private static _padZeros(address: string) {
+ let formatted = address;
+ if (_.startsWith(formatted, '0x')) {
+ formatted = formatted.slice(2);
+ }
- formatted = _.padStart(formatted, 40, '0');
- return `0x${formatted}`;
- }
- constructor(abiArrays: Web3.AbiDefinition[][]) {
- _.map(abiArrays, this._addABI.bind(this));
- }
- // This method can only decode logs from the 0x & ERC20 smart contracts
- public tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
- log: Web3.LogEntry,
- ): LogWithDecodedArgs<ArgsType> | RawLog {
- const methodId = log.topics[0];
- const event = this._methodIds[methodId];
- if (_.isUndefined(event)) {
- return log;
- }
- const logData = log.data;
- const decodedParams: DecodedLogArgs = {};
- let dataIndex = 0;
- let topicsIndex = 1;
+ formatted = _.padStart(formatted, 40, '0');
+ return `0x${formatted}`;
+ }
+ constructor(abiArrays: Web3.AbiDefinition[][]) {
+ _.map(abiArrays, this._addABI.bind(this));
+ }
+ // This method can only decode logs from the 0x & ERC20 smart contracts
+ public tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
+ log: Web3.LogEntry,
+ ): LogWithDecodedArgs<ArgsType> | RawLog {
+ const methodId = log.topics[0];
+ const event = this._methodIds[methodId];
+ if (_.isUndefined(event)) {
+ return log;
+ }
+ const logData = log.data;
+ const decodedParams: DecodedLogArgs = {};
+ let dataIndex = 0;
+ let topicsIndex = 1;
- const nonIndexedInputs = _.filter(event.inputs, input => !input.indexed);
- const dataTypes = _.map(nonIndexedInputs, input => input.type);
- const decodedData = SolidityCoder.decodeParams(dataTypes, logData.slice('0x'.length));
+ const nonIndexedInputs = _.filter(event.inputs, input => !input.indexed);
+ const dataTypes = _.map(nonIndexedInputs, input => input.type);
+ const decodedData = SolidityCoder.decodeParams(dataTypes, logData.slice('0x'.length));
- _.map(event.inputs, (param: Web3.EventParameter) => {
- // Indexed parameters are stored in topics. Non-indexed ones in decodedData
- let value = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++];
- if (param.type === SolidityTypes.Address) {
- value = AbiDecoder._padZeros(new BigNumber(value).toString(16));
- } else if (
- param.type === SolidityTypes.Uint256 ||
- param.type === SolidityTypes.Uint8 ||
- param.type === SolidityTypes.Uint
- ) {
- value = new BigNumber(value);
- }
- decodedParams[param.name] = value;
- });
+ _.map(event.inputs, (param: Web3.EventParameter) => {
+ // Indexed parameters are stored in topics. Non-indexed ones in decodedData
+ let value = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++];
+ if (param.type === SolidityTypes.Address) {
+ value = AbiDecoder._padZeros(new BigNumber(value).toString(16));
+ } else if (
+ param.type === SolidityTypes.Uint256 ||
+ param.type === SolidityTypes.Uint8 ||
+ param.type === SolidityTypes.Uint
+ ) {
+ value = new BigNumber(value);
+ }
+ decodedParams[param.name] = value;
+ });
- return {
- ...log,
- event: event.name,
- args: decodedParams,
- };
- }
- private _addABI(abiArray: Web3.AbiDefinition[]): void {
- _.map(abiArray, (abi: Web3.AbiDefinition) => {
- if (abi.type === AbiType.Event) {
- const signature = `${abi.name}(${_.map(abi.inputs, input => input.type).join(',')})`;
- const signatureHash = new Web3().sha3(signature);
- this._methodIds[signatureHash] = abi;
- }
- });
- this._savedABIs = this._savedABIs.concat(abiArray);
- }
+ return {
+ ...log,
+ event: event.name,
+ args: decodedParams,
+ };
+ }
+ private _addABI(abiArray: Web3.AbiDefinition[]): void {
+ _.map(abiArray, (abi: Web3.AbiDefinition) => {
+ if (abi.type === AbiType.Event) {
+ const signature = `${abi.name}(${_.map(abi.inputs, input => input.type).join(',')})`;
+ const signatureHash = new Web3().sha3(signature);
+ this._methodIds[signatureHash] = abi;
+ }
+ });
+ this._savedABIs = this._savedABIs.concat(abiArray);
+ }
}
diff --git a/packages/0x.js/src/utils/assert.ts b/packages/0x.js/src/utils/assert.ts
index c21f2dbca..776ee7b79 100644
--- a/packages/0x.js/src/utils/assert.ts
+++ b/packages/0x.js/src/utils/assert.ts
@@ -11,25 +11,25 @@ import { ECSignature } from '../types';
import { signatureUtils } from '../utils/signature_utils';
export const assert = {
- ...sharedAssert,
- isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string) {
- const isValidSignature = signatureUtils.isValidSignature(orderHash, ecSignature, signerAddress);
- this.assert(isValidSignature, `Expected order with hash '${orderHash}' to have a valid signature`);
- },
- async isSenderAddressAsync(
- variableName: string,
- senderAddressHex: string,
- web3Wrapper: Web3Wrapper,
- ): Promise<void> {
- sharedAssert.isETHAddressHex(variableName, senderAddressHex);
- const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex);
- sharedAssert.assert(
- isSenderAddressAvailable,
- `Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`,
- );
- },
- async isUserAddressAvailableAsync(web3Wrapper: Web3Wrapper): Promise<void> {
- const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
- this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 provider');
- },
+ ...sharedAssert,
+ isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string) {
+ const isValidSignature = signatureUtils.isValidSignature(orderHash, ecSignature, signerAddress);
+ this.assert(isValidSignature, `Expected order with hash '${orderHash}' to have a valid signature`);
+ },
+ async isSenderAddressAsync(
+ variableName: string,
+ senderAddressHex: string,
+ web3Wrapper: Web3Wrapper,
+ ): Promise<void> {
+ sharedAssert.isETHAddressHex(variableName, senderAddressHex);
+ const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex);
+ sharedAssert.assert(
+ isSenderAddressAvailable,
+ `Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`,
+ );
+ },
+ async isUserAddressAvailableAsync(web3Wrapper: Web3Wrapper): Promise<void> {
+ const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
+ this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 provider');
+ },
};
diff --git a/packages/0x.js/src/utils/constants.ts b/packages/0x.js/src/utils/constants.ts
index 06beec8e2..f32847ec2 100644
--- a/packages/0x.js/src/utils/constants.ts
+++ b/packages/0x.js/src/utils/constants.ts
@@ -1,12 +1,12 @@
import { BigNumber } from '@0xproject/utils';
export const constants = {
- NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
- TESTRPC_NETWORK_ID: 50,
- MAX_DIGITS_IN_UNSIGNED_256_INT: 78,
- INVALID_JUMP_PATTERN: 'invalid JUMP at',
- OUT_OF_GAS_PATTERN: 'out of gas',
- INVALID_TAKER_FORMAT: 'instance.taker is not of a type(s) string',
- UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1),
- DEFAULT_BLOCK_POLLING_INTERVAL: 1000,
+ NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
+ TESTRPC_NETWORK_ID: 50,
+ MAX_DIGITS_IN_UNSIGNED_256_INT: 78,
+ INVALID_JUMP_PATTERN: 'invalid JUMP at',
+ OUT_OF_GAS_PATTERN: 'out of gas',
+ INVALID_TAKER_FORMAT: 'instance.taker is not of a type(s) string',
+ UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1),
+ DEFAULT_BLOCK_POLLING_INTERVAL: 1000,
};
diff --git a/packages/0x.js/src/utils/decorators.ts b/packages/0x.js/src/utils/decorators.ts
index f774d734e..c1a022a81 100644
--- a/packages/0x.js/src/utils/decorators.ts
+++ b/packages/0x.js/src/utils/decorators.ts
@@ -7,85 +7,85 @@ import { constants } from './constants';
type ErrorTransformer = (err: Error) => Error;
const contractCallErrorTransformer = (error: Error) => {
- if (_.includes(error.message, constants.INVALID_JUMP_PATTERN)) {
- return new Error(ZeroExError.InvalidJump);
- }
- if (_.includes(error.message, constants.OUT_OF_GAS_PATTERN)) {
- return new Error(ZeroExError.OutOfGas);
- }
- return error;
+ if (_.includes(error.message, constants.INVALID_JUMP_PATTERN)) {
+ return new Error(ZeroExError.InvalidJump);
+ }
+ if (_.includes(error.message, constants.OUT_OF_GAS_PATTERN)) {
+ return new Error(ZeroExError.OutOfGas);
+ }
+ return error;
};
const schemaErrorTransformer = (error: Error) => {
- if (_.includes(error.message, constants.INVALID_TAKER_FORMAT)) {
- const errMsg =
- '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 error;
+ if (_.includes(error.message, constants.INVALID_TAKER_FORMAT)) {
+ const errMsg =
+ '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 error;
};
/**
* Source: https://stackoverflow.com/a/29837695/3546986
*/
const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
- const asyncErrorHandlingDecorator = (
- target: object,
- key: string | symbol,
- descriptor: TypedPropertyDescriptor<AsyncMethod>,
- ) => {
- const originalMethod = descriptor.value as AsyncMethod;
+ const asyncErrorHandlingDecorator = (
+ target: object,
+ key: string | symbol,
+ descriptor: TypedPropertyDescriptor<AsyncMethod>,
+ ) => {
+ const originalMethod = descriptor.value as AsyncMethod;
- // Do not use arrow syntax here. Use a function expression in
- // order to use the correct value of `this` in this method
- // tslint:disable-next-line:only-arrow-functions
- descriptor.value = async function(...args: any[]) {
- try {
- const result = await originalMethod.apply(this, args);
- return result;
- } catch (error) {
- const transformedError = errorTransformer(error);
- throw transformedError;
- }
- };
+ // Do not use arrow syntax here. Use a function expression in
+ // order to use the correct value of `this` in this method
+ // tslint:disable-next-line:only-arrow-functions
+ descriptor.value = async function(...args: any[]) {
+ try {
+ const result = await originalMethod.apply(this, args);
+ return result;
+ } catch (error) {
+ const transformedError = errorTransformer(error);
+ throw transformedError;
+ }
+ };
- return descriptor;
- };
+ return descriptor;
+ };
- return asyncErrorHandlingDecorator;
+ return asyncErrorHandlingDecorator;
};
const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
- const syncErrorHandlingDecorator = (
- target: object,
- key: string | symbol,
- descriptor: TypedPropertyDescriptor<SyncMethod>,
- ) => {
- const originalMethod = descriptor.value as SyncMethod;
+ const syncErrorHandlingDecorator = (
+ target: object,
+ key: string | symbol,
+ descriptor: TypedPropertyDescriptor<SyncMethod>,
+ ) => {
+ const originalMethod = descriptor.value as SyncMethod;
- // Do not use arrow syntax here. Use a function expression in
- // order to use the correct value of `this` in this method
- // tslint:disable-next-line:only-arrow-functions
- descriptor.value = function(...args: any[]) {
- try {
- const result = originalMethod.apply(this, args);
- return result;
- } catch (error) {
- const transformedError = errorTransformer(error);
- throw transformedError;
- }
- };
+ // Do not use arrow syntax here. Use a function expression in
+ // order to use the correct value of `this` in this method
+ // tslint:disable-next-line:only-arrow-functions
+ descriptor.value = function(...args: any[]) {
+ try {
+ const result = originalMethod.apply(this, args);
+ return result;
+ } catch (error) {
+ const transformedError = errorTransformer(error);
+ throw transformedError;
+ }
+ };
- return descriptor;
- };
+ return descriptor;
+ };
- return syncErrorHandlingDecorator;
+ return syncErrorHandlingDecorator;
};
// _.flow(f, g) = f ∘ g
const zeroExErrorTransformer = _.flow(schemaErrorTransformer, contractCallErrorTransformer);
export const decorators = {
- asyncZeroExErrorHandler: asyncErrorHandlerFactory(zeroExErrorTransformer),
- syncZeroExErrorHandler: syncErrorHandlerFactory(zeroExErrorTransformer),
+ asyncZeroExErrorHandler: asyncErrorHandlerFactory(zeroExErrorTransformer),
+ syncZeroExErrorHandler: syncErrorHandlerFactory(zeroExErrorTransformer),
};
diff --git a/packages/0x.js/src/utils/exchange_transfer_simulator.ts b/packages/0x.js/src/utils/exchange_transfer_simulator.ts
index 662cd210c..c9a2f87ce 100644
--- a/packages/0x.js/src/utils/exchange_transfer_simulator.ts
+++ b/packages/0x.js/src/utils/exchange_transfer_simulator.ts
@@ -6,101 +6,101 @@ import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allow
import { BlockParamLiteral, ExchangeContractErrs, TradeSide, TransferType } from '../types';
enum FailureReason {
- Balance = 'balance',
- ProxyAllowance = 'proxyAllowance',
+ Balance = 'balance',
+ ProxyAllowance = 'proxyAllowance',
}
const ERR_MSG_MAPPING = {
- [FailureReason.Balance]: {
- [TradeSide.Maker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerBalance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeBalance,
- },
- [TradeSide.Taker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerBalance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeBalance,
- },
- },
- [FailureReason.ProxyAllowance]: {
- [TradeSide.Maker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerAllowance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeAllowance,
- },
- [TradeSide.Taker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerAllowance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeAllowance,
- },
- },
+ [FailureReason.Balance]: {
+ [TradeSide.Maker]: {
+ [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerBalance,
+ [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeBalance,
+ },
+ [TradeSide.Taker]: {
+ [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerBalance,
+ [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeBalance,
+ },
+ },
+ [FailureReason.ProxyAllowance]: {
+ [TradeSide.Maker]: {
+ [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerAllowance,
+ [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeAllowance,
+ },
+ [TradeSide.Taker]: {
+ [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerAllowance,
+ [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeAllowance,
+ },
+ },
};
export class ExchangeTransferSimulator {
- private _store: BalanceAndProxyAllowanceLazyStore;
- private _UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber;
- private static _throwValidationError(
- failureReason: FailureReason,
- tradeSide: TradeSide,
- transferType: TransferType,
- ): never {
- const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType];
- throw new Error(errMsg);
- }
- constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) {
- this._store = new BalanceAndProxyAllowanceLazyStore(token, defaultBlock);
- this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
- }
- /**
- * Simulates transferFrom call performed by a proxy
- * @param tokenAddress Address of the token to be transferred
- * @param from Owner of the transferred tokens
- * @param to Recipient of the transferred tokens
- * @param amountInBaseUnits The amount of tokens being transferred
- * @param tradeSide Is Maker/Taker transferring
- * @param transferType Is it a fee payment or a value transfer
- */
- public async transferFromAsync(
- tokenAddress: string,
- from: string,
- to: string,
- amountInBaseUnits: BigNumber,
- tradeSide: TradeSide,
- transferType: TransferType,
- ): Promise<void> {
- const balance = await this._store.getBalanceAsync(tokenAddress, from);
- const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from);
- if (proxyAllowance.lessThan(amountInBaseUnits)) {
- ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType);
- }
- if (balance.lessThan(amountInBaseUnits)) {
- ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType);
- }
- await this._decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits);
- await this._decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits);
- await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits);
- }
- private async _decreaseProxyAllowanceAsync(
- tokenAddress: string,
- userAddress: string,
- amountInBaseUnits: BigNumber,
- ): Promise<void> {
- const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, userAddress);
- if (!proxyAllowance.eq(this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
- this._store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits));
- }
- }
- private async _increaseBalanceAsync(
- tokenAddress: string,
- userAddress: string,
- amountInBaseUnits: BigNumber,
- ): Promise<void> {
- const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
- this._store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits));
- }
- private async _decreaseBalanceAsync(
- tokenAddress: string,
- userAddress: string,
- amountInBaseUnits: BigNumber,
- ): Promise<void> {
- const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
- this._store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits));
- }
+ private _store: BalanceAndProxyAllowanceLazyStore;
+ private _UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber;
+ private static _throwValidationError(
+ failureReason: FailureReason,
+ tradeSide: TradeSide,
+ transferType: TransferType,
+ ): never {
+ const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType];
+ throw new Error(errMsg);
+ }
+ constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) {
+ this._store = new BalanceAndProxyAllowanceLazyStore(token, defaultBlock);
+ this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
+ }
+ /**
+ * Simulates transferFrom call performed by a proxy
+ * @param tokenAddress Address of the token to be transferred
+ * @param from Owner of the transferred tokens
+ * @param to Recipient of the transferred tokens
+ * @param amountInBaseUnits The amount of tokens being transferred
+ * @param tradeSide Is Maker/Taker transferring
+ * @param transferType Is it a fee payment or a value transfer
+ */
+ public async transferFromAsync(
+ tokenAddress: string,
+ from: string,
+ to: string,
+ amountInBaseUnits: BigNumber,
+ tradeSide: TradeSide,
+ transferType: TransferType,
+ ): Promise<void> {
+ const balance = await this._store.getBalanceAsync(tokenAddress, from);
+ const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from);
+ if (proxyAllowance.lessThan(amountInBaseUnits)) {
+ ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType);
+ }
+ if (balance.lessThan(amountInBaseUnits)) {
+ ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType);
+ }
+ await this._decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits);
+ await this._decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits);
+ await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits);
+ }
+ private async _decreaseProxyAllowanceAsync(
+ tokenAddress: string,
+ userAddress: string,
+ amountInBaseUnits: BigNumber,
+ ): Promise<void> {
+ const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, userAddress);
+ if (!proxyAllowance.eq(this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
+ this._store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits));
+ }
+ }
+ private async _increaseBalanceAsync(
+ tokenAddress: string,
+ userAddress: string,
+ amountInBaseUnits: BigNumber,
+ ): Promise<void> {
+ const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
+ this._store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits));
+ }
+ private async _decreaseBalanceAsync(
+ tokenAddress: string,
+ userAddress: string,
+ amountInBaseUnits: BigNumber,
+ ): Promise<void> {
+ const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
+ this._store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits));
+ }
}
diff --git a/packages/0x.js/src/utils/filter_utils.ts b/packages/0x.js/src/utils/filter_utils.ts
index 97205ace3..c34ae8893 100644
--- a/packages/0x.js/src/utils/filter_utils.ts
+++ b/packages/0x.js/src/utils/filter_utils.ts
@@ -9,79 +9,79 @@ import { BlockRange, ContractEvents, IndexedFilterValues } from '../types';
const TOPIC_LENGTH = 32;
export const filterUtils = {
- generateUUID(): string {
- return uuid();
- },
- getFilter(
- address: string,
- eventName: ContractEvents,
- indexFilterValues: IndexedFilterValues,
- abi: Web3.ContractAbi,
- blockRange?: BlockRange,
- ): Web3.FilterObject {
- const eventAbi = _.find(abi, { name: eventName }) as Web3.EventAbi;
- const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi, eventName);
- const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature));
- const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues);
- const topics = [topicForEventSignature, ...topicsForIndexedArgs];
- let filter: Web3.FilterObject = {
- address,
- topics,
- };
- if (!_.isUndefined(blockRange)) {
- filter = {
- ...blockRange,
- ...filter,
- };
- }
- return filter;
- },
- getEventSignatureFromAbiByName(eventAbi: Web3.EventAbi, eventName: ContractEvents): string {
- const types = _.map(eventAbi.inputs, 'type');
- const signature = `${eventAbi.name}(${types.join(',')})`;
- return signature;
- },
- getTopicsForIndexedArgs(abi: Web3.EventAbi, indexFilterValues: IndexedFilterValues): Array<string | null> {
- const topics: Array<string | null> = [];
- for (const eventInput of abi.inputs) {
- if (!eventInput.indexed) {
- continue;
- }
- if (_.isUndefined(indexFilterValues[eventInput.name])) {
- // Null is a wildcard topic in a JSON-RPC call
- topics.push(null);
- } else {
- const value = indexFilterValues[eventInput.name] as string;
- const buffer = ethUtil.toBuffer(value);
- const paddedBuffer = ethUtil.setLengthLeft(buffer, TOPIC_LENGTH);
- const topic = ethUtil.bufferToHex(paddedBuffer);
- topics.push(topic);
- }
- }
- return topics;
- },
- matchesFilter(log: Web3.LogEntry, filter: Web3.FilterObject): boolean {
- if (!_.isUndefined(filter.address) && log.address !== filter.address) {
- return false;
- }
- if (!_.isUndefined(filter.topics)) {
- return filterUtils.matchesTopics(log.topics, filter.topics);
- }
- return true;
- },
- matchesTopics(logTopics: string[], filterTopics: Array<string[] | string | null>): boolean {
- const matchesTopic = _.zipWith(logTopics, filterTopics, filterUtils.matchesTopic.bind(filterUtils));
- const matchesTopics = _.every(matchesTopic);
- return matchesTopics;
- },
- matchesTopic(logTopic: string, filterTopic: string[] | string | null): boolean {
- if (_.isArray(filterTopic)) {
- return _.includes(filterTopic, logTopic);
- }
- if (_.isString(filterTopic)) {
- return filterTopic === logTopic;
- }
- // null topic is a wildcard
- return true;
- },
+ generateUUID(): string {
+ return uuid();
+ },
+ getFilter(
+ address: string,
+ eventName: ContractEvents,
+ indexFilterValues: IndexedFilterValues,
+ abi: Web3.ContractAbi,
+ blockRange?: BlockRange,
+ ): Web3.FilterObject {
+ const eventAbi = _.find(abi, { name: eventName }) as Web3.EventAbi;
+ const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi, eventName);
+ const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature));
+ const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues);
+ const topics = [topicForEventSignature, ...topicsForIndexedArgs];
+ let filter: Web3.FilterObject = {
+ address,
+ topics,
+ };
+ if (!_.isUndefined(blockRange)) {
+ filter = {
+ ...blockRange,
+ ...filter,
+ };
+ }
+ return filter;
+ },
+ getEventSignatureFromAbiByName(eventAbi: Web3.EventAbi, eventName: ContractEvents): string {
+ const types = _.map(eventAbi.inputs, 'type');
+ const signature = `${eventAbi.name}(${types.join(',')})`;
+ return signature;
+ },
+ getTopicsForIndexedArgs(abi: Web3.EventAbi, indexFilterValues: IndexedFilterValues): Array<string | null> {
+ const topics: Array<string | null> = [];
+ for (const eventInput of abi.inputs) {
+ if (!eventInput.indexed) {
+ continue;
+ }
+ if (_.isUndefined(indexFilterValues[eventInput.name])) {
+ // Null is a wildcard topic in a JSON-RPC call
+ topics.push(null);
+ } else {
+ const value = indexFilterValues[eventInput.name] as string;
+ const buffer = ethUtil.toBuffer(value);
+ const paddedBuffer = ethUtil.setLengthLeft(buffer, TOPIC_LENGTH);
+ const topic = ethUtil.bufferToHex(paddedBuffer);
+ topics.push(topic);
+ }
+ }
+ return topics;
+ },
+ matchesFilter(log: Web3.LogEntry, filter: Web3.FilterObject): boolean {
+ if (!_.isUndefined(filter.address) && log.address !== filter.address) {
+ return false;
+ }
+ if (!_.isUndefined(filter.topics)) {
+ return filterUtils.matchesTopics(log.topics, filter.topics);
+ }
+ return true;
+ },
+ matchesTopics(logTopics: string[], filterTopics: Array<string[] | string | null>): boolean {
+ const matchesTopic = _.zipWith(logTopics, filterTopics, filterUtils.matchesTopic.bind(filterUtils));
+ const matchesTopics = _.every(matchesTopic);
+ return matchesTopics;
+ },
+ matchesTopic(logTopic: string, filterTopic: string[] | string | null): boolean {
+ if (_.isArray(filterTopic)) {
+ return _.includes(filterTopic, logTopic);
+ }
+ if (_.isString(filterTopic)) {
+ return filterTopic === logTopic;
+ }
+ // null topic is a wildcard
+ return true;
+ },
};
diff --git a/packages/0x.js/src/utils/order_state_utils.ts b/packages/0x.js/src/utils/order_state_utils.ts
index b7a55ff42..04d3b641e 100644
--- a/packages/0x.js/src/utils/order_state_utils.ts
+++ b/packages/0x.js/src/utils/order_state_utils.ts
@@ -7,138 +7,138 @@ import { RemainingFillableCalculator } from '../order_watcher/remaining_fillable
import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store';
import { OrderFilledCancelledLazyStore } from '../stores/order_filled_cancelled_lazy_store';
import {
- ExchangeContractErrs,
- OrderRelevantState,
- OrderState,
- OrderStateInvalid,
- OrderStateValid,
- SignedOrder,
+ ExchangeContractErrs,
+ OrderRelevantState,
+ OrderState,
+ OrderStateInvalid,
+ OrderStateValid,
+ SignedOrder,
} from '../types';
const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001;
export class OrderStateUtils {
- private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
- private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
- private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
- const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add(
- orderRelevantState.filledTakerTokenAmount,
- );
- const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
- if (availableTakerTokenAmount.eq(0)) {
- throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
- }
+ private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
+ private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
+ private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
+ const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add(
+ orderRelevantState.filledTakerTokenAmount,
+ );
+ const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
+ if (availableTakerTokenAmount.eq(0)) {
+ throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ }
- if (orderRelevantState.makerBalance.eq(0)) {
- throw new Error(ExchangeContractErrs.InsufficientMakerBalance);
- }
- if (orderRelevantState.makerProxyAllowance.eq(0)) {
- throw new Error(ExchangeContractErrs.InsufficientMakerAllowance);
- }
- if (!signedOrder.makerFee.eq(0)) {
- if (orderRelevantState.makerFeeBalance.eq(0)) {
- throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance);
- }
- if (orderRelevantState.makerFeeProxyAllowance.eq(0)) {
- throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance);
- }
- }
- const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount
- .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
- .dividedBy(signedOrder.makerTokenAmount);
- if (
- orderRelevantState.remainingFillableTakerTokenAmount.lessThan(
- minFillableTakerTokenAmountWithinNoRoundingErrorRange,
- )
- ) {
- throw new Error(ExchangeContractErrs.OrderFillRoundingError);
- }
- }
- constructor(
- balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore,
- orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore,
- ) {
- this._balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore;
- this._orderFilledCancelledLazyStore = orderFilledCancelledLazyStore;
- }
- public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> {
- const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder);
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- try {
- OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState);
- const orderState: OrderStateValid = {
- isValid: true,
- orderHash,
- orderRelevantState,
- };
- return orderState;
- } catch (err) {
- const orderState: OrderStateInvalid = {
- isValid: false,
- orderHash,
- error: err.message,
- };
- return orderState;
- }
- }
- public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
- // HACK: We access the private property here but otherwise the interface will be less nice.
- // If we pass it from the instantiator - there is no opportunity to get it there
- // because JS doesn't support async constructors.
- // Moreover - it's cached under the hood so it's equivalent to an async constructor.
- const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
- const zrxTokenAddress = exchange.getZRXTokenAddress();
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- const makerBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync(
- signedOrder.makerTokenAddress,
- signedOrder.maker,
- );
- const makerProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
- signedOrder.makerTokenAddress,
- signedOrder.maker,
- );
- const makerFeeBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync(
- zrxTokenAddress,
- signedOrder.maker,
- );
- const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
- zrxTokenAddress,
- signedOrder.maker,
- );
- const filledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getFilledTakerAmountAsync(orderHash);
- const cancelledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getCancelledTakerAmountAsync(
- orderHash,
- );
- const unavailableTakerTokenAmount = await exchange.getUnavailableTakerAmountAsync(orderHash);
- const totalMakerTokenAmount = signedOrder.makerTokenAmount;
- const totalTakerTokenAmount = signedOrder.takerTokenAmount;
- const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount);
- const remainingMakerTokenAmount = remainingTakerTokenAmount
- .times(totalMakerTokenAmount)
- .dividedToIntegerBy(totalTakerTokenAmount);
- const transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
- const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]);
+ if (orderRelevantState.makerBalance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerBalance);
+ }
+ if (orderRelevantState.makerProxyAllowance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerAllowance);
+ }
+ if (!signedOrder.makerFee.eq(0)) {
+ if (orderRelevantState.makerFeeBalance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance);
+ }
+ if (orderRelevantState.makerFeeProxyAllowance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance);
+ }
+ }
+ const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount
+ .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
+ .dividedBy(signedOrder.makerTokenAmount);
+ if (
+ orderRelevantState.remainingFillableTakerTokenAmount.lessThan(
+ minFillableTakerTokenAmountWithinNoRoundingErrorRange,
+ )
+ ) {
+ throw new Error(ExchangeContractErrs.OrderFillRoundingError);
+ }
+ }
+ constructor(
+ balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore,
+ orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore,
+ ) {
+ this._balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore;
+ this._orderFilledCancelledLazyStore = orderFilledCancelledLazyStore;
+ }
+ public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> {
+ const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder);
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ try {
+ OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState);
+ const orderState: OrderStateValid = {
+ isValid: true,
+ orderHash,
+ orderRelevantState,
+ };
+ return orderState;
+ } catch (err) {
+ const orderState: OrderStateInvalid = {
+ isValid: false,
+ orderHash,
+ error: err.message,
+ };
+ return orderState;
+ }
+ }
+ public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
+ // HACK: We access the private property here but otherwise the interface will be less nice.
+ // If we pass it from the instantiator - there is no opportunity to get it there
+ // because JS doesn't support async constructors.
+ // Moreover - it's cached under the hood so it's equivalent to an async constructor.
+ const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
+ const zrxTokenAddress = exchange.getZRXTokenAddress();
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ const makerBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync(
+ signedOrder.makerTokenAddress,
+ signedOrder.maker,
+ );
+ const makerProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
+ signedOrder.makerTokenAddress,
+ signedOrder.maker,
+ );
+ const makerFeeBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync(
+ zrxTokenAddress,
+ signedOrder.maker,
+ );
+ const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
+ zrxTokenAddress,
+ signedOrder.maker,
+ );
+ const filledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getFilledTakerAmountAsync(orderHash);
+ const cancelledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getCancelledTakerAmountAsync(
+ orderHash,
+ );
+ const unavailableTakerTokenAmount = await exchange.getUnavailableTakerAmountAsync(orderHash);
+ const totalMakerTokenAmount = signedOrder.makerTokenAmount;
+ const totalTakerTokenAmount = signedOrder.takerTokenAmount;
+ const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount);
+ const remainingMakerTokenAmount = remainingTakerTokenAmount
+ .times(totalMakerTokenAmount)
+ .dividedToIntegerBy(totalTakerTokenAmount);
+ const transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
+ const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]);
- const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress;
- const remainingFillableCalculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- const remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable();
- const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable();
- const orderRelevantState = {
- makerBalance,
- makerProxyAllowance,
- makerFeeBalance,
- makerFeeProxyAllowance,
- filledTakerTokenAmount,
- cancelledTakerTokenAmount,
- remainingFillableMakerTokenAmount,
- remainingFillableTakerTokenAmount,
- };
- return orderRelevantState;
- }
+ const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress;
+ const remainingFillableCalculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ const remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable();
+ const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable();
+ const orderRelevantState = {
+ makerBalance,
+ makerProxyAllowance,
+ makerFeeBalance,
+ makerFeeProxyAllowance,
+ filledTakerTokenAmount,
+ cancelledTakerTokenAmount,
+ remainingFillableMakerTokenAmount,
+ remainingFillableTakerTokenAmount,
+ };
+ return orderRelevantState;
+ }
}
diff --git a/packages/0x.js/src/utils/order_validation_utils.ts b/packages/0x.js/src/utils/order_validation_utils.ts
index 917d414c8..8ff22266f 100644
--- a/packages/0x.js/src/utils/order_validation_utils.ts
+++ b/packages/0x.js/src/utils/order_validation_utils.ts
@@ -10,207 +10,207 @@ import { utils } from '../utils/utils';
import { ExchangeTransferSimulator } from './exchange_transfer_simulator';
export class OrderValidationUtils {
- private _exchangeWrapper: ExchangeWrapper;
- public static validateCancelOrderThrowIfInvalid(
- order: Order,
- cancelTakerTokenAmount: BigNumber,
- unavailableTakerTokenAmount: BigNumber,
- ): void {
- if (cancelTakerTokenAmount.eq(0)) {
- throw new Error(ExchangeContractErrs.OrderCancelAmountZero);
- }
- if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) {
- throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
- }
- const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
- if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
- throw new Error(ExchangeContractErrs.OrderCancelExpired);
- }
- }
- public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- senderAddress: string,
- zrxTokenAddress: string,
- ): Promise<void> {
- const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerTokenAmount,
- );
- await exchangeTradeEmulator.transferFromAsync(
- signedOrder.makerTokenAddress,
- signedOrder.maker,
- senderAddress,
- fillMakerTokenAmount,
- TradeSide.Maker,
- TransferType.Trade,
- );
- await exchangeTradeEmulator.transferFromAsync(
- signedOrder.takerTokenAddress,
- senderAddress,
- signedOrder.maker,
- fillTakerTokenAmount,
- TradeSide.Taker,
- TransferType.Trade,
- );
- const makerFeeAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerFee,
- );
- await exchangeTradeEmulator.transferFromAsync(
- zrxTokenAddress,
- signedOrder.maker,
- signedOrder.feeRecipient,
- makerFeeAmount,
- TradeSide.Maker,
- TransferType.Fee,
- );
- const takerFeeAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.takerFee,
- );
- await exchangeTradeEmulator.transferFromAsync(
- zrxTokenAddress,
- senderAddress,
- signedOrder.feeRecipient,
- takerFeeAmount,
- TradeSide.Taker,
- TransferType.Fee,
- );
- }
- private static _validateRemainingFillAmountNotZeroOrThrow(
- takerTokenAmount: BigNumber,
- unavailableTakerTokenAmount: BigNumber,
- ) {
- if (takerTokenAmount.eq(unavailableTakerTokenAmount)) {
- throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
- }
- }
- private static _validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber) {
- const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
- if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
- throw new Error(ExchangeContractErrs.OrderFillExpired);
- }
- }
- private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
- const fillMakerTokenAmount = numerator
- .mul(target)
- .div(denominator)
- .round(0);
- return fillMakerTokenAmount;
- }
- constructor(exchangeWrapper: ExchangeWrapper) {
- this._exchangeWrapper = exchangeWrapper;
- }
- public async validateOrderFillableOrThrowAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- zrxTokenAddress: string,
- expectedFillTakerTokenAmount?: BigNumber,
- ): Promise<void> {
- const orderHash = utils.getOrderHashHex(signedOrder);
- const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
- signedOrder.takerTokenAmount,
- unavailableTakerTokenAmount,
- );
- OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
- let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
- if (!_.isUndefined(expectedFillTakerTokenAmount)) {
- fillTakerTokenAmount = expectedFillTakerTokenAmount;
- }
- const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerTokenAmount,
- );
- await exchangeTradeEmulator.transferFromAsync(
- signedOrder.makerTokenAddress,
- signedOrder.maker,
- signedOrder.taker,
- fillMakerTokenAmount,
- TradeSide.Maker,
- TransferType.Trade,
- );
- const makerFeeAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerFee,
- );
- await exchangeTradeEmulator.transferFromAsync(
- zrxTokenAddress,
- signedOrder.maker,
- signedOrder.feeRecipient,
- makerFeeAmount,
- TradeSide.Maker,
- TransferType.Fee,
- );
- }
- public async validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- zrxTokenAddress: string,
- ): Promise<BigNumber> {
- if (fillTakerTokenAmount.eq(0)) {
- throw new Error(ExchangeContractErrs.OrderFillAmountZero);
- }
- const orderHash = utils.getOrderHashHex(signedOrder);
- if (!ZeroEx.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker)) {
- throw new Error(ZeroExError.InvalidSignature);
- }
- const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
- signedOrder.takerTokenAmount,
- unavailableTakerTokenAmount,
- );
- if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) {
- throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
- }
- OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
- const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
- const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount)
- ? remainingTakerTokenAmount
- : fillTakerTokenAmount;
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- filledTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
+ private _exchangeWrapper: ExchangeWrapper;
+ public static validateCancelOrderThrowIfInvalid(
+ order: Order,
+ cancelTakerTokenAmount: BigNumber,
+ unavailableTakerTokenAmount: BigNumber,
+ ): void {
+ if (cancelTakerTokenAmount.eq(0)) {
+ throw new Error(ExchangeContractErrs.OrderCancelAmountZero);
+ }
+ if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) {
+ throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
+ }
+ const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
+ if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
+ throw new Error(ExchangeContractErrs.OrderCancelExpired);
+ }
+ }
+ public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTradeEmulator: ExchangeTransferSimulator,
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ senderAddress: string,
+ zrxTokenAddress: string,
+ ): Promise<void> {
+ const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
+ fillTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.makerTokenAmount,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ signedOrder.makerTokenAddress,
+ signedOrder.maker,
+ senderAddress,
+ fillMakerTokenAmount,
+ TradeSide.Maker,
+ TransferType.Trade,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ signedOrder.takerTokenAddress,
+ senderAddress,
+ signedOrder.maker,
+ fillTakerTokenAmount,
+ TradeSide.Taker,
+ TransferType.Trade,
+ );
+ const makerFeeAmount = OrderValidationUtils._getPartialAmount(
+ fillTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.makerFee,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ zrxTokenAddress,
+ signedOrder.maker,
+ signedOrder.feeRecipient,
+ makerFeeAmount,
+ TradeSide.Maker,
+ TransferType.Fee,
+ );
+ const takerFeeAmount = OrderValidationUtils._getPartialAmount(
+ fillTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.takerFee,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ zrxTokenAddress,
+ senderAddress,
+ signedOrder.feeRecipient,
+ takerFeeAmount,
+ TradeSide.Taker,
+ TransferType.Fee,
+ );
+ }
+ private static _validateRemainingFillAmountNotZeroOrThrow(
+ takerTokenAmount: BigNumber,
+ unavailableTakerTokenAmount: BigNumber,
+ ) {
+ if (takerTokenAmount.eq(unavailableTakerTokenAmount)) {
+ throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ }
+ }
+ private static _validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber) {
+ const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
+ if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
+ throw new Error(ExchangeContractErrs.OrderFillExpired);
+ }
+ }
+ private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
+ const fillMakerTokenAmount = numerator
+ .mul(target)
+ .div(denominator)
+ .round(0);
+ return fillMakerTokenAmount;
+ }
+ constructor(exchangeWrapper: ExchangeWrapper) {
+ this._exchangeWrapper = exchangeWrapper;
+ }
+ public async validateOrderFillableOrThrowAsync(
+ exchangeTradeEmulator: ExchangeTransferSimulator,
+ signedOrder: SignedOrder,
+ zrxTokenAddress: string,
+ expectedFillTakerTokenAmount?: BigNumber,
+ ): Promise<void> {
+ const orderHash = utils.getOrderHashHex(signedOrder);
+ const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
+ OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
+ signedOrder.takerTokenAmount,
+ unavailableTakerTokenAmount,
+ );
+ OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
+ let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
+ if (!_.isUndefined(expectedFillTakerTokenAmount)) {
+ fillTakerTokenAmount = expectedFillTakerTokenAmount;
+ }
+ const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
+ fillTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.makerTokenAmount,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ signedOrder.makerTokenAddress,
+ signedOrder.maker,
+ signedOrder.taker,
+ fillMakerTokenAmount,
+ TradeSide.Maker,
+ TransferType.Trade,
+ );
+ const makerFeeAmount = OrderValidationUtils._getPartialAmount(
+ fillTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.makerFee,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ zrxTokenAddress,
+ signedOrder.maker,
+ signedOrder.feeRecipient,
+ makerFeeAmount,
+ TradeSide.Maker,
+ TransferType.Fee,
+ );
+ }
+ public async validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator: ExchangeTransferSimulator,
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ zrxTokenAddress: string,
+ ): Promise<BigNumber> {
+ if (fillTakerTokenAmount.eq(0)) {
+ throw new Error(ExchangeContractErrs.OrderFillAmountZero);
+ }
+ const orderHash = utils.getOrderHashHex(signedOrder);
+ if (!ZeroEx.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker)) {
+ throw new Error(ZeroExError.InvalidSignature);
+ }
+ const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
+ OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
+ signedOrder.takerTokenAmount,
+ unavailableTakerTokenAmount,
+ );
+ if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) {
+ throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
+ }
+ OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
+ const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
+ const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount)
+ ? remainingTakerTokenAmount
+ : fillTakerTokenAmount;
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ filledTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
- const wouldRoundingErrorOccur = await this._exchangeWrapper.isRoundingErrorAsync(
- filledTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerTokenAmount,
- );
- if (wouldRoundingErrorOccur) {
- throw new Error(ExchangeContractErrs.OrderFillRoundingError);
- }
- return filledTakerTokenAmount;
- }
- public async validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- zrxTokenAddress: string,
- ): Promise<void> {
- const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- if (filledTakerTokenAmount !== fillTakerTokenAmount) {
- throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount);
- }
- }
+ const wouldRoundingErrorOccur = await this._exchangeWrapper.isRoundingErrorAsync(
+ filledTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.makerTokenAmount,
+ );
+ if (wouldRoundingErrorOccur) {
+ throw new Error(ExchangeContractErrs.OrderFillRoundingError);
+ }
+ return filledTakerTokenAmount;
+ }
+ public async validateFillOrKillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator: ExchangeTransferSimulator,
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ zrxTokenAddress: string,
+ ): Promise<void> {
+ const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ if (filledTakerTokenAmount !== fillTakerTokenAmount) {
+ throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount);
+ }
+ }
}
diff --git a/packages/0x.js/src/utils/signature_utils.ts b/packages/0x.js/src/utils/signature_utils.ts
index b0f1d61ef..19b5c76c7 100644
--- a/packages/0x.js/src/utils/signature_utils.ts
+++ b/packages/0x.js/src/utils/signature_utils.ts
@@ -3,44 +3,44 @@ import * as ethUtil from 'ethereumjs-util';
import { ECSignature } from '../types';
export const signatureUtils = {
- isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
- const dataBuff = ethUtil.toBuffer(data);
- const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
- try {
- const pubKey = ethUtil.ecrecover(
- msgHashBuff,
- signature.v,
- ethUtil.toBuffer(signature.r),
- ethUtil.toBuffer(signature.s),
- );
- const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
- return retrievedAddress === signerAddress;
- } catch (err) {
- return false;
- }
- },
- parseSignatureHexAsVRS(signatureHex: string): ECSignature {
- const signatureBuffer = ethUtil.toBuffer(signatureHex);
- let v = signatureBuffer[0];
- if (v < 27) {
- v += 27;
- }
- const r = signatureBuffer.slice(1, 33);
- const s = signatureBuffer.slice(33, 65);
- const ecSignature: ECSignature = {
- v,
- r: ethUtil.bufferToHex(r),
- s: ethUtil.bufferToHex(s),
- };
- return ecSignature;
- },
- parseSignatureHexAsRSV(signatureHex: string): ECSignature {
- const { v, r, s } = ethUtil.fromRpcSig(signatureHex);
- const ecSignature: ECSignature = {
- v,
- r: ethUtil.bufferToHex(r),
- s: ethUtil.bufferToHex(s),
- };
- return ecSignature;
- },
+ isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
+ const dataBuff = ethUtil.toBuffer(data);
+ const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
+ try {
+ const pubKey = ethUtil.ecrecover(
+ msgHashBuff,
+ signature.v,
+ ethUtil.toBuffer(signature.r),
+ ethUtil.toBuffer(signature.s),
+ );
+ const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
+ return retrievedAddress === signerAddress;
+ } catch (err) {
+ return false;
+ }
+ },
+ parseSignatureHexAsVRS(signatureHex: string): ECSignature {
+ const signatureBuffer = ethUtil.toBuffer(signatureHex);
+ let v = signatureBuffer[0];
+ if (v < 27) {
+ v += 27;
+ }
+ const r = signatureBuffer.slice(1, 33);
+ const s = signatureBuffer.slice(33, 65);
+ const ecSignature: ECSignature = {
+ v,
+ r: ethUtil.bufferToHex(r),
+ s: ethUtil.bufferToHex(s),
+ };
+ return ecSignature;
+ },
+ parseSignatureHexAsRSV(signatureHex: string): ECSignature {
+ const { v, r, s } = ethUtil.fromRpcSig(signatureHex);
+ const ecSignature: ECSignature = {
+ v,
+ r: ethUtil.bufferToHex(r),
+ s: ethUtil.bufferToHex(s),
+ };
+ return ecSignature;
+ },
};
diff --git a/packages/0x.js/src/utils/utils.ts b/packages/0x.js/src/utils/utils.ts
index 42cf5d956..ae07941ef 100644
--- a/packages/0x.js/src/utils/utils.ts
+++ b/packages/0x.js/src/utils/utils.ts
@@ -7,62 +7,62 @@ import * as _ from 'lodash';
import { Order, SignedOrder, SolidityTypes } from '../types';
export const utils = {
- /**
- * Converts BigNumber instance to BN
- * The only reason we convert to BN is to remain compatible with `ethABI. soliditySHA3` that
- * expects values of Solidity type `uint` to be passed as type `BN`.
- * We do not use BN anywhere else in the codebase.
- */
- bigNumberToBN(value: BigNumber) {
- return new BN(value.toString(), 10);
- },
- consoleLog(message: string): void {
- // tslint:disable-next-line: no-console
- console.log(message);
- },
- spawnSwitchErr(name: string, value: any): Error {
- return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
- },
- getOrderHashHex(order: Order | SignedOrder): string {
- const orderParts = [
- { value: order.exchangeContractAddress, type: SolidityTypes.Address },
- { value: order.maker, type: SolidityTypes.Address },
- { value: order.taker, type: SolidityTypes.Address },
- { value: order.makerTokenAddress, type: SolidityTypes.Address },
- { value: order.takerTokenAddress, 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.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 },
- ];
- const types = _.map(orderParts, o => o.type);
- const values = _.map(orderParts, o => o.value);
- const hashBuff = ethABI.soliditySHA3(types, values);
- const hashHex = ethUtil.bufferToHex(hashBuff);
- return hashHex;
- },
- getCurrentUnixTimestampSec(): BigNumber {
- return new BigNumber(Date.now() / 1000).round();
- },
- getCurrentUnixTimestampMs(): BigNumber {
- return new BigNumber(Date.now());
- },
+ /**
+ * Converts BigNumber instance to BN
+ * The only reason we convert to BN is to remain compatible with `ethABI. soliditySHA3` that
+ * expects values of Solidity type `uint` to be passed as type `BN`.
+ * We do not use BN anywhere else in the codebase.
+ */
+ bigNumberToBN(value: BigNumber) {
+ return new BN(value.toString(), 10);
+ },
+ consoleLog(message: string): void {
+ // tslint:disable-next-line: no-console
+ console.log(message);
+ },
+ spawnSwitchErr(name: string, value: any): Error {
+ return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
+ },
+ getOrderHashHex(order: Order | SignedOrder): string {
+ const orderParts = [
+ { value: order.exchangeContractAddress, type: SolidityTypes.Address },
+ { value: order.maker, type: SolidityTypes.Address },
+ { value: order.taker, type: SolidityTypes.Address },
+ { value: order.makerTokenAddress, type: SolidityTypes.Address },
+ { value: order.takerTokenAddress, 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.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 },
+ ];
+ const types = _.map(orderParts, o => o.type);
+ const values = _.map(orderParts, o => o.value);
+ const hashBuff = ethABI.soliditySHA3(types, values);
+ const hashHex = ethUtil.bufferToHex(hashBuff);
+ return hashHex;
+ },
+ getCurrentUnixTimestampSec(): BigNumber {
+ return new BigNumber(Date.now() / 1000).round();
+ },
+ getCurrentUnixTimestampMs(): BigNumber {
+ return new BigNumber(Date.now());
+ },
};
diff --git a/packages/0x.js/test/0x.js_test.ts b/packages/0x.js/test/0x.js_test.ts
index 927fe20be..5ece5192f 100644
--- a/packages/0x.js/test/0x.js_test.ts
+++ b/packages/0x.js/test/0x.js_test.ts
@@ -19,278 +19,278 @@ const expect = chai.expect;
const SHOULD_ADD_PERSONAL_MESSAGE_PREFIX = false;
describe('ZeroEx library', () => {
- const web3 = web3Factory.create();
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3.currentProvider, config);
- describe('#setProvider', () => {
- it('overrides provider in nested web3s and invalidates contractInstances', async () => {
- // Instantiate the contract instances with the current provider
- await (zeroEx.exchange as any)._getExchangeContractAsync();
- await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
- expect((zeroEx.exchange as any)._exchangeContractIfExists).to.not.be.undefined();
- expect((zeroEx.tokenRegistry as any)._tokenRegistryContractIfExists).to.not.be.undefined();
+ const web3 = web3Factory.create();
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3.currentProvider, config);
+ describe('#setProvider', () => {
+ it('overrides provider in nested web3s and invalidates contractInstances', async () => {
+ // Instantiate the contract instances with the current provider
+ await (zeroEx.exchange as any)._getExchangeContractAsync();
+ await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
+ expect((zeroEx.exchange as any)._exchangeContractIfExists).to.not.be.undefined();
+ expect((zeroEx.tokenRegistry as any)._tokenRegistryContractIfExists).to.not.be.undefined();
- const newProvider = web3Factory.getRpcProvider();
- // Add property to newProvider so that we can differentiate it from old provider
- (newProvider as any).zeroExTestId = 1;
- zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
+ const newProvider = web3Factory.getRpcProvider();
+ // Add property to newProvider so that we can differentiate it from old provider
+ (newProvider as any).zeroExTestId = 1;
+ zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
- // Check that contractInstances with old provider are removed after provider update
- expect((zeroEx.exchange as any)._exchangeContractIfExists).to.be.undefined();
- expect((zeroEx.tokenRegistry as any)._tokenRegistryContractIfExists).to.be.undefined();
+ // Check that contractInstances with old provider are removed after provider update
+ expect((zeroEx.exchange as any)._exchangeContractIfExists).to.be.undefined();
+ expect((zeroEx.tokenRegistry as any)._tokenRegistryContractIfExists).to.be.undefined();
- // Check that all nested web3 wrapper instances return the updated provider
- const nestedWeb3WrapperProvider = (zeroEx as any)._web3Wrapper.getCurrentProvider();
- expect(nestedWeb3WrapperProvider.zeroExTestId).to.be.a('number');
- const exchangeWeb3WrapperProvider = (zeroEx.exchange as any)._web3Wrapper.getCurrentProvider();
- expect(exchangeWeb3WrapperProvider.zeroExTestId).to.be.a('number');
- const tokenRegistryWeb3WrapperProvider = (zeroEx.tokenRegistry as any)._web3Wrapper.getCurrentProvider();
- expect(tokenRegistryWeb3WrapperProvider.zeroExTestId).to.be.a('number');
- });
- });
- describe('#isValidSignature', () => {
- // The Exchange smart contract `isValidSignature` method only validates orderHashes and assumes
- // the length of the data is exactly 32 bytes. Thus for these tests, we use data of this size.
- const dataHex = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
- const signature = {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- };
- const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
- it("should return false if the data doesn't pertain to the signature & address", async () => {
- expect(ZeroEx.isValidSignature('0x0', signature, address)).to.be.false();
- return expect(
- (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync('0x0', signature, address),
- ).to.become(false);
- });
- it("should return false if the address doesn't pertain to the signature & data", async () => {
- const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42';
- expect(ZeroEx.isValidSignature(dataHex, signature, validUnrelatedAddress)).to.be.false();
- return expect(
- (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(
- dataHex,
- signature,
- validUnrelatedAddress,
- ),
- ).to.become(false);
- });
- it("should return false if the signature doesn't pertain to the dataHex & address", async () => {
- const wrongSignature = _.assign({}, signature, { v: 28 });
- expect(ZeroEx.isValidSignature(dataHex, wrongSignature, address)).to.be.false();
- return expect(
- (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, wrongSignature, address),
- ).to.become(false);
- });
- it('should return true if the signature does pertain to the dataHex & address', async () => {
- const isValidSignatureLocal = ZeroEx.isValidSignature(dataHex, signature, address);
- expect(isValidSignatureLocal).to.be.true();
- return expect(
- (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, signature, address),
- ).to.become(true);
- });
- });
- describe('#generateSalt', () => {
- it('generates different salts', () => {
- const equal = ZeroEx.generatePseudoRandomSalt().eq(ZeroEx.generatePseudoRandomSalt());
- expect(equal).to.be.false();
- });
- it('generates salt in range [0..2^256)', () => {
- const salt = ZeroEx.generatePseudoRandomSalt();
- expect(salt.greaterThanOrEqualTo(0)).to.be.true();
- const twoPow256 = new BigNumber(2).pow(256);
- expect(salt.lessThan(twoPow256)).to.be.true();
- });
- });
- describe('#isValidOrderHash', () => {
- it('returns false if the value is not a hex string', () => {
- const isValid = ZeroEx.isValidOrderHash('not a hex');
- expect(isValid).to.be.false();
- });
- it('returns false if the length is wrong', () => {
- const isValid = ZeroEx.isValidOrderHash('0xdeadbeef');
- expect(isValid).to.be.false();
- });
- it('returns true if order hash is correct', () => {
- const isValid = ZeroEx.isValidOrderHash('0x' + Array(65).join('0'));
- expect(isValid).to.be.true();
- });
- });
- describe('#toUnitAmount', () => {
- it('should throw if invalid baseUnit amount supplied as argument', () => {
- const invalidBaseUnitAmount = new BigNumber(1000000000.4);
- const decimals = 6;
- expect(() => ZeroEx.toUnitAmount(invalidBaseUnitAmount, decimals)).to.throw(
- 'amount should be in baseUnits (no decimals), found value: 1000000000.4',
- );
- });
- it('Should return the expected unit amount for the decimals passed in', () => {
- const baseUnitAmount = new BigNumber(1000000000);
- const decimals = 6;
- const unitAmount = ZeroEx.toUnitAmount(baseUnitAmount, decimals);
- const expectedUnitAmount = new BigNumber(1000);
- expect(unitAmount).to.be.bignumber.equal(expectedUnitAmount);
- });
- });
- describe('#toBaseUnitAmount', () => {
- it('Should return the expected base unit amount for the decimals passed in', () => {
- const unitAmount = new BigNumber(1000);
- const decimals = 6;
- const baseUnitAmount = ZeroEx.toBaseUnitAmount(unitAmount, decimals);
- const expectedUnitAmount = new BigNumber(1000000000);
- expect(baseUnitAmount).to.be.bignumber.equal(expectedUnitAmount);
- });
- it('should throw if unitAmount has more decimals then specified as the max decimal precision', () => {
- const unitAmount = new BigNumber(0.823091);
- const decimals = 5;
- expect(() => ZeroEx.toBaseUnitAmount(unitAmount, decimals)).to.throw(
- 'Invalid unit amount: 0.823091 - Too many decimal places',
- );
- });
- });
- describe('#getOrderHashHex', () => {
- const expectedOrderHash = '0x39da987067a3c9e5f1617694f1301326ba8c8b0498ebef5df4863bed394e3c83';
- const fakeExchangeContractAddress = '0xb69e673309512a9d726f87304c6984054f87a93b';
- const order: Order = {
- maker: constants.NULL_ADDRESS,
- taker: constants.NULL_ADDRESS,
- feeRecipient: constants.NULL_ADDRESS,
- makerTokenAddress: constants.NULL_ADDRESS,
- takerTokenAddress: constants.NULL_ADDRESS,
- exchangeContractAddress: fakeExchangeContractAddress,
- salt: new BigNumber(0),
- makerFee: new BigNumber(0),
- takerFee: new BigNumber(0),
- makerTokenAmount: new BigNumber(0),
- takerTokenAmount: new BigNumber(0),
- expirationUnixTimestampSec: new BigNumber(0),
- };
- it('calculates the order hash', async () => {
- const orderHash = ZeroEx.getOrderHashHex(order);
- expect(orderHash).to.be.equal(expectedOrderHash);
- });
- it('throws a readable error message if taker format is invalid', async () => {
- const orderWithInvalidtakerFormat = {
- ...order,
- taker: (null as any) as string,
- };
- const expectedErrorMessage =
- '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);
- });
- });
- describe('#signOrderHashAsync', () => {
- let stubs: Sinon.SinonStub[] = [];
- let makerAddress: string;
- before(async () => {
- const availableAddreses = await zeroEx.getAvailableAddressesAsync();
- makerAddress = availableAddreses[0];
- });
- afterEach(() => {
- // clean up any stubs after the test has completed
- _.each(stubs, s => s.restore());
- stubs = [];
- });
- it('Should return the correct ECSignature', async () => {
- const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
- const expectedECSignature = {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- };
- const ecSignature = await zeroEx.signOrderHashAsync(
- orderHash,
- makerAddress,
- SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
- );
- expect(ecSignature).to.deep.equal(expectedECSignature);
- });
- it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => {
- const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
- const signature =
- '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb021b';
- const expectedECSignature = {
- v: 27,
- r: '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3',
- s: '0x050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb02',
- };
- stubs = [
- Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)),
- Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
- ];
+ // Check that all nested web3 wrapper instances return the updated provider
+ const nestedWeb3WrapperProvider = (zeroEx as any)._web3Wrapper.getCurrentProvider();
+ expect(nestedWeb3WrapperProvider.zeroExTestId).to.be.a('number');
+ const exchangeWeb3WrapperProvider = (zeroEx.exchange as any)._web3Wrapper.getCurrentProvider();
+ expect(exchangeWeb3WrapperProvider.zeroExTestId).to.be.a('number');
+ const tokenRegistryWeb3WrapperProvider = (zeroEx.tokenRegistry as any)._web3Wrapper.getCurrentProvider();
+ expect(tokenRegistryWeb3WrapperProvider.zeroExTestId).to.be.a('number');
+ });
+ });
+ describe('#isValidSignature', () => {
+ // The Exchange smart contract `isValidSignature` method only validates orderHashes and assumes
+ // the length of the data is exactly 32 bytes. Thus for these tests, we use data of this size.
+ const dataHex = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
+ const signature = {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ };
+ const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
+ it("should return false if the data doesn't pertain to the signature & address", async () => {
+ expect(ZeroEx.isValidSignature('0x0', signature, address)).to.be.false();
+ return expect(
+ (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync('0x0', signature, address),
+ ).to.become(false);
+ });
+ it("should return false if the address doesn't pertain to the signature & data", async () => {
+ const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42';
+ expect(ZeroEx.isValidSignature(dataHex, signature, validUnrelatedAddress)).to.be.false();
+ return expect(
+ (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(
+ dataHex,
+ signature,
+ validUnrelatedAddress,
+ ),
+ ).to.become(false);
+ });
+ it("should return false if the signature doesn't pertain to the dataHex & address", async () => {
+ const wrongSignature = _.assign({}, signature, { v: 28 });
+ expect(ZeroEx.isValidSignature(dataHex, wrongSignature, address)).to.be.false();
+ return expect(
+ (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, wrongSignature, address),
+ ).to.become(false);
+ });
+ it('should return true if the signature does pertain to the dataHex & address', async () => {
+ const isValidSignatureLocal = ZeroEx.isValidSignature(dataHex, signature, address);
+ expect(isValidSignatureLocal).to.be.true();
+ return expect(
+ (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, signature, address),
+ ).to.become(true);
+ });
+ });
+ describe('#generateSalt', () => {
+ it('generates different salts', () => {
+ const equal = ZeroEx.generatePseudoRandomSalt().eq(ZeroEx.generatePseudoRandomSalt());
+ expect(equal).to.be.false();
+ });
+ it('generates salt in range [0..2^256)', () => {
+ const salt = ZeroEx.generatePseudoRandomSalt();
+ expect(salt.greaterThanOrEqualTo(0)).to.be.true();
+ const twoPow256 = new BigNumber(2).pow(256);
+ expect(salt.lessThan(twoPow256)).to.be.true();
+ });
+ });
+ describe('#isValidOrderHash', () => {
+ it('returns false if the value is not a hex string', () => {
+ const isValid = ZeroEx.isValidOrderHash('not a hex');
+ expect(isValid).to.be.false();
+ });
+ it('returns false if the length is wrong', () => {
+ const isValid = ZeroEx.isValidOrderHash('0xdeadbeef');
+ expect(isValid).to.be.false();
+ });
+ it('returns true if order hash is correct', () => {
+ const isValid = ZeroEx.isValidOrderHash('0x' + Array(65).join('0'));
+ expect(isValid).to.be.true();
+ });
+ });
+ describe('#toUnitAmount', () => {
+ it('should throw if invalid baseUnit amount supplied as argument', () => {
+ const invalidBaseUnitAmount = new BigNumber(1000000000.4);
+ const decimals = 6;
+ expect(() => ZeroEx.toUnitAmount(invalidBaseUnitAmount, decimals)).to.throw(
+ 'amount should be in baseUnits (no decimals), found value: 1000000000.4',
+ );
+ });
+ it('Should return the expected unit amount for the decimals passed in', () => {
+ const baseUnitAmount = new BigNumber(1000000000);
+ const decimals = 6;
+ const unitAmount = ZeroEx.toUnitAmount(baseUnitAmount, decimals);
+ const expectedUnitAmount = new BigNumber(1000);
+ expect(unitAmount).to.be.bignumber.equal(expectedUnitAmount);
+ });
+ });
+ describe('#toBaseUnitAmount', () => {
+ it('Should return the expected base unit amount for the decimals passed in', () => {
+ const unitAmount = new BigNumber(1000);
+ const decimals = 6;
+ const baseUnitAmount = ZeroEx.toBaseUnitAmount(unitAmount, decimals);
+ const expectedUnitAmount = new BigNumber(1000000000);
+ expect(baseUnitAmount).to.be.bignumber.equal(expectedUnitAmount);
+ });
+ it('should throw if unitAmount has more decimals then specified as the max decimal precision', () => {
+ const unitAmount = new BigNumber(0.823091);
+ const decimals = 5;
+ expect(() => ZeroEx.toBaseUnitAmount(unitAmount, decimals)).to.throw(
+ 'Invalid unit amount: 0.823091 - Too many decimal places',
+ );
+ });
+ });
+ describe('#getOrderHashHex', () => {
+ const expectedOrderHash = '0x39da987067a3c9e5f1617694f1301326ba8c8b0498ebef5df4863bed394e3c83';
+ const fakeExchangeContractAddress = '0xb69e673309512a9d726f87304c6984054f87a93b';
+ const order: Order = {
+ maker: constants.NULL_ADDRESS,
+ taker: constants.NULL_ADDRESS,
+ feeRecipient: constants.NULL_ADDRESS,
+ makerTokenAddress: constants.NULL_ADDRESS,
+ takerTokenAddress: constants.NULL_ADDRESS,
+ exchangeContractAddress: fakeExchangeContractAddress,
+ salt: new BigNumber(0),
+ makerFee: new BigNumber(0),
+ takerFee: new BigNumber(0),
+ makerTokenAmount: new BigNumber(0),
+ takerTokenAmount: new BigNumber(0),
+ expirationUnixTimestampSec: new BigNumber(0),
+ };
+ it('calculates the order hash', async () => {
+ const orderHash = ZeroEx.getOrderHashHex(order);
+ expect(orderHash).to.be.equal(expectedOrderHash);
+ });
+ it('throws a readable error message if taker format is invalid', async () => {
+ const orderWithInvalidtakerFormat = {
+ ...order,
+ taker: (null as any) as string,
+ };
+ const expectedErrorMessage =
+ '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);
+ });
+ });
+ describe('#signOrderHashAsync', () => {
+ let stubs: Sinon.SinonStub[] = [];
+ let makerAddress: string;
+ before(async () => {
+ const availableAddreses = await zeroEx.getAvailableAddressesAsync();
+ makerAddress = availableAddreses[0];
+ });
+ afterEach(() => {
+ // clean up any stubs after the test has completed
+ _.each(stubs, s => s.restore());
+ stubs = [];
+ });
+ it('Should return the correct ECSignature', async () => {
+ const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
+ const expectedECSignature = {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ };
+ const ecSignature = await zeroEx.signOrderHashAsync(
+ orderHash,
+ makerAddress,
+ SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
+ );
+ expect(ecSignature).to.deep.equal(expectedECSignature);
+ });
+ it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => {
+ const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
+ const signature =
+ '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb021b';
+ const expectedECSignature = {
+ v: 27,
+ r: '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3',
+ s: '0x050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb02',
+ };
+ stubs = [
+ Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)),
+ Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
+ ];
- const ecSignature = await zeroEx.signOrderHashAsync(
- orderHash,
- makerAddress,
- SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
- );
- expect(ecSignature).to.deep.equal(expectedECSignature);
- });
- it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => {
- const orderHash = '0xc793e33ffded933b76f2f48d9aa3339fc090399d5e7f5dec8d3660f5480793f7';
- const signature =
- '0x1bc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee02dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960';
- const expectedECSignature = {
- v: 27,
- r: '0xc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee0',
- s: '0x2dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960',
- };
- stubs = [
- Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)),
- Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
- ];
+ const ecSignature = await zeroEx.signOrderHashAsync(
+ orderHash,
+ makerAddress,
+ SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
+ );
+ expect(ecSignature).to.deep.equal(expectedECSignature);
+ });
+ it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => {
+ const orderHash = '0xc793e33ffded933b76f2f48d9aa3339fc090399d5e7f5dec8d3660f5480793f7';
+ const signature =
+ '0x1bc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee02dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960';
+ const expectedECSignature = {
+ v: 27,
+ r: '0xc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee0',
+ s: '0x2dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960',
+ };
+ stubs = [
+ Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)),
+ Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
+ ];
- const ecSignature = await zeroEx.signOrderHashAsync(
- orderHash,
- makerAddress,
- SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
- );
- expect(ecSignature).to.deep.equal(expectedECSignature);
- });
- });
- describe('#awaitTransactionMinedAsync', () => {
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- it('returns transaction receipt with decoded logs', async () => {
- const availableAddresses = await zeroEx.getAvailableAddressesAsync();
- const coinbase = availableAddresses[0];
- const tokens = await zeroEx.tokenRegistry.getTokensAsync();
- const tokenUtils = new TokenUtils(tokens);
- const zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- const proxyAddress = zeroEx.proxy.getContractAddress();
- const txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(zrxTokenAddress, coinbase);
- const txReceiptWithDecodedLogs = await zeroEx.awaitTransactionMinedAsync(txHash);
- const log = txReceiptWithDecodedLogs.logs[0] as LogWithDecodedArgs<ApprovalContractEventArgs>;
- expect(log.event).to.be.equal(TokenEvents.Approval);
- expect(log.args._owner).to.be.equal(coinbase);
- expect(log.args._spender).to.be.equal(proxyAddress);
- expect(log.args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- });
- describe('#config', () => {
- it('allows to specify exchange contract address', async () => {
- const zeroExConfig = {
- exchangeContractAddress: ZeroEx.NULL_ADDRESS,
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroExWithWrongExchangeAddress = new ZeroEx(web3.currentProvider, zeroExConfig);
- expect(zeroExWithWrongExchangeAddress.exchange.getContractAddress()).to.be.equal(ZeroEx.NULL_ADDRESS);
- });
- it('allows to specify token registry token contract address', async () => {
- const zeroExConfig = {
- tokenRegistryContractAddress: ZeroEx.NULL_ADDRESS,
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroExWithWrongTokenRegistryAddress = new ZeroEx(web3.currentProvider, zeroExConfig);
- expect(zeroExWithWrongTokenRegistryAddress.tokenRegistry.getContractAddress()).to.be.equal(
- ZeroEx.NULL_ADDRESS,
- );
- });
- });
+ const ecSignature = await zeroEx.signOrderHashAsync(
+ orderHash,
+ makerAddress,
+ SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
+ );
+ expect(ecSignature).to.deep.equal(expectedECSignature);
+ });
+ });
+ describe('#awaitTransactionMinedAsync', () => {
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ it('returns transaction receipt with decoded logs', async () => {
+ const availableAddresses = await zeroEx.getAvailableAddressesAsync();
+ const coinbase = availableAddresses[0];
+ const tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ const tokenUtils = new TokenUtils(tokens);
+ const zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ const proxyAddress = zeroEx.proxy.getContractAddress();
+ const txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(zrxTokenAddress, coinbase);
+ const txReceiptWithDecodedLogs = await zeroEx.awaitTransactionMinedAsync(txHash);
+ const log = txReceiptWithDecodedLogs.logs[0] as LogWithDecodedArgs<ApprovalContractEventArgs>;
+ expect(log.event).to.be.equal(TokenEvents.Approval);
+ expect(log.args._owner).to.be.equal(coinbase);
+ expect(log.args._spender).to.be.equal(proxyAddress);
+ expect(log.args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ });
+ describe('#config', () => {
+ it('allows to specify exchange contract address', async () => {
+ const zeroExConfig = {
+ exchangeContractAddress: ZeroEx.NULL_ADDRESS,
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroExWithWrongExchangeAddress = new ZeroEx(web3.currentProvider, zeroExConfig);
+ expect(zeroExWithWrongExchangeAddress.exchange.getContractAddress()).to.be.equal(ZeroEx.NULL_ADDRESS);
+ });
+ it('allows to specify token registry token contract address', async () => {
+ const zeroExConfig = {
+ tokenRegistryContractAddress: ZeroEx.NULL_ADDRESS,
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroExWithWrongTokenRegistryAddress = new ZeroEx(web3.currentProvider, zeroExConfig);
+ expect(zeroExWithWrongTokenRegistryAddress.tokenRegistry.getContractAddress()).to.be.equal(
+ ZeroEx.NULL_ADDRESS,
+ );
+ });
+ });
});
diff --git a/packages/0x.js/test/artifacts_test.ts b/packages/0x.js/test/artifacts_test.ts
index e8ab9aa97..3a791f436 100644
--- a/packages/0x.js/test/artifacts_test.ts
+++ b/packages/0x.js/test/artifacts_test.ts
@@ -12,44 +12,44 @@ chaiSetup.configure();
const TIMEOUT = 10000;
describe('Artifacts', () => {
- describe('contracts are deployed on kovan', () => {
- const kovanRpcUrl = constants.KOVAN_RPC_URL;
- const packageJSONContent = fs.readFileSync('package.json', 'utf-8');
- const packageJSON = JSON.parse(packageJSONContent);
- const mnemonic = packageJSON.config.mnemonic;
- const web3Provider = new HDWalletProvider(mnemonic, kovanRpcUrl);
- const config = {
- networkId: constants.KOVAN_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3Provider, config);
- it('token registry contract is deployed', async () => {
- await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
- }).timeout(TIMEOUT);
- it('proxy contract is deployed', async () => {
- await (zeroEx.proxy as any)._getTokenTransferProxyContractAsync();
- }).timeout(TIMEOUT);
- it('exchange contract is deployed', async () => {
- await (zeroEx.exchange as any)._getExchangeContractAsync();
- }).timeout(TIMEOUT);
- });
- describe('contracts are deployed on ropsten', () => {
- const ropstenRpcUrl = constants.ROPSTEN_RPC_URL;
- const packageJSONContent = fs.readFileSync('package.json', 'utf-8');
- const packageJSON = JSON.parse(packageJSONContent);
- const mnemonic = packageJSON.config.mnemonic;
- const web3Provider = new HDWalletProvider(mnemonic, ropstenRpcUrl);
- const config = {
- networkId: constants.ROPSTEN_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3Provider, config);
- it('token registry contract is deployed', async () => {
- await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
- }).timeout(TIMEOUT);
- it('proxy contract is deployed', async () => {
- await (zeroEx.proxy as any)._getTokenTransferProxyContractAsync();
- }).timeout(TIMEOUT);
- it('exchange contract is deployed', async () => {
- await (zeroEx.exchange as any)._getExchangeContractAsync();
- }).timeout(TIMEOUT);
- });
+ describe('contracts are deployed on kovan', () => {
+ const kovanRpcUrl = constants.KOVAN_RPC_URL;
+ const packageJSONContent = fs.readFileSync('package.json', 'utf-8');
+ const packageJSON = JSON.parse(packageJSONContent);
+ const mnemonic = packageJSON.config.mnemonic;
+ const web3Provider = new HDWalletProvider(mnemonic, kovanRpcUrl);
+ const config = {
+ networkId: constants.KOVAN_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3Provider, config);
+ it('token registry contract is deployed', async () => {
+ await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
+ }).timeout(TIMEOUT);
+ it('proxy contract is deployed', async () => {
+ await (zeroEx.proxy as any)._getTokenTransferProxyContractAsync();
+ }).timeout(TIMEOUT);
+ it('exchange contract is deployed', async () => {
+ await (zeroEx.exchange as any)._getExchangeContractAsync();
+ }).timeout(TIMEOUT);
+ });
+ describe('contracts are deployed on ropsten', () => {
+ const ropstenRpcUrl = constants.ROPSTEN_RPC_URL;
+ const packageJSONContent = fs.readFileSync('package.json', 'utf-8');
+ const packageJSON = JSON.parse(packageJSONContent);
+ const mnemonic = packageJSON.config.mnemonic;
+ const web3Provider = new HDWalletProvider(mnemonic, ropstenRpcUrl);
+ const config = {
+ networkId: constants.ROPSTEN_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3Provider, config);
+ it('token registry contract is deployed', async () => {
+ await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
+ }).timeout(TIMEOUT);
+ it('proxy contract is deployed', async () => {
+ await (zeroEx.proxy as any)._getTokenTransferProxyContractAsync();
+ }).timeout(TIMEOUT);
+ it('exchange contract is deployed', async () => {
+ await (zeroEx.exchange as any)._getExchangeContractAsync();
+ }).timeout(TIMEOUT);
+ });
});
diff --git a/packages/0x.js/test/assert_test.ts b/packages/0x.js/test/assert_test.ts
index 1f2820070..2c72a5e88 100644
--- a/packages/0x.js/test/assert_test.ts
+++ b/packages/0x.js/test/assert_test.ts
@@ -10,34 +10,34 @@ import { web3Factory } from './utils/web3_factory';
const expect = chai.expect;
describe('Assertion library', () => {
- const web3 = web3Factory.create();
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3.currentProvider, config);
- describe('#isSenderAddressHexAsync', () => {
- it('throws when address is invalid', async () => {
- const address = '0xdeadbeef';
- const varName = 'address';
- return expect(
- 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 () => {
- const validUnrelatedAddress = '0x8b0292b11a196601eddce54b665cafeca0347d42';
- const varName = 'address';
- return expect(
- assert.isSenderAddressAsync(varName, validUnrelatedAddress, (zeroEx as any)._web3Wrapper),
- ).to.be.rejectedWith(
- `Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 provider`,
- );
- });
- it("doesn't throw if address is available", async () => {
- const availableAddress = (await zeroEx.getAvailableAddressesAsync())[0];
- const varName = 'address';
- return expect(
- assert.isSenderAddressAsync(varName, availableAddress, (zeroEx as any)._web3Wrapper),
- ).to.become(undefined);
- });
- });
+ const web3 = web3Factory.create();
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3.currentProvider, config);
+ describe('#isSenderAddressHexAsync', () => {
+ it('throws when address is invalid', async () => {
+ const address = '0xdeadbeef';
+ const varName = 'address';
+ return expect(
+ 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 () => {
+ const validUnrelatedAddress = '0x8b0292b11a196601eddce54b665cafeca0347d42';
+ const varName = 'address';
+ return expect(
+ assert.isSenderAddressAsync(varName, validUnrelatedAddress, (zeroEx as any)._web3Wrapper),
+ ).to.be.rejectedWith(
+ `Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 provider`,
+ );
+ });
+ it("doesn't throw if address is available", async () => {
+ const availableAddress = (await zeroEx.getAvailableAddressesAsync())[0];
+ const varName = 'address';
+ return expect(
+ assert.isSenderAddressAsync(varName, availableAddress, (zeroEx as any)._web3Wrapper),
+ ).to.become(undefined);
+ });
+ });
});
diff --git a/packages/0x.js/test/ether_token_wrapper_test.ts b/packages/0x.js/test/ether_token_wrapper_test.ts
index b810fc9f1..67e62dc12 100644
--- a/packages/0x.js/test/ether_token_wrapper_test.ts
+++ b/packages/0x.js/test/ether_token_wrapper_test.ts
@@ -5,17 +5,17 @@ import 'mocha';
import * as Web3 from 'web3';
import {
- ApprovalContractEventArgs,
- BlockParamLiteral,
- BlockRange,
- DecodedLogEvent,
- DepositContractEventArgs,
- EtherTokenEvents,
- Token,
- TransferContractEventArgs,
- WithdrawalContractEventArgs,
- ZeroEx,
- ZeroExError,
+ ApprovalContractEventArgs,
+ BlockParamLiteral,
+ BlockRange,
+ DecodedLogEvent,
+ DepositContractEventArgs,
+ EtherTokenEvents,
+ Token,
+ TransferContractEventArgs,
+ WithdrawalContractEventArgs,
+ ZeroEx,
+ ZeroExError,
} from '../src';
import { artifacts } from '../src/artifacts';
import { DoneCallback } from '../src/types';
@@ -37,339 +37,339 @@ const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
const MAX_REASONABLE_GAS_COST_IN_WEI = 62517;
describe('EtherTokenWrapper', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let tokens: Token[];
- let userAddresses: string[];
- let addressWithETH: string;
- let wethContractAddress: string;
- let depositWeiAmount: BigNumber;
- let decimalPlaces: number;
- let addressWithoutFunds: string;
- const gasPrice = new BigNumber(1);
- const zeroExConfig = {
- gasPrice,
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const transferAmount = new BigNumber(42);
- const allowanceAmount = new BigNumber(42);
- const depositAmount = new BigNumber(42);
- const withdrawalAmount = new BigNumber(42);
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig);
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- addressWithETH = userAddresses[0];
- wethContractAddress = (zeroEx.etherToken as any)._getContractAddress(artifacts.EtherTokenArtifact);
- depositWeiAmount = (zeroEx as any)._web3Wrapper.toWei(new BigNumber(5));
- decimalPlaces = 7;
- addressWithoutFunds = userAddresses[1];
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#depositAsync', () => {
- it('should successfully deposit ETH and issue Wrapped ETH tokens', async () => {
- const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
- expect(preETHBalance).to.be.bignumber.gt(0);
- expect(preWETHBalance).to.be.bignumber.equal(0);
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let tokens: Token[];
+ let userAddresses: string[];
+ let addressWithETH: string;
+ let wethContractAddress: string;
+ let depositWeiAmount: BigNumber;
+ let decimalPlaces: number;
+ let addressWithoutFunds: string;
+ const gasPrice = new BigNumber(1);
+ const zeroExConfig = {
+ gasPrice,
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const transferAmount = new BigNumber(42);
+ const allowanceAmount = new BigNumber(42);
+ const depositAmount = new BigNumber(42);
+ const withdrawalAmount = new BigNumber(42);
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig);
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ addressWithETH = userAddresses[0];
+ wethContractAddress = (zeroEx.etherToken as any)._getContractAddress(artifacts.EtherTokenArtifact);
+ depositWeiAmount = (zeroEx as any)._web3Wrapper.toWei(new BigNumber(5));
+ decimalPlaces = 7;
+ addressWithoutFunds = userAddresses[1];
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#depositAsync', () => {
+ it('should successfully deposit ETH and issue Wrapped ETH tokens', async () => {
+ const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
+ expect(preETHBalance).to.be.bignumber.gt(0);
+ expect(preWETHBalance).to.be.bignumber.equal(0);
- const txHash = await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH);
- await zeroEx.awaitTransactionMinedAsync(txHash);
+ const txHash = await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
- const postETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
+ const postETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
- expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(depositWeiAmount);
- const remainingETHInWei = preETHBalance.minus(depositWeiAmount);
- const gasCost = remainingETHInWei.minus(postETHBalanceInWei);
- expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
- });
- it('should throw if user has insufficient ETH balance for deposit', async () => {
- const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(depositWeiAmount);
+ const remainingETHInWei = preETHBalance.minus(depositWeiAmount);
+ const gasCost = remainingETHInWei.minus(postETHBalanceInWei);
+ expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
+ });
+ it('should throw if user has insufficient ETH balance for deposit', async () => {
+ const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- const extraETHBalance = (zeroEx as any)._web3Wrapper.toWei(5, 'ether');
- const overETHBalanceinWei = preETHBalance.add(extraETHBalance);
+ const extraETHBalance = (zeroEx as any)._web3Wrapper.toWei(5, 'ether');
+ const overETHBalanceinWei = preETHBalance.add(extraETHBalance);
- return expect(
- zeroEx.etherToken.depositAsync(wethContractAddress, overETHBalanceinWei, addressWithETH),
- ).to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit);
- });
- });
- describe('#withdrawAsync', () => {
- it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => {
- const ETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ return expect(
+ zeroEx.etherToken.depositAsync(wethContractAddress, overETHBalanceinWei, addressWithETH),
+ ).to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit);
+ });
+ });
+ describe('#withdrawAsync', () => {
+ it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => {
+ const ETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH);
+ await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH);
- const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount);
- const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
- let gasCost = expectedPreETHBalance.minus(preETHBalance);
- expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
- expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount);
+ const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount);
+ const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
+ let gasCost = expectedPreETHBalance.minus(preETHBalance);
+ expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
+ expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount);
- const txHash = await zeroEx.etherToken.withdrawAsync(wethContractAddress, depositWeiAmount, addressWithETH);
- await zeroEx.awaitTransactionMinedAsync(txHash);
+ const txHash = await zeroEx.etherToken.withdrawAsync(wethContractAddress, depositWeiAmount, addressWithETH);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
- const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
+ const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
- expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(0);
- const expectedETHBalance = preETHBalance.add(depositWeiAmount).round(decimalPlaces);
- gasCost = expectedETHBalance.minus(postETHBalance);
- expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
- });
- it('should throw if user has insufficient WETH balance for withdrawl', async () => {
- const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
- expect(preWETHBalance).to.be.bignumber.equal(0);
+ expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(0);
+ const expectedETHBalance = preETHBalance.add(depositWeiAmount).round(decimalPlaces);
+ gasCost = expectedETHBalance.minus(postETHBalance);
+ expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
+ });
+ it('should throw if user has insufficient WETH balance for withdrawl', async () => {
+ const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
+ expect(preWETHBalance).to.be.bignumber.equal(0);
- const overWETHBalance = preWETHBalance.add(999999999);
+ const overWETHBalance = preWETHBalance.add(999999999);
- return expect(
- zeroEx.etherToken.withdrawAsync(wethContractAddress, overWETHBalance, addressWithETH),
- ).to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal);
- });
- });
- describe('#subscribe', () => {
- const indexFilterValues = {};
- let etherTokenAddress: string;
- before(() => {
- const tokenUtils = new TokenUtils(tokens);
- const etherToken = tokenUtils.getWethTokenOrThrow();
- etherTokenAddress = etherToken.address;
- });
- afterEach(() => {
- zeroEx.etherToken.unsubscribeAll();
- });
- // Hack: Mocha does not allow a test to be both async and have a `done` callback
- // Since we need to await the receipt of the event in the `subscribe` callback,
- // we do need both. A hack is to make the top-level async fn w/ a done callback and then
- // wrap the rest of the test in an async block
- // Source: https://github.com/mochajs/mocha/issues/2407
- it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<TransferContractEventArgs>) => {
- expect(logEvent).to.not.be.undefined();
- expect(logEvent.isRemoved).to.be.false();
- expect(logEvent.log.logIndex).to.be.equal(0);
- expect(logEvent.log.transactionIndex).to.be.equal(0);
- expect(logEvent.log.blockNumber).to.be.a('number');
- const args = logEvent.log.args;
- expect(args._from).to.be.equal(addressWithETH);
- expect(args._to).to.be.equal(addressWithoutFunds);
- expect(args._value).to.be.bignumber.equal(transferAmount);
- },
- );
- await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
- zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Transfer, indexFilterValues, callback);
- await zeroEx.token.transferAsync(
- etherTokenAddress,
- addressWithETH,
- addressWithoutFunds,
- transferAmount,
- );
- })().catch(done);
- });
- it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- expect(logEvent).to.not.be.undefined();
- expect(logEvent.isRemoved).to.be.false();
- const args = logEvent.log.args;
- expect(args._owner).to.be.equal(addressWithETH);
- expect(args._spender).to.be.equal(addressWithoutFunds);
- expect(args._value).to.be.bignumber.equal(allowanceAmount);
- },
- );
- zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Approval, indexFilterValues, callback);
- await zeroEx.token.setAllowanceAsync(
- etherTokenAddress,
- addressWithETH,
- addressWithoutFunds,
- allowanceAmount,
- );
- })().catch(done);
- });
- it('Should receive the Deposit event when ether is being deposited', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<DepositContractEventArgs>) => {
- expect(logEvent).to.not.be.undefined();
- expect(logEvent.isRemoved).to.be.false();
- const args = logEvent.log.args;
- expect(args._owner).to.be.equal(addressWithETH);
- expect(args._value).to.be.bignumber.equal(depositAmount);
- },
- );
- zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Deposit, indexFilterValues, callback);
- await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
- })().catch(done);
- });
- it('Should receive the Withdrawal event when ether is being withdrawn', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<WithdrawalContractEventArgs>) => {
- expect(logEvent).to.not.be.undefined();
- expect(logEvent.isRemoved).to.be.false();
- const args = logEvent.log.args;
- expect(args._owner).to.be.equal(addressWithETH);
- expect(args._value).to.be.bignumber.equal(depositAmount);
- },
- );
- await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
- zeroEx.etherToken.subscribe(
- etherTokenAddress,
- EtherTokenEvents.Withdrawal,
- indexFilterValues,
- callback,
- );
- await zeroEx.etherToken.withdrawAsync(etherTokenAddress, withdrawalAmount, addressWithETH);
- })().catch(done);
- });
- it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- zeroEx.etherToken.subscribe(
- etherTokenAddress,
- EtherTokenEvents.Transfer,
- indexFilterValues,
- callbackNeverToBeCalled,
- );
- const callbackToBeCalled = reportNodeCallbackErrors(done)();
- const newProvider = web3Factory.getRpcProvider();
- zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
- await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
- zeroEx.etherToken.subscribe(
- etherTokenAddress,
- EtherTokenEvents.Transfer,
- indexFilterValues,
- callbackToBeCalled,
- );
- await zeroEx.token.transferAsync(
- etherTokenAddress,
- addressWithETH,
- addressWithoutFunds,
- transferAmount,
- );
- })().catch(done);
- });
- it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
- const subscriptionToken = zeroEx.etherToken.subscribe(
- etherTokenAddress,
- EtherTokenEvents.Transfer,
- indexFilterValues,
- callbackNeverToBeCalled,
- );
- zeroEx.etherToken.unsubscribe(subscriptionToken);
- await zeroEx.token.transferAsync(
- etherTokenAddress,
- addressWithETH,
- addressWithoutFunds,
- transferAmount,
- );
- done();
- })().catch(done);
- });
- });
- describe('#getLogsAsync', () => {
- let etherTokenAddress: string;
- let tokenTransferProxyAddress: string;
- const blockRange: BlockRange = {
- fromBlock: 0,
- toBlock: BlockParamLiteral.Latest,
- };
- let txHash: string;
- before(() => {
- addressWithETH = userAddresses[0];
- const tokenUtils = new TokenUtils(tokens);
- const etherToken = tokenUtils.getWethTokenOrThrow();
- etherTokenAddress = etherToken.address;
- tokenTransferProxyAddress = zeroEx.proxy.getContractAddress();
- });
- it('should get logs with decoded args emitted by Approval', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = EtherTokenEvents.Approval;
- const indexFilterValues = {};
- const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>(
- etherTokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(logs[0].event).to.be.equal(eventName);
- expect(args._owner).to.be.equal(addressWithETH);
- expect(args._spender).to.be.equal(tokenTransferProxyAddress);
- expect(args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- it('should get logs with decoded args emitted by Deposit', async () => {
- await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
- const eventName = EtherTokenEvents.Deposit;
- const indexFilterValues = {};
- const logs = await zeroEx.etherToken.getLogsAsync<DepositContractEventArgs>(
- etherTokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(logs[0].event).to.be.equal(eventName);
- expect(args._owner).to.be.equal(addressWithETH);
- expect(args._value).to.be.bignumber.equal(depositAmount);
- });
- it('should only get the logs with the correct event name', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const differentEventName = EtherTokenEvents.Transfer;
- const indexFilterValues = {};
- const logs = await zeroEx.etherToken.getLogsAsync(
- etherTokenAddress,
- differentEventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(0);
- });
- it('should only get the logs with the correct indexed fields', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithoutFunds);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = EtherTokenEvents.Approval;
- const indexFilterValues = {
- _owner: addressWithETH,
- };
- const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>(
- etherTokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(args._owner).to.be.equal(addressWithETH);
- });
- });
+ return expect(
+ zeroEx.etherToken.withdrawAsync(wethContractAddress, overWETHBalance, addressWithETH),
+ ).to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal);
+ });
+ });
+ describe('#subscribe', () => {
+ const indexFilterValues = {};
+ let etherTokenAddress: string;
+ before(() => {
+ const tokenUtils = new TokenUtils(tokens);
+ const etherToken = tokenUtils.getWethTokenOrThrow();
+ etherTokenAddress = etherToken.address;
+ });
+ afterEach(() => {
+ zeroEx.etherToken.unsubscribeAll();
+ });
+ // Hack: Mocha does not allow a test to be both async and have a `done` callback
+ // Since we need to await the receipt of the event in the `subscribe` callback,
+ // we do need both. A hack is to make the top-level async fn w/ a done callback and then
+ // wrap the rest of the test in an async block
+ // Source: https://github.com/mochajs/mocha/issues/2407
+ it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<TransferContractEventArgs>) => {
+ expect(logEvent).to.not.be.undefined();
+ expect(logEvent.isRemoved).to.be.false();
+ expect(logEvent.log.logIndex).to.be.equal(0);
+ expect(logEvent.log.transactionIndex).to.be.equal(0);
+ expect(logEvent.log.blockNumber).to.be.a('number');
+ const args = logEvent.log.args;
+ expect(args._from).to.be.equal(addressWithETH);
+ expect(args._to).to.be.equal(addressWithoutFunds);
+ expect(args._value).to.be.bignumber.equal(transferAmount);
+ },
+ );
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
+ zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Transfer, indexFilterValues, callback);
+ await zeroEx.token.transferAsync(
+ etherTokenAddress,
+ addressWithETH,
+ addressWithoutFunds,
+ transferAmount,
+ );
+ })().catch(done);
+ });
+ it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ expect(logEvent).to.not.be.undefined();
+ expect(logEvent.isRemoved).to.be.false();
+ const args = logEvent.log.args;
+ expect(args._owner).to.be.equal(addressWithETH);
+ expect(args._spender).to.be.equal(addressWithoutFunds);
+ expect(args._value).to.be.bignumber.equal(allowanceAmount);
+ },
+ );
+ zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Approval, indexFilterValues, callback);
+ await zeroEx.token.setAllowanceAsync(
+ etherTokenAddress,
+ addressWithETH,
+ addressWithoutFunds,
+ allowanceAmount,
+ );
+ })().catch(done);
+ });
+ it('Should receive the Deposit event when ether is being deposited', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<DepositContractEventArgs>) => {
+ expect(logEvent).to.not.be.undefined();
+ expect(logEvent.isRemoved).to.be.false();
+ const args = logEvent.log.args;
+ expect(args._owner).to.be.equal(addressWithETH);
+ expect(args._value).to.be.bignumber.equal(depositAmount);
+ },
+ );
+ zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Deposit, indexFilterValues, callback);
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
+ })().catch(done);
+ });
+ it('Should receive the Withdrawal event when ether is being withdrawn', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<WithdrawalContractEventArgs>) => {
+ expect(logEvent).to.not.be.undefined();
+ expect(logEvent.isRemoved).to.be.false();
+ const args = logEvent.log.args;
+ expect(args._owner).to.be.equal(addressWithETH);
+ expect(args._value).to.be.bignumber.equal(depositAmount);
+ },
+ );
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
+ zeroEx.etherToken.subscribe(
+ etherTokenAddress,
+ EtherTokenEvents.Withdrawal,
+ indexFilterValues,
+ callback,
+ );
+ await zeroEx.etherToken.withdrawAsync(etherTokenAddress, withdrawalAmount, addressWithETH);
+ })().catch(done);
+ });
+ it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ zeroEx.etherToken.subscribe(
+ etherTokenAddress,
+ EtherTokenEvents.Transfer,
+ indexFilterValues,
+ callbackNeverToBeCalled,
+ );
+ const callbackToBeCalled = reportNodeCallbackErrors(done)();
+ const newProvider = web3Factory.getRpcProvider();
+ zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
+ zeroEx.etherToken.subscribe(
+ etherTokenAddress,
+ EtherTokenEvents.Transfer,
+ indexFilterValues,
+ callbackToBeCalled,
+ );
+ await zeroEx.token.transferAsync(
+ etherTokenAddress,
+ addressWithETH,
+ addressWithoutFunds,
+ transferAmount,
+ );
+ })().catch(done);
+ });
+ it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
+ const subscriptionToken = zeroEx.etherToken.subscribe(
+ etherTokenAddress,
+ EtherTokenEvents.Transfer,
+ indexFilterValues,
+ callbackNeverToBeCalled,
+ );
+ zeroEx.etherToken.unsubscribe(subscriptionToken);
+ await zeroEx.token.transferAsync(
+ etherTokenAddress,
+ addressWithETH,
+ addressWithoutFunds,
+ transferAmount,
+ );
+ done();
+ })().catch(done);
+ });
+ });
+ describe('#getLogsAsync', () => {
+ let etherTokenAddress: string;
+ let tokenTransferProxyAddress: string;
+ const blockRange: BlockRange = {
+ fromBlock: 0,
+ toBlock: BlockParamLiteral.Latest,
+ };
+ let txHash: string;
+ before(() => {
+ addressWithETH = userAddresses[0];
+ const tokenUtils = new TokenUtils(tokens);
+ const etherToken = tokenUtils.getWethTokenOrThrow();
+ etherTokenAddress = etherToken.address;
+ tokenTransferProxyAddress = zeroEx.proxy.getContractAddress();
+ });
+ it('should get logs with decoded args emitted by Approval', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const eventName = EtherTokenEvents.Approval;
+ const indexFilterValues = {};
+ const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>(
+ etherTokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(logs[0].event).to.be.equal(eventName);
+ expect(args._owner).to.be.equal(addressWithETH);
+ expect(args._spender).to.be.equal(tokenTransferProxyAddress);
+ expect(args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ it('should get logs with decoded args emitted by Deposit', async () => {
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
+ const eventName = EtherTokenEvents.Deposit;
+ const indexFilterValues = {};
+ const logs = await zeroEx.etherToken.getLogsAsync<DepositContractEventArgs>(
+ etherTokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(logs[0].event).to.be.equal(eventName);
+ expect(args._owner).to.be.equal(addressWithETH);
+ expect(args._value).to.be.bignumber.equal(depositAmount);
+ });
+ it('should only get the logs with the correct event name', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const differentEventName = EtherTokenEvents.Transfer;
+ const indexFilterValues = {};
+ const logs = await zeroEx.etherToken.getLogsAsync(
+ etherTokenAddress,
+ differentEventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(0);
+ });
+ it('should only get the logs with the correct indexed fields', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithoutFunds);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const eventName = EtherTokenEvents.Approval;
+ const indexFilterValues = {
+ _owner: addressWithETH,
+ };
+ const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>(
+ etherTokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(args._owner).to.be.equal(addressWithETH);
+ });
+ });
});
diff --git a/packages/0x.js/test/event_watcher_test.ts b/packages/0x.js/test/event_watcher_test.ts
index f92fb2b02..f0c97eced 100644
--- a/packages/0x.js/test/event_watcher_test.ts
+++ b/packages/0x.js/test/event_watcher_test.ts
@@ -17,110 +17,110 @@ chaiSetup.configure();
const expect = chai.expect;
describe('EventWatcher', () => {
- let web3: Web3;
- let stubs: Sinon.SinonStub[] = [];
- let eventWatcher: EventWatcher;
- let web3Wrapper: Web3Wrapper;
- const logA: Web3.LogEntry = {
- address: '0x71d271f8b14adef568f8f28f1587ce7271ac4ca5',
- blockHash: null,
- blockNumber: null,
- data: '',
- logIndex: null,
- topics: [],
- transactionHash: '0x004881d38cd4a8f72f1a0d68c8b9b8124504706041ff37019c1d1ed6bfda8e17',
- transactionIndex: 0,
- };
- const logB: Web3.LogEntry = {
- address: '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819',
- blockHash: null,
- blockNumber: null,
- data: '',
- logIndex: null,
- topics: ['0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567'],
- transactionHash: '0x01ef3c048b18d9b09ea195b4ed94cf8dd5f3d857a1905ff886b152cfb1166f25',
- transactionIndex: 0,
- };
- const logC: Web3.LogEntry = {
- address: '0x1d271f8b174adef58f1587ce68f8f27271ac4ca5',
- blockHash: null,
- blockNumber: null,
- data: '',
- logIndex: null,
- topics: ['0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567'],
- transactionHash: '0x01ef3c048b18d9b09ea195b4ed94cf8dd5f3d857a1905ff886b152cfb1166f25',
- transactionIndex: 0,
- };
- before(async () => {
- web3 = web3Factory.create();
- const pollingIntervalMs = 10;
- web3Wrapper = new Web3Wrapper(web3.currentProvider);
- eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalMs);
- });
- afterEach(() => {
- // clean up any stubs after the test has completed
- _.each(stubs, s => s.restore());
- stubs = [];
- eventWatcher.unsubscribe();
- });
- it('correctly emits initial log events', (done: DoneCallback) => {
- const logs: Web3.LogEntry[] = [logA, logB];
- const expectedLogEvents = [
- {
- removed: false,
- ...logA,
- },
- {
- removed: false,
- ...logB,
- },
- ];
- const getLogsStub = Sinon.stub(web3Wrapper, 'getLogsAsync');
- getLogsStub.onCall(0).returns(logs);
- stubs.push(getLogsStub);
- const expectedToBeCalledOnce = false;
- const callback = reportNodeCallbackErrors(done, expectedToBeCalledOnce)((event: LogEvent) => {
- const expectedLogEvent = expectedLogEvents.shift();
- expect(event).to.be.deep.equal(expectedLogEvent);
- if (_.isEmpty(expectedLogEvents)) {
- done();
- }
- });
- eventWatcher.subscribe(callback);
- });
- it('correctly computes the difference and emits only changes', (done: DoneCallback) => {
- const initialLogs: Web3.LogEntry[] = [logA, logB];
- const changedLogs: Web3.LogEntry[] = [logA, logC];
- const expectedLogEvents = [
- {
- removed: false,
- ...logA,
- },
- {
- removed: false,
- ...logB,
- },
- {
- removed: true,
- ...logB,
- },
- {
- removed: false,
- ...logC,
- },
- ];
- const getLogsStub = Sinon.stub(web3Wrapper, 'getLogsAsync');
- getLogsStub.onCall(0).returns(initialLogs);
- getLogsStub.onCall(1).returns(changedLogs);
- stubs.push(getLogsStub);
- const expectedToBeCalledOnce = false;
- const callback = reportNodeCallbackErrors(done, expectedToBeCalledOnce)((event: LogEvent) => {
- const expectedLogEvent = expectedLogEvents.shift();
- expect(event).to.be.deep.equal(expectedLogEvent);
- if (_.isEmpty(expectedLogEvents)) {
- done();
- }
- });
- eventWatcher.subscribe(callback);
- });
+ let web3: Web3;
+ let stubs: Sinon.SinonStub[] = [];
+ let eventWatcher: EventWatcher;
+ let web3Wrapper: Web3Wrapper;
+ const logA: Web3.LogEntry = {
+ address: '0x71d271f8b14adef568f8f28f1587ce7271ac4ca5',
+ blockHash: null,
+ blockNumber: null,
+ data: '',
+ logIndex: null,
+ topics: [],
+ transactionHash: '0x004881d38cd4a8f72f1a0d68c8b9b8124504706041ff37019c1d1ed6bfda8e17',
+ transactionIndex: 0,
+ };
+ const logB: Web3.LogEntry = {
+ address: '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819',
+ blockHash: null,
+ blockNumber: null,
+ data: '',
+ logIndex: null,
+ topics: ['0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567'],
+ transactionHash: '0x01ef3c048b18d9b09ea195b4ed94cf8dd5f3d857a1905ff886b152cfb1166f25',
+ transactionIndex: 0,
+ };
+ const logC: Web3.LogEntry = {
+ address: '0x1d271f8b174adef58f1587ce68f8f27271ac4ca5',
+ blockHash: null,
+ blockNumber: null,
+ data: '',
+ logIndex: null,
+ topics: ['0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567'],
+ transactionHash: '0x01ef3c048b18d9b09ea195b4ed94cf8dd5f3d857a1905ff886b152cfb1166f25',
+ transactionIndex: 0,
+ };
+ before(async () => {
+ web3 = web3Factory.create();
+ const pollingIntervalMs = 10;
+ web3Wrapper = new Web3Wrapper(web3.currentProvider);
+ eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalMs);
+ });
+ afterEach(() => {
+ // clean up any stubs after the test has completed
+ _.each(stubs, s => s.restore());
+ stubs = [];
+ eventWatcher.unsubscribe();
+ });
+ it('correctly emits initial log events', (done: DoneCallback) => {
+ const logs: Web3.LogEntry[] = [logA, logB];
+ const expectedLogEvents = [
+ {
+ removed: false,
+ ...logA,
+ },
+ {
+ removed: false,
+ ...logB,
+ },
+ ];
+ const getLogsStub = Sinon.stub(web3Wrapper, 'getLogsAsync');
+ getLogsStub.onCall(0).returns(logs);
+ stubs.push(getLogsStub);
+ const expectedToBeCalledOnce = false;
+ const callback = reportNodeCallbackErrors(done, expectedToBeCalledOnce)((event: LogEvent) => {
+ const expectedLogEvent = expectedLogEvents.shift();
+ expect(event).to.be.deep.equal(expectedLogEvent);
+ if (_.isEmpty(expectedLogEvents)) {
+ done();
+ }
+ });
+ eventWatcher.subscribe(callback);
+ });
+ it('correctly computes the difference and emits only changes', (done: DoneCallback) => {
+ const initialLogs: Web3.LogEntry[] = [logA, logB];
+ const changedLogs: Web3.LogEntry[] = [logA, logC];
+ const expectedLogEvents = [
+ {
+ removed: false,
+ ...logA,
+ },
+ {
+ removed: false,
+ ...logB,
+ },
+ {
+ removed: true,
+ ...logB,
+ },
+ {
+ removed: false,
+ ...logC,
+ },
+ ];
+ const getLogsStub = Sinon.stub(web3Wrapper, 'getLogsAsync');
+ getLogsStub.onCall(0).returns(initialLogs);
+ getLogsStub.onCall(1).returns(changedLogs);
+ stubs.push(getLogsStub);
+ const expectedToBeCalledOnce = false;
+ const callback = reportNodeCallbackErrors(done, expectedToBeCalledOnce)((event: LogEvent) => {
+ const expectedLogEvent = expectedLogEvents.shift();
+ expect(event).to.be.deep.equal(expectedLogEvent);
+ if (_.isEmpty(expectedLogEvents)) {
+ done();
+ }
+ });
+ eventWatcher.subscribe(callback);
+ });
});
diff --git a/packages/0x.js/test/exchange_transfer_simulator_test.ts b/packages/0x.js/test/exchange_transfer_simulator_test.ts
index 20b4a05ca..9f4d76933 100644
--- a/packages/0x.js/test/exchange_transfer_simulator_test.ts
+++ b/packages/0x.js/test/exchange_transfer_simulator_test.ts
@@ -15,103 +15,103 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
describe('ExchangeTransferSimulator', () => {
- const web3 = web3Factory.create();
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3.currentProvider, config);
- const transferAmount = new BigNumber(5);
- let userAddresses: string[];
- let tokens: Token[];
- let coinbase: string;
- let sender: string;
- let recipient: string;
- let exampleTokenAddress: string;
- let exchangeTransferSimulator: ExchangeTransferSimulator;
- let txHash: string;
- before(async () => {
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- [coinbase, sender, recipient] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- exampleTokenAddress = tokens[0].address;
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#transferFromAsync', () => {
- beforeEach(() => {
- exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest);
- });
- it("throws if the user doesn't have enough allowance", async () => {
- return expect(
- exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Taker,
- TransferType.Trade,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance);
- });
- it("throws if the user doesn't have enough balance", async () => {
- txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- return expect(
- exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Maker,
- TransferType.Trade,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
- });
- it('updates balances and proxyAllowance after transfer', async () => {
- txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- await exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Taker,
- TransferType.Trade,
- );
- const store = (exchangeTransferSimulator as any)._store;
- const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
- const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
- const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
- expect(senderBalance).to.be.bignumber.equal(0);
- expect(recipientBalance).to.be.bignumber.equal(transferAmount);
- expect(senderProxyAllowance).to.be.bignumber.equal(0);
- });
- it("doesn't update proxyAllowance after transfer if unlimited", async () => {
- txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- await exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Taker,
- TransferType.Trade,
- );
- const store = (exchangeTransferSimulator as any)._store;
- const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
- const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
- const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
- expect(senderBalance).to.be.bignumber.equal(0);
- expect(recipientBalance).to.be.bignumber.equal(transferAmount);
- expect(senderProxyAllowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- });
+ const web3 = web3Factory.create();
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3.currentProvider, config);
+ const transferAmount = new BigNumber(5);
+ let userAddresses: string[];
+ let tokens: Token[];
+ let coinbase: string;
+ let sender: string;
+ let recipient: string;
+ let exampleTokenAddress: string;
+ let exchangeTransferSimulator: ExchangeTransferSimulator;
+ let txHash: string;
+ before(async () => {
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ [coinbase, sender, recipient] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ exampleTokenAddress = tokens[0].address;
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#transferFromAsync', () => {
+ beforeEach(() => {
+ exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest);
+ });
+ it("throws if the user doesn't have enough allowance", async () => {
+ return expect(
+ exchangeTransferSimulator.transferFromAsync(
+ exampleTokenAddress,
+ sender,
+ recipient,
+ transferAmount,
+ TradeSide.Taker,
+ TransferType.Trade,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance);
+ });
+ it("throws if the user doesn't have enough balance", async () => {
+ txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ return expect(
+ exchangeTransferSimulator.transferFromAsync(
+ exampleTokenAddress,
+ sender,
+ recipient,
+ transferAmount,
+ TradeSide.Maker,
+ TransferType.Trade,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
+ });
+ it('updates balances and proxyAllowance after transfer', async () => {
+ txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ await exchangeTransferSimulator.transferFromAsync(
+ exampleTokenAddress,
+ sender,
+ recipient,
+ transferAmount,
+ TradeSide.Taker,
+ TransferType.Trade,
+ );
+ const store = (exchangeTransferSimulator as any)._store;
+ const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
+ const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
+ const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
+ expect(senderBalance).to.be.bignumber.equal(0);
+ expect(recipientBalance).to.be.bignumber.equal(transferAmount);
+ expect(senderProxyAllowance).to.be.bignumber.equal(0);
+ });
+ it("doesn't update proxyAllowance after transfer if unlimited", async () => {
+ txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ await exchangeTransferSimulator.transferFromAsync(
+ exampleTokenAddress,
+ sender,
+ recipient,
+ transferAmount,
+ TradeSide.Taker,
+ TransferType.Trade,
+ );
+ const store = (exchangeTransferSimulator as any)._store;
+ const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
+ const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
+ const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
+ expect(senderBalance).to.be.bignumber.equal(0);
+ expect(recipientBalance).to.be.bignumber.equal(transferAmount);
+ expect(senderProxyAllowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ });
});
diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts
index 7e0ffd818..f67e61a55 100644
--- a/packages/0x.js/test/exchange_wrapper_test.ts
+++ b/packages/0x.js/test/exchange_wrapper_test.ts
@@ -6,17 +6,17 @@ import 'mocha';
import * as Web3 from 'web3';
import {
- BlockRange,
- DecodedLogEvent,
- ExchangeContractErrs,
- ExchangeEvents,
- LogCancelContractEventArgs,
- LogFillContractEventArgs,
- OrderCancellationRequest,
- OrderFillRequest,
- SignedOrder,
- Token,
- ZeroEx,
+ BlockRange,
+ DecodedLogEvent,
+ ExchangeContractErrs,
+ ExchangeEvents,
+ LogCancelContractEventArgs,
+ LogFillContractEventArgs,
+ OrderCancellationRequest,
+ OrderFillRequest,
+ SignedOrder,
+ Token,
+ ZeroEx,
} from '../src';
import { BlockParamLiteral, DoneCallback } from '../src/types';
@@ -34,1114 +34,1114 @@ const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
const NON_EXISTENT_ORDER_HASH = '0x79370342234e7acd6bbeac335bd3bb1d368383294b64b8160a00f4060e4d3777';
describe('ExchangeWrapper', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let tokenUtils: TokenUtils;
- let tokens: Token[];
- let userAddresses: string[];
- let zrxTokenAddress: string;
- let fillScenarios: FillScenarios;
- let exchangeContractAddress: string;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- exchangeContractAddress = zeroEx.exchange.getContractAddress();
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- await fillScenarios.initTokenBalancesAsync();
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('fillOrKill order(s)', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const takerTokenFillAmount = new BigNumber(5);
- before(async () => {
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- describe('#batchFillOrKillAsync', () => {
- it('successfully batch fillOrKill', async () => {
- const fillableAmount = new BigNumber(5);
- const partialFillTakerAmount = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const orderFillRequests = [
- {
- signedOrder,
- takerTokenFillAmount: partialFillTakerAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount: partialFillTakerAmount,
- },
- ];
- await zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress);
- });
- describe('order transaction options', () => {
- let signedOrder: SignedOrder;
- let orderFillRequests: OrderFillRequest[];
- const fillableAmount = new BigNumber(5);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- orderFillRequests = [
- {
- signedOrder,
- takerTokenFillAmount: new BigNumber(0),
- },
- ];
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- });
- describe('#fillOrKillOrderAsync', () => {
- let signedOrder: SignedOrder;
- const fillableAmount = new BigNumber(5);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- describe('successful fills', () => {
- it('should fill a valid order', async () => {
- 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,
- );
- await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(takerTokenFillAmount),
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
- takerTokenFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
- takerTokenFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(takerTokenFillAmount),
- );
- });
- it('should partially fill a valid order', async () => {
- const partialFillAmount = new BigNumber(3);
- await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, takerAddress);
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(partialFillAmount),
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
- partialFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
- partialFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(partialFillAmount),
- );
- });
- });
- describe('order transaction options', () => {
- const emptyFillableAmount = new BigNumber(0);
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- });
- });
- describe('fill order(s)', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const fillableAmount = new BigNumber(5);
- const takerTokenFillAmount = new BigNumber(5);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- before(async () => {
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- describe('#fillOrderAsync', () => {
- describe('successful fills', () => {
- it('should fill a valid order', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- 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,
- );
- const txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(takerTokenFillAmount),
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
- takerTokenFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
- takerTokenFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(takerTokenFillAmount),
- );
- });
- it('should partially fill the valid order', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const partialFillAmount = new BigNumber(3);
- const txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- partialFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(partialFillAmount),
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
- partialFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
- partialFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(partialFillAmount),
- );
- });
- it('should fill the valid orders with fees', async () => {
- const makerFee = new BigNumber(1);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- feeRecipient,
- );
- const txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient)).to.be.bignumber.equal(
- makerFee.plus(takerFee),
- );
- });
- });
- describe('order transaction options', () => {
- let signedOrder: SignedOrder;
- const emptyFillTakerAmount = new BigNumber(0);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.fillOrderAsync(
- signedOrder,
- emptyFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrderAsync(
- signedOrder,
- emptyFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: true,
- },
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrderAsync(
- signedOrder,
- emptyFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: false,
- },
- ),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- describe('negative fill amount', async () => {
- let signedOrder: SignedOrder;
- const negativeFillTakerAmount = new BigNumber(-100);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- it('should not allow the exchange wrapper to fill if amount is negative', async () => {
- return expect(
- zeroEx.exchange.fillOrderAsync(
- signedOrder,
- negativeFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejected();
- });
- });
- });
- describe('#batchFillOrdersAsync', () => {
- let signedOrder: SignedOrder;
- let signedOrderHashHex: string;
- let anotherSignedOrder: SignedOrder;
- let anotherOrderHashHex: string;
- let orderFillBatch: OrderFillRequest[];
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
- anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
- });
- describe('successful batch fills', () => {
- beforeEach(() => {
- orderFillBatch = [
- {
- signedOrder,
- takerTokenFillAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount,
- },
- ];
- });
- it('should throw if a batch is empty', async () => {
- return expect(
- zeroEx.exchange.batchFillOrdersAsync(
- [],
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- });
- it('should successfully fill multiple orders', async () => {
- const txHash = await zeroEx.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
- const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
- expect(filledAmount).to.be.bignumber.equal(takerTokenFillAmount);
- expect(anotherFilledAmount).to.be.bignumber.equal(takerTokenFillAmount);
- });
- });
- describe('order transaction options', () => {
- beforeEach(async () => {
- const emptyFillTakerAmount = new BigNumber(0);
- orderFillBatch = [
- {
- signedOrder,
- takerTokenFillAmount: emptyFillTakerAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount: emptyFillTakerAmount,
- },
- ];
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: true,
- },
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: false,
- },
- ),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- describe('negative batch fill amount', async () => {
- beforeEach(async () => {
- const negativeFillTakerAmount = new BigNumber(-100);
- orderFillBatch = [
- {
- signedOrder,
- takerTokenFillAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount: negativeFillTakerAmount,
- },
- ];
- });
- it('should not allow the exchange wrapper to batch fill if any amount is negative', async () => {
- return expect(
- zeroEx.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejected();
- });
- });
- });
- describe('#fillOrdersUpTo', () => {
- let signedOrder: SignedOrder;
- let signedOrderHashHex: string;
- let anotherSignedOrder: SignedOrder;
- let anotherOrderHashHex: string;
- let signedOrders: SignedOrder[];
- const fillUpToAmount = fillableAmount.plus(fillableAmount).minus(1);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
- anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
- signedOrders = [signedOrder, anotherSignedOrder];
- });
- describe('successful batch fills', () => {
- it('should throw if a batch is empty', async () => {
- return expect(
- zeroEx.exchange.fillOrdersUpToAsync(
- [],
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- });
- it('should successfully fill up to specified amount when all orders are fully funded', async () => {
- const txHash = await zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
- const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
- expect(filledAmount).to.be.bignumber.equal(fillableAmount);
- const remainingFillAmount = fillableAmount.minus(1);
- expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
- });
- it('should successfully fill up to specified amount even if filling all orders would fail', async () => {
- const missingBalance = new BigNumber(1); // User will still have enough balance to fill up to 9,
- // but won't have 10 to fully fill all orders in a batch.
- await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinbase, missingBalance);
- const txHash = await zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
- const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
- expect(filledAmount).to.be.bignumber.equal(fillableAmount);
- const remainingFillAmount = fillableAmount.minus(1);
- expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
- });
- });
- describe('failed batch fills', () => {
- it("should fail validation if user doesn't have enough balance without fill up to", async () => {
- const missingBalance = new BigNumber(2); // User will only have enough balance to fill up to 8
- await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinbase, missingBalance);
- return expect(
- zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
- });
- });
- describe('order transaction options', () => {
- const emptyFillUpToAmount = new BigNumber(0);
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- emptyFillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- emptyFillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: true,
- },
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- emptyFillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: false,
- },
- ),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- });
- });
- describe('cancel order(s)', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- const fillableAmount = new BigNumber(5);
- let signedOrder: SignedOrder;
- let orderHashHex: string;
- const cancelAmount = new BigNumber(3);
- beforeEach(async () => {
- [coinbase, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- orderHashHex = ZeroEx.getOrderHashHex(signedOrder);
- });
- describe('#cancelOrderAsync', () => {
- describe('successful cancels', () => {
- it('should cancel an order', async () => {
- const txHash = await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const cancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHashHex);
- expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
- });
- });
- describe('order transaction options', () => {
- const emptyCancelTakerTokenAmount = new BigNumber(0);
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- });
- });
- describe('#batchCancelOrdersAsync', () => {
- let anotherSignedOrder: SignedOrder;
- let anotherOrderHashHex: string;
- let cancelBatch: OrderCancellationRequest[];
- beforeEach(async () => {
- anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
- cancelBatch = [
- {
- order: signedOrder,
- takerTokenCancelAmount: cancelAmount,
- },
- {
- order: anotherSignedOrder,
- takerTokenCancelAmount: cancelAmount,
- },
- ];
- });
- describe('failed batch cancels', () => {
- it('should throw when orders have different makers', async () => {
- const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- takerAddress,
- takerAddress,
- fillableAmount,
- );
- return expect(
- zeroEx.exchange.batchCancelOrdersAsync([
- cancelBatch[0],
- {
- order: signedOrderWithDifferentMaker,
- takerTokenCancelAmount: cancelAmount,
- },
- ]),
- ).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
- });
- });
- describe('successful batch cancels', () => {
- it('should cancel a batch of orders', async () => {
- await zeroEx.exchange.batchCancelOrdersAsync(cancelBatch);
- const cancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHashHex);
- const anotherCancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(
- anotherOrderHashHex,
- );
- expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
- expect(anotherCancelledAmount).to.be.bignumber.equal(cancelAmount);
- });
- });
- describe('order transaction options', () => {
- beforeEach(async () => {
- const emptyTakerTokenCancelAmount = new BigNumber(0);
- cancelBatch = [
- {
- order: signedOrder,
- takerTokenCancelAmount: emptyTakerTokenCancelAmount,
- },
- {
- order: anotherSignedOrder,
- takerTokenCancelAmount: emptyTakerTokenCancelAmount,
- },
- ];
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch)).to.be.rejectedWith(
- ExchangeContractErrs.OrderCancelAmountZero,
- );
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- });
- });
- });
- describe('tests that require partially filled order', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let takerAddress: string;
- let fillableAmount: BigNumber;
- let partialFillAmount: BigNumber;
- let signedOrder: SignedOrder;
- let orderHash: string;
- before(() => {
- takerAddress = userAddresses[1];
- tokenUtils = new TokenUtils(tokens);
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- fillableAmount = new BigNumber(5);
- partialFillAmount = new BigNumber(2);
- signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- takerAddress,
- fillableAmount,
- partialFillAmount,
- );
- orderHash = ZeroEx.getOrderHashHex(signedOrder);
- });
- describe('#getUnavailableTakerAmountAsync', () => {
- it('should throw if passed an invalid orderHash', async () => {
- const invalidOrderHashHex = '0x123';
- return expect(zeroEx.exchange.getUnavailableTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
- });
- it('should return zero if passed a valid but non-existent orderHash', async () => {
- const unavailableValueT = await zeroEx.exchange.getUnavailableTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
- expect(unavailableValueT).to.be.bignumber.equal(0);
- });
- it('should return the unavailableValueT for a valid and partially filled orderHash', async () => {
- const unavailableValueT = await zeroEx.exchange.getUnavailableTakerAmountAsync(orderHash);
- expect(unavailableValueT).to.be.bignumber.equal(partialFillAmount);
- });
- });
- describe('#getFilledTakerAmountAsync', () => {
- it('should throw if passed an invalid orderHash', async () => {
- const invalidOrderHashHex = '0x123';
- return expect(zeroEx.exchange.getFilledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
- });
- it('should return zero if passed a valid but non-existent orderHash', async () => {
- const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
- expect(filledValueT).to.be.bignumber.equal(0);
- });
- it('should return the filledValueT for a valid and partially filled orderHash', async () => {
- const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(orderHash);
- expect(filledValueT).to.be.bignumber.equal(partialFillAmount);
- });
- });
- describe('#getCancelledTakerAmountAsync', () => {
- it('should throw if passed an invalid orderHash', async () => {
- const invalidOrderHashHex = '0x123';
- return expect(zeroEx.exchange.getCancelledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
- });
- it('should return zero if passed a valid but non-existent orderHash', async () => {
- const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
- expect(cancelledValueT).to.be.bignumber.equal(0);
- });
- it('should return the cancelledValueT for a valid and partially filled orderHash', async () => {
- const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHash);
- expect(cancelledValueT).to.be.bignumber.equal(0);
- });
- it('should return the cancelledValueT for a valid and cancelled orderHash', async () => {
- const cancelAmount = fillableAmount.minus(partialFillAmount);
- await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
- const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHash);
- expect(cancelledValueT).to.be.bignumber.equal(cancelAmount);
- });
- });
- });
- describe('#subscribe', () => {
- const indexFilterValues = {};
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let takerAddress: string;
- let makerAddress: string;
- let fillableAmount: BigNumber;
- let signedOrder: SignedOrder;
- const takerTokenFillAmountInBaseUnits = new BigNumber(1);
- const cancelTakerAmountInBaseUnits = new BigNumber(1);
- before(() => {
- [coinbase, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- fillableAmount = new BigNumber(5);
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- afterEach(async () => {
- zeroEx.exchange.unsubscribeAll();
- });
- // Hack: Mocha does not allow a test to be both async and have a `done` callback
- // Since we need to await the receipt of the event in the `subscribe` callback,
- // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
- // wrap the rest of the test in an async block
- // Source: https://github.com/mochajs/mocha/issues/2407
- it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
- },
- );
- zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- })().catch(done);
- });
- it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogCancelContractEventArgs>) => {
- expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel);
- },
- );
- zeroEx.exchange.subscribe(ExchangeEvents.LogCancel, indexFilterValues, callback);
- await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits);
- })().catch(done);
- });
- it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled);
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let tokenUtils: TokenUtils;
+ let tokens: Token[];
+ let userAddresses: string[];
+ let zrxTokenAddress: string;
+ let fillScenarios: FillScenarios;
+ let exchangeContractAddress: string;
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ exchangeContractAddress = zeroEx.exchange.getContractAddress();
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
+ await fillScenarios.initTokenBalancesAsync();
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('fillOrKill order(s)', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ let feeRecipient: string;
+ const takerTokenFillAmount = new BigNumber(5);
+ before(async () => {
+ [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ describe('#batchFillOrKillAsync', () => {
+ it('successfully batch fillOrKill', async () => {
+ const fillableAmount = new BigNumber(5);
+ const partialFillTakerAmount = new BigNumber(2);
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const orderFillRequests = [
+ {
+ signedOrder,
+ takerTokenFillAmount: partialFillTakerAmount,
+ },
+ {
+ signedOrder: anotherSignedOrder,
+ takerTokenFillAmount: partialFillTakerAmount,
+ },
+ ];
+ await zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress);
+ });
+ describe('order transaction options', () => {
+ let signedOrder: SignedOrder;
+ let orderFillRequests: OrderFillRequest[];
+ const fillableAmount = new BigNumber(5);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ orderFillRequests = [
+ {
+ signedOrder,
+ takerTokenFillAmount: new BigNumber(0),
+ },
+ ];
+ });
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
+ shouldValidate: true,
+ }),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
+ shouldValidate: false,
+ }),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ });
+ });
+ describe('#fillOrKillOrderAsync', () => {
+ let signedOrder: SignedOrder;
+ const fillableAmount = new BigNumber(5);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ describe('successful fills', () => {
+ it('should fill a valid order', async () => {
+ 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,
+ );
+ await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(takerTokenFillAmount),
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ takerTokenFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ takerTokenFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(takerTokenFillAmount),
+ );
+ });
+ it('should partially fill a valid order', async () => {
+ const partialFillAmount = new BigNumber(3);
+ await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, takerAddress);
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(partialFillAmount),
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ partialFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ partialFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(partialFillAmount),
+ );
+ });
+ });
+ describe('order transaction options', () => {
+ const emptyFillableAmount = new BigNumber(0);
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
+ shouldValidate: true,
+ }),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
+ shouldValidate: false,
+ }),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ });
+ });
+ });
+ describe('fill order(s)', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ let feeRecipient: string;
+ const fillableAmount = new BigNumber(5);
+ const takerTokenFillAmount = new BigNumber(5);
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ before(async () => {
+ [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ describe('#fillOrderAsync', () => {
+ describe('successful fills', () => {
+ it('should fill a valid order', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ 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,
+ );
+ const txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ takerTokenFillAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(takerTokenFillAmount),
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ takerTokenFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ takerTokenFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(takerTokenFillAmount),
+ );
+ });
+ it('should partially fill the valid order', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const partialFillAmount = new BigNumber(3);
+ const txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ partialFillAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(partialFillAmount),
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ partialFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ partialFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(partialFillAmount),
+ );
+ });
+ it('should fill the valid orders with fees', async () => {
+ const makerFee = new BigNumber(1);
+ const takerFee = new BigNumber(2);
+ const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ feeRecipient,
+ );
+ const txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ takerTokenFillAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient)).to.be.bignumber.equal(
+ makerFee.plus(takerFee),
+ );
+ });
+ });
+ describe('order transaction options', () => {
+ let signedOrder: SignedOrder;
+ const emptyFillTakerAmount = new BigNumber(0);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ emptyFillTakerAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ emptyFillTakerAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: true,
+ },
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ emptyFillTakerAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: false,
+ },
+ ),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ });
+ describe('negative fill amount', async () => {
+ let signedOrder: SignedOrder;
+ const negativeFillTakerAmount = new BigNumber(-100);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ it('should not allow the exchange wrapper to fill if amount is negative', async () => {
+ return expect(
+ zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ negativeFillTakerAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejected();
+ });
+ });
+ });
+ describe('#batchFillOrdersAsync', () => {
+ let signedOrder: SignedOrder;
+ let signedOrderHashHex: string;
+ let anotherSignedOrder: SignedOrder;
+ let anotherOrderHashHex: string;
+ let orderFillBatch: OrderFillRequest[];
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
+ anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
+ });
+ describe('successful batch fills', () => {
+ beforeEach(() => {
+ orderFillBatch = [
+ {
+ signedOrder,
+ takerTokenFillAmount,
+ },
+ {
+ signedOrder: anotherSignedOrder,
+ takerTokenFillAmount,
+ },
+ ];
+ });
+ it('should throw if a batch is empty', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ [],
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ });
+ it('should successfully fill multiple orders', async () => {
+ const txHash = await zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
+ const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
+ expect(filledAmount).to.be.bignumber.equal(takerTokenFillAmount);
+ expect(anotherFilledAmount).to.be.bignumber.equal(takerTokenFillAmount);
+ });
+ });
+ describe('order transaction options', () => {
+ beforeEach(async () => {
+ const emptyFillTakerAmount = new BigNumber(0);
+ orderFillBatch = [
+ {
+ signedOrder,
+ takerTokenFillAmount: emptyFillTakerAmount,
+ },
+ {
+ signedOrder: anotherSignedOrder,
+ takerTokenFillAmount: emptyFillTakerAmount,
+ },
+ ];
+ });
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: true,
+ },
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: false,
+ },
+ ),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ });
+ describe('negative batch fill amount', async () => {
+ beforeEach(async () => {
+ const negativeFillTakerAmount = new BigNumber(-100);
+ orderFillBatch = [
+ {
+ signedOrder,
+ takerTokenFillAmount,
+ },
+ {
+ signedOrder: anotherSignedOrder,
+ takerTokenFillAmount: negativeFillTakerAmount,
+ },
+ ];
+ });
+ it('should not allow the exchange wrapper to batch fill if any amount is negative', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejected();
+ });
+ });
+ });
+ describe('#fillOrdersUpTo', () => {
+ let signedOrder: SignedOrder;
+ let signedOrderHashHex: string;
+ let anotherSignedOrder: SignedOrder;
+ let anotherOrderHashHex: string;
+ let signedOrders: SignedOrder[];
+ const fillUpToAmount = fillableAmount.plus(fillableAmount).minus(1);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
+ anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
+ signedOrders = [signedOrder, anotherSignedOrder];
+ });
+ describe('successful batch fills', () => {
+ it('should throw if a batch is empty', async () => {
+ return expect(
+ zeroEx.exchange.fillOrdersUpToAsync(
+ [],
+ fillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ });
+ it('should successfully fill up to specified amount when all orders are fully funded', async () => {
+ const txHash = await zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ fillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
+ const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
+ expect(filledAmount).to.be.bignumber.equal(fillableAmount);
+ const remainingFillAmount = fillableAmount.minus(1);
+ expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
+ });
+ it('should successfully fill up to specified amount even if filling all orders would fail', async () => {
+ const missingBalance = new BigNumber(1); // User will still have enough balance to fill up to 9,
+ // but won't have 10 to fully fill all orders in a batch.
+ await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinbase, missingBalance);
+ const txHash = await zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ fillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
+ const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
+ expect(filledAmount).to.be.bignumber.equal(fillableAmount);
+ const remainingFillAmount = fillableAmount.minus(1);
+ expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
+ });
+ });
+ describe('failed batch fills', () => {
+ it("should fail validation if user doesn't have enough balance without fill up to", async () => {
+ const missingBalance = new BigNumber(2); // User will only have enough balance to fill up to 8
+ await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinbase, missingBalance);
+ return expect(
+ zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ fillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
+ });
+ });
+ describe('order transaction options', () => {
+ const emptyFillUpToAmount = new BigNumber(0);
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ emptyFillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ emptyFillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: true,
+ },
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ emptyFillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: false,
+ },
+ ),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ });
+ });
+ });
+ describe('cancel order(s)', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ const fillableAmount = new BigNumber(5);
+ let signedOrder: SignedOrder;
+ let orderHashHex: string;
+ const cancelAmount = new BigNumber(3);
+ beforeEach(async () => {
+ [coinbase, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ orderHashHex = ZeroEx.getOrderHashHex(signedOrder);
+ });
+ describe('#cancelOrderAsync', () => {
+ describe('successful cancels', () => {
+ it('should cancel an order', async () => {
+ const txHash = await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const cancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHashHex);
+ expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
+ });
+ });
+ describe('order transaction options', () => {
+ const emptyCancelTakerTokenAmount = new BigNumber(0);
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
+ shouldValidate: true,
+ }),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
+ shouldValidate: false,
+ }),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ });
+ });
+ describe('#batchCancelOrdersAsync', () => {
+ let anotherSignedOrder: SignedOrder;
+ let anotherOrderHashHex: string;
+ let cancelBatch: OrderCancellationRequest[];
+ beforeEach(async () => {
+ anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
+ cancelBatch = [
+ {
+ order: signedOrder,
+ takerTokenCancelAmount: cancelAmount,
+ },
+ {
+ order: anotherSignedOrder,
+ takerTokenCancelAmount: cancelAmount,
+ },
+ ];
+ });
+ describe('failed batch cancels', () => {
+ it('should throw when orders have different makers', async () => {
+ const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ takerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ return expect(
+ zeroEx.exchange.batchCancelOrdersAsync([
+ cancelBatch[0],
+ {
+ order: signedOrderWithDifferentMaker,
+ takerTokenCancelAmount: cancelAmount,
+ },
+ ]),
+ ).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
+ });
+ });
+ describe('successful batch cancels', () => {
+ it('should cancel a batch of orders', async () => {
+ await zeroEx.exchange.batchCancelOrdersAsync(cancelBatch);
+ const cancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHashHex);
+ const anotherCancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(
+ anotherOrderHashHex,
+ );
+ expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
+ expect(anotherCancelledAmount).to.be.bignumber.equal(cancelAmount);
+ });
+ });
+ describe('order transaction options', () => {
+ beforeEach(async () => {
+ const emptyTakerTokenCancelAmount = new BigNumber(0);
+ cancelBatch = [
+ {
+ order: signedOrder,
+ takerTokenCancelAmount: emptyTakerTokenCancelAmount,
+ },
+ {
+ order: anotherSignedOrder,
+ takerTokenCancelAmount: emptyTakerTokenCancelAmount,
+ },
+ ];
+ });
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch)).to.be.rejectedWith(
+ ExchangeContractErrs.OrderCancelAmountZero,
+ );
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, {
+ shouldValidate: true,
+ }),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, {
+ shouldValidate: false,
+ }),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ });
+ });
+ });
+ describe('tests that require partially filled order', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let takerAddress: string;
+ let fillableAmount: BigNumber;
+ let partialFillAmount: BigNumber;
+ let signedOrder: SignedOrder;
+ let orderHash: string;
+ before(() => {
+ takerAddress = userAddresses[1];
+ tokenUtils = new TokenUtils(tokens);
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ beforeEach(async () => {
+ fillableAmount = new BigNumber(5);
+ partialFillAmount = new BigNumber(2);
+ signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ takerAddress,
+ fillableAmount,
+ partialFillAmount,
+ );
+ orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ });
+ describe('#getUnavailableTakerAmountAsync', () => {
+ it('should throw if passed an invalid orderHash', async () => {
+ const invalidOrderHashHex = '0x123';
+ return expect(zeroEx.exchange.getUnavailableTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
+ });
+ it('should return zero if passed a valid but non-existent orderHash', async () => {
+ const unavailableValueT = await zeroEx.exchange.getUnavailableTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
+ expect(unavailableValueT).to.be.bignumber.equal(0);
+ });
+ it('should return the unavailableValueT for a valid and partially filled orderHash', async () => {
+ const unavailableValueT = await zeroEx.exchange.getUnavailableTakerAmountAsync(orderHash);
+ expect(unavailableValueT).to.be.bignumber.equal(partialFillAmount);
+ });
+ });
+ describe('#getFilledTakerAmountAsync', () => {
+ it('should throw if passed an invalid orderHash', async () => {
+ const invalidOrderHashHex = '0x123';
+ return expect(zeroEx.exchange.getFilledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
+ });
+ it('should return zero if passed a valid but non-existent orderHash', async () => {
+ const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
+ expect(filledValueT).to.be.bignumber.equal(0);
+ });
+ it('should return the filledValueT for a valid and partially filled orderHash', async () => {
+ const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(orderHash);
+ expect(filledValueT).to.be.bignumber.equal(partialFillAmount);
+ });
+ });
+ describe('#getCancelledTakerAmountAsync', () => {
+ it('should throw if passed an invalid orderHash', async () => {
+ const invalidOrderHashHex = '0x123';
+ return expect(zeroEx.exchange.getCancelledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
+ });
+ it('should return zero if passed a valid but non-existent orderHash', async () => {
+ const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
+ expect(cancelledValueT).to.be.bignumber.equal(0);
+ });
+ it('should return the cancelledValueT for a valid and partially filled orderHash', async () => {
+ const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHash);
+ expect(cancelledValueT).to.be.bignumber.equal(0);
+ });
+ it('should return the cancelledValueT for a valid and cancelled orderHash', async () => {
+ const cancelAmount = fillableAmount.minus(partialFillAmount);
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
+ const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHash);
+ expect(cancelledValueT).to.be.bignumber.equal(cancelAmount);
+ });
+ });
+ });
+ describe('#subscribe', () => {
+ const indexFilterValues = {};
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let takerAddress: string;
+ let makerAddress: string;
+ let fillableAmount: BigNumber;
+ let signedOrder: SignedOrder;
+ const takerTokenFillAmountInBaseUnits = new BigNumber(1);
+ const cancelTakerAmountInBaseUnits = new BigNumber(1);
+ before(() => {
+ [coinbase, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ beforeEach(async () => {
+ fillableAmount = new BigNumber(5);
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ afterEach(async () => {
+ zeroEx.exchange.unsubscribeAll();
+ });
+ // Hack: Mocha does not allow a test to be both async and have a `done` callback
+ // Since we need to await the receipt of the event in the `subscribe` callback,
+ // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
+ // wrap the rest of the test in an async block
+ // Source: https://github.com/mochajs/mocha/issues/2407
+ it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
+ expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
+ },
+ );
+ zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ takerTokenFillAmountInBaseUnits,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ })().catch(done);
+ });
+ it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<LogCancelContractEventArgs>) => {
+ expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel);
+ },
+ );
+ zeroEx.exchange.subscribe(ExchangeEvents.LogCancel, indexFilterValues, callback);
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits);
+ })().catch(done);
+ });
+ it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled);
- const newProvider = web3Factory.getRpcProvider();
- zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
+ const newProvider = web3Factory.getRpcProvider();
+ zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
- },
- );
- zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- })().catch(done);
- });
- it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- const subscriptionToken = zeroEx.exchange.subscribe(
- ExchangeEvents.LogFill,
- indexFilterValues,
- callbackNeverToBeCalled,
- );
- zeroEx.exchange.unsubscribe(subscriptionToken);
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- done();
- })().catch(done);
- });
- });
- describe('#getOrderHashHexUsingContractCallAsync', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let makerAddress: string;
- let takerAddress: string;
- const fillableAmount = new BigNumber(5);
- before(async () => {
- [, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- it("get's the same hash as the local function", async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- const orderHashFromContract = await (zeroEx.exchange as any)._getOrderHashHexUsingContractCallAsync(
- signedOrder,
- );
- expect(orderHash).to.equal(orderHashFromContract);
- });
- });
- describe('#getZRXTokenAddressAsync', () => {
- it('gets the same token as is in token registry', () => {
- const zrxAddress = zeroEx.exchange.getZRXTokenAddress();
- const zrxToken = tokenUtils.getProtocolTokenOrThrow();
- expect(zrxAddress).to.equal(zrxToken.address);
- });
- });
- describe('#getLogsAsync', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let makerAddress: string;
- let takerAddress: string;
- const fillableAmount = new BigNumber(5);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- const blockRange: BlockRange = {
- fromBlock: 0,
- toBlock: BlockParamLiteral.Latest,
- };
- let txHash: string;
- before(async () => {
- [, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- it('should get logs with decoded args emitted by LogFill', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = ExchangeEvents.LogFill;
- const indexFilterValues = {};
- const logs = await zeroEx.exchange.getLogsAsync(eventName, blockRange, indexFilterValues);
- expect(logs).to.have.length(1);
- expect(logs[0].event).to.be.equal(eventName);
- });
- it('should only get the logs with the correct event name', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const differentEventName = ExchangeEvents.LogCancel;
- const indexFilterValues = {};
- const logs = await zeroEx.exchange.getLogsAsync(differentEventName, blockRange, indexFilterValues);
- expect(logs).to.have.length(0);
- });
- it('should only get the logs with the correct indexed fields', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
+ expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
+ },
+ );
+ zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ takerTokenFillAmountInBaseUnits,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ })().catch(done);
+ });
+ it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ const subscriptionToken = zeroEx.exchange.subscribe(
+ ExchangeEvents.LogFill,
+ indexFilterValues,
+ callbackNeverToBeCalled,
+ );
+ zeroEx.exchange.unsubscribe(subscriptionToken);
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ takerTokenFillAmountInBaseUnits,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ done();
+ })().catch(done);
+ });
+ });
+ describe('#getOrderHashHexUsingContractCallAsync', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ const fillableAmount = new BigNumber(5);
+ before(async () => {
+ [, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ it("get's the same hash as the local function", async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ const orderHashFromContract = await (zeroEx.exchange as any)._getOrderHashHexUsingContractCallAsync(
+ signedOrder,
+ );
+ expect(orderHash).to.equal(orderHashFromContract);
+ });
+ });
+ describe('#getZRXTokenAddressAsync', () => {
+ it('gets the same token as is in token registry', () => {
+ const zrxAddress = zeroEx.exchange.getZRXTokenAddress();
+ const zrxToken = tokenUtils.getProtocolTokenOrThrow();
+ expect(zrxAddress).to.equal(zrxToken.address);
+ });
+ });
+ describe('#getLogsAsync', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ const fillableAmount = new BigNumber(5);
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ const blockRange: BlockRange = {
+ fromBlock: 0,
+ toBlock: BlockParamLiteral.Latest,
+ };
+ let txHash: string;
+ before(async () => {
+ [, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ it('should get logs with decoded args emitted by LogFill', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillableAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const eventName = ExchangeEvents.LogFill;
+ const indexFilterValues = {};
+ const logs = await zeroEx.exchange.getLogsAsync(eventName, blockRange, indexFilterValues);
+ expect(logs).to.have.length(1);
+ expect(logs[0].event).to.be.equal(eventName);
+ });
+ it('should only get the logs with the correct event name', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillableAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const differentEventName = ExchangeEvents.LogCancel;
+ const indexFilterValues = {};
+ const logs = await zeroEx.exchange.getLogsAsync(differentEventName, blockRange, indexFilterValues);
+ expect(logs).to.have.length(0);
+ });
+ it('should only get the logs with the correct indexed fields', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillableAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
- const differentMakerAddress = userAddresses[2];
- const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- differentMakerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await zeroEx.exchange.fillOrderAsync(
- anotherSignedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
+ const differentMakerAddress = userAddresses[2];
+ const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ differentMakerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ txHash = await zeroEx.exchange.fillOrderAsync(
+ anotherSignedOrder,
+ fillableAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = ExchangeEvents.LogFill;
- const indexFilterValues = {
- maker: differentMakerAddress,
- };
- const logs = await zeroEx.exchange.getLogsAsync<LogFillContractEventArgs>(
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(args.maker).to.be.equal(differentMakerAddress);
- });
- });
+ const eventName = ExchangeEvents.LogFill;
+ const indexFilterValues = {
+ maker: differentMakerAddress,
+ };
+ const logs = await zeroEx.exchange.getLogsAsync<LogFillContractEventArgs>(
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(args.maker).to.be.equal(differentMakerAddress);
+ });
+ });
}); // tslint:disable:max-file-line-count
diff --git a/packages/0x.js/test/expiration_watcher_test.ts b/packages/0x.js/test/expiration_watcher_test.ts
index 770615f88..a76d84da1 100644
--- a/packages/0x.js/test/expiration_watcher_test.ts
+++ b/packages/0x.js/test/expiration_watcher_test.ts
@@ -24,137 +24,137 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(testConstants.RPC_URL);
describe('ExpirationWatcher', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let tokenUtils: TokenUtils;
- let tokens: Token[];
- let userAddresses: string[];
- let zrxTokenAddress: string;
- let fillScenarios: FillScenarios;
- let exchangeContractAddress: string;
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const fillableAmount = new BigNumber(5);
- let currentUnixTimestampSec: BigNumber;
- let timer: Sinon.SinonFakeTimers;
- let expirationWatcher: ExpirationWatcher;
- before(async () => {
- web3 = web3Factory.create();
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- zeroEx = new ZeroEx(web3.currentProvider, config);
- exchangeContractAddress = zeroEx.exchange.getContractAddress();
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- const sinonTimerConfig = { shouldAdvanceTime: true } as any;
- // This constructor has incorrect types
- timer = Sinon.useFakeTimers(sinonTimerConfig);
- currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
- expirationWatcher = new ExpirationWatcher();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- timer.restore();
- expirationWatcher.unsubscribe();
- });
- it('correctly emits events when order expires', (done: DoneCallback) => {
- (async () => {
- const orderLifetimeSec = 60;
- const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationUnixTimestampSec,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(1000));
- const callbackAsync = reportNoErrorCallbackErrors(done)((hash: string) => {
- expect(hash).to.be.equal(orderHash);
- expect(utils.getCurrentUnixTimestampSec()).to.be.bignumber.gte(expirationUnixTimestampSec);
- });
- expirationWatcher.subscribe(callbackAsync);
- timer.tick(orderLifetimeSec * 1000);
- })().catch(done);
- });
- it("doesn't emit events before order expires", (done: DoneCallback) => {
- (async () => {
- const orderLifetimeSec = 60;
- const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationUnixTimestampSec,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(1000));
- const callbackAsync = reportNoErrorCallbackErrors(done)(async (hash: string) => {
- done(new Error('Emitted expiration went before the order actually expired'));
- });
- expirationWatcher.subscribe(callbackAsync);
- const notEnoughTime = orderLifetimeSec - 1;
- timer.tick(notEnoughTime * 1000);
- done();
- })().catch(done);
- });
- it('emits events in correct order', (done: DoneCallback) => {
- (async () => {
- const order1Lifetime = 60;
- const order2Lifetime = 120;
- const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime);
- const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime);
- const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- order1ExpirationUnixTimestampSec,
- );
- const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- order2ExpirationUnixTimestampSec,
- );
- const orderHash1 = ZeroEx.getOrderHashHex(signedOrder1);
- const orderHash2 = ZeroEx.getOrderHashHex(signedOrder2);
- expirationWatcher.addOrder(orderHash2, signedOrder2.expirationUnixTimestampSec.times(1000));
- expirationWatcher.addOrder(orderHash1, signedOrder1.expirationUnixTimestampSec.times(1000));
- const expirationOrder = [orderHash1, orderHash2];
- const expectToBeCalledOnce = false;
- const callbackAsync = reportNoErrorCallbackErrors(done, expectToBeCalledOnce)((hash: string) => {
- const orderHash = expirationOrder.shift();
- expect(hash).to.be.equal(orderHash);
- if (_.isEmpty(expirationOrder)) {
- done();
- }
- });
- expirationWatcher.subscribe(callbackAsync);
- timer.tick(order2Lifetime * 1000);
- })().catch(done);
- });
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let tokenUtils: TokenUtils;
+ let tokens: Token[];
+ let userAddresses: string[];
+ let zrxTokenAddress: string;
+ let fillScenarios: FillScenarios;
+ let exchangeContractAddress: string;
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ let feeRecipient: string;
+ const fillableAmount = new BigNumber(5);
+ let currentUnixTimestampSec: BigNumber;
+ let timer: Sinon.SinonFakeTimers;
+ let expirationWatcher: ExpirationWatcher;
+ before(async () => {
+ web3 = web3Factory.create();
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ exchangeContractAddress = zeroEx.exchange.getContractAddress();
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
+ [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ const sinonTimerConfig = { shouldAdvanceTime: true } as any;
+ // This constructor has incorrect types
+ timer = Sinon.useFakeTimers(sinonTimerConfig);
+ currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
+ expirationWatcher = new ExpirationWatcher();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ timer.restore();
+ expirationWatcher.unsubscribe();
+ });
+ it('correctly emits events when order expires', (done: DoneCallback) => {
+ (async () => {
+ const orderLifetimeSec = 60;
+ const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ expirationUnixTimestampSec,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(1000));
+ const callbackAsync = reportNoErrorCallbackErrors(done)((hash: string) => {
+ expect(hash).to.be.equal(orderHash);
+ expect(utils.getCurrentUnixTimestampSec()).to.be.bignumber.gte(expirationUnixTimestampSec);
+ });
+ expirationWatcher.subscribe(callbackAsync);
+ timer.tick(orderLifetimeSec * 1000);
+ })().catch(done);
+ });
+ it("doesn't emit events before order expires", (done: DoneCallback) => {
+ (async () => {
+ const orderLifetimeSec = 60;
+ const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ expirationUnixTimestampSec,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(1000));
+ const callbackAsync = reportNoErrorCallbackErrors(done)(async (hash: string) => {
+ done(new Error('Emitted expiration went before the order actually expired'));
+ });
+ expirationWatcher.subscribe(callbackAsync);
+ const notEnoughTime = orderLifetimeSec - 1;
+ timer.tick(notEnoughTime * 1000);
+ done();
+ })().catch(done);
+ });
+ it('emits events in correct order', (done: DoneCallback) => {
+ (async () => {
+ const order1Lifetime = 60;
+ const order2Lifetime = 120;
+ const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime);
+ const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime);
+ const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ order1ExpirationUnixTimestampSec,
+ );
+ const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ order2ExpirationUnixTimestampSec,
+ );
+ const orderHash1 = ZeroEx.getOrderHashHex(signedOrder1);
+ const orderHash2 = ZeroEx.getOrderHashHex(signedOrder2);
+ expirationWatcher.addOrder(orderHash2, signedOrder2.expirationUnixTimestampSec.times(1000));
+ expirationWatcher.addOrder(orderHash1, signedOrder1.expirationUnixTimestampSec.times(1000));
+ const expirationOrder = [orderHash1, orderHash2];
+ const expectToBeCalledOnce = false;
+ const callbackAsync = reportNoErrorCallbackErrors(done, expectToBeCalledOnce)((hash: string) => {
+ const orderHash = expirationOrder.shift();
+ expect(hash).to.be.equal(orderHash);
+ if (_.isEmpty(expirationOrder)) {
+ done();
+ }
+ });
+ expirationWatcher.subscribe(callbackAsync);
+ timer.tick(order2Lifetime * 1000);
+ })().catch(done);
+ });
});
diff --git a/packages/0x.js/test/order_state_watcher_test.ts b/packages/0x.js/test/order_state_watcher_test.ts
index 2e9202fe2..22ed80446 100644
--- a/packages/0x.js/test/order_state_watcher_test.ts
+++ b/packages/0x.js/test/order_state_watcher_test.ts
@@ -6,14 +6,14 @@ import 'mocha';
import * as Web3 from 'web3';
import {
- ExchangeContractErrs,
- OrderState,
- OrderStateInvalid,
- OrderStateValid,
- SignedOrder,
- Token,
- ZeroEx,
- ZeroExError,
+ ExchangeContractErrs,
+ OrderState,
+ OrderStateInvalid,
+ OrderStateValid,
+ SignedOrder,
+ Token,
+ ZeroEx,
+ ZeroExError,
} from '../src';
import { DoneCallback } from '../src/types';
@@ -31,528 +31,528 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
describe('OrderStateWatcher', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let tokens: Token[];
- let tokenUtils: TokenUtils;
- let fillScenarios: FillScenarios;
- let userAddresses: string[];
- let zrxTokenAddress: string;
- let exchangeContractAddress: string;
- let makerToken: Token;
- let takerToken: Token;
- let maker: string;
- let taker: string;
- let signedOrder: SignedOrder;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const decimals = constants.ZRX_DECIMALS;
- const fillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- exchangeContractAddress = zeroEx.exchange.getContractAddress();
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- [, maker, taker] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- await fillScenarios.initTokenBalancesAsync();
- [makerToken, takerToken] = tokenUtils.getDummyTokens();
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#removeOrder', async () => {
- it('should successfully remove existing order', async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.include({
- [orderHash]: signedOrder,
- });
- let dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes;
- expect(dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress]).to.have.keys(orderHash);
- zeroEx.orderStateWatcher.removeOrder(orderHash);
- expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.not.include({
- [orderHash]: signedOrder,
- });
- dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes;
- expect(dependentOrderHashes[signedOrder.maker]).to.be.undefined();
- });
- it('should no-op when removing a non-existing order', async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- const nonExistentOrderHash = `0x${orderHash
- .substr(2)
- .split('')
- .reverse()
- .join('')}`;
- zeroEx.orderStateWatcher.removeOrder(nonExistentOrderHash);
- });
- });
- describe('#subscribe', async () => {
- afterEach(async () => {
- zeroEx.orderStateWatcher.unsubscribe();
- });
- it('should fail when trying to subscribe twice', async () => {
- zeroEx.orderStateWatcher.subscribe(_.noop);
- expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)).to.throw(ZeroExError.SubscriptionAlreadyPresent);
- });
- });
- describe('tests with cleanup', async () => {
- afterEach(async () => {
- zeroEx.orderStateWatcher.unsubscribe();
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.removeOrder(orderHash);
- });
- it('should emit orderStateInvalid when maker allowance set to 0 for watched order', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0));
- })().catch(done);
- });
- it('should not emit an orderState event when irrelevant Transfer event received', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- throw new Error('OrderState callback fired for irrelevant order');
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- const notTheMaker = userAddresses[0];
- const anyRecipient = taker;
- const transferAmount = new BigNumber(2);
- await zeroEx.token.transferAsync(makerToken.address, notTheMaker, anyRecipient, transferAmount);
- setTimeout(() => {
- done();
- }, TIMEOUT_MS);
- })().catch(done);
- });
- it('should emit orderStateInvalid when maker moves balance backing watched order', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- const anyRecipient = taker;
- const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
- await zeroEx.token.transferAsync(makerToken.address, maker, anyRecipient, makerBalance);
- })().catch(done);
- });
- it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let tokens: Token[];
+ let tokenUtils: TokenUtils;
+ let fillScenarios: FillScenarios;
+ let userAddresses: string[];
+ let zrxTokenAddress: string;
+ let exchangeContractAddress: string;
+ let makerToken: Token;
+ let takerToken: Token;
+ let maker: string;
+ let taker: string;
+ let signedOrder: SignedOrder;
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const decimals = constants.ZRX_DECIMALS;
+ const fillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ exchangeContractAddress = zeroEx.exchange.getContractAddress();
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ [, maker, taker] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
+ await fillScenarios.initTokenBalancesAsync();
+ [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#removeOrder', async () => {
+ it('should successfully remove existing order', async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.include({
+ [orderHash]: signedOrder,
+ });
+ let dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes;
+ expect(dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress]).to.have.keys(orderHash);
+ zeroEx.orderStateWatcher.removeOrder(orderHash);
+ expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.not.include({
+ [orderHash]: signedOrder,
+ });
+ dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes;
+ expect(dependentOrderHashes[signedOrder.maker]).to.be.undefined();
+ });
+ it('should no-op when removing a non-existing order', async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ const nonExistentOrderHash = `0x${orderHash
+ .substr(2)
+ .split('')
+ .reverse()
+ .join('')}`;
+ zeroEx.orderStateWatcher.removeOrder(nonExistentOrderHash);
+ });
+ });
+ describe('#subscribe', async () => {
+ afterEach(async () => {
+ zeroEx.orderStateWatcher.unsubscribe();
+ });
+ it('should fail when trying to subscribe twice', async () => {
+ zeroEx.orderStateWatcher.subscribe(_.noop);
+ expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)).to.throw(ZeroExError.SubscriptionAlreadyPresent);
+ });
+ });
+ describe('tests with cleanup', async () => {
+ afterEach(async () => {
+ zeroEx.orderStateWatcher.unsubscribe();
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.removeOrder(orderHash);
+ });
+ it('should emit orderStateInvalid when maker allowance set to 0 for watched order', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0));
+ })().catch(done);
+ });
+ it('should not emit an orderState event when irrelevant Transfer event received', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ throw new Error('OrderState callback fired for irrelevant order');
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ const notTheMaker = userAddresses[0];
+ const anyRecipient = taker;
+ const transferAmount = new BigNumber(2);
+ await zeroEx.token.transferAsync(makerToken.address, notTheMaker, anyRecipient, transferAmount);
+ setTimeout(() => {
+ done();
+ }, TIMEOUT_MS);
+ })().catch(done);
+ });
+ it('should emit orderStateInvalid when maker moves balance backing watched order', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ const anyRecipient = taker;
+ const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
+ await zeroEx.token.transferAsync(makerToken.address, maker, anyRecipient, makerBalance);
+ })().catch(done);
+ });
+ it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- taker,
- );
- })().catch(done);
- });
- it('should emit orderStateValid when watched order partially filled', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillableAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ taker,
+ );
+ })().catch(done);
+ });
+ it('should emit orderStateValid when watched order partially filled', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
- const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
- const fillAmountInBaseUnits = new BigNumber(2);
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
+ const fillAmountInBaseUnits = new BigNumber(2);
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- expect(validOrderState.orderHash).to.be.equal(orderHash);
- const orderRelevantState = validOrderState.orderRelevantState;
- const remainingMakerBalance = makerBalance.sub(fillAmountInBaseUnits);
- const remainingFillable = fillableAmount.minus(fillAmountInBaseUnits);
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- remainingFillable,
- );
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
- remainingFillable,
- );
- expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- taker,
- );
- })().catch(done);
- });
- it('should trigger the callback when orders backing ZRX allowance changes', (done: DoneCallback) => {
- (async () => {
- const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18);
- const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), 18);
- signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
- makerFee,
- takerFee,
- maker,
- taker,
- fillableAmount,
- taker,
- );
- const callback = reportNodeCallbackErrors(done)();
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, new BigNumber(0));
- })().catch(done);
- });
- describe('remainingFillable(M|T)akerTokenAmount', () => {
- it('should calculate correct remaining fillable', (done: DoneCallback) => {
- (async () => {
- const takerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(10), decimals);
- const makerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(20), decimals);
- signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- makerFillableAmount,
- takerFillableAmount,
- );
- const fillAmountInBaseUnits = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- expect(validOrderState.orderHash).to.be.equal(orderHash);
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- ZeroEx.toBaseUnitAmount(new BigNumber(16), decimals),
- );
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
- ZeroEx.toBaseUnitAmount(new BigNumber(8), decimals),
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- taker,
- );
- })().catch(done);
- });
- it('should equal approved amount when approved amount is lowest', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.true();
+ const validOrderState = orderState as OrderStateValid;
+ expect(validOrderState.orderHash).to.be.equal(orderHash);
+ const orderRelevantState = validOrderState.orderRelevantState;
+ const remainingMakerBalance = makerBalance.sub(fillAmountInBaseUnits);
+ const remainingFillable = fillableAmount.minus(fillAmountInBaseUnits);
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ remainingFillable,
+ );
+ expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ remainingFillable,
+ );
+ expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillAmountInBaseUnits,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ taker,
+ );
+ })().catch(done);
+ });
+ it('should trigger the callback when orders backing ZRX allowance changes', (done: DoneCallback) => {
+ (async () => {
+ const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18);
+ const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), 18);
+ signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerToken.address,
+ takerToken.address,
+ makerFee,
+ takerFee,
+ maker,
+ taker,
+ fillableAmount,
+ taker,
+ );
+ const callback = reportNodeCallbackErrors(done)();
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, new BigNumber(0));
+ })().catch(done);
+ });
+ describe('remainingFillable(M|T)akerTokenAmount', () => {
+ it('should calculate correct remaining fillable', (done: DoneCallback) => {
+ (async () => {
+ const takerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(10), decimals);
+ const makerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(20), decimals);
+ signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ makerFillableAmount,
+ takerFillableAmount,
+ );
+ const fillAmountInBaseUnits = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.true();
+ const validOrderState = orderState as OrderStateValid;
+ expect(validOrderState.orderHash).to.be.equal(orderHash);
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ ZeroEx.toBaseUnitAmount(new BigNumber(16), decimals),
+ );
+ expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ ZeroEx.toBaseUnitAmount(new BigNumber(8), decimals),
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillAmountInBaseUnits,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ taker,
+ );
+ })().catch(done);
+ });
+ it('should equal approved amount when approved amount is lowest', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
- const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- changedMakerApprovalAmount,
- );
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
- changedMakerApprovalAmount,
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, changedMakerApprovalAmount);
- })().catch(done);
- });
- it('should equal balance amount when balance amount is lowest', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ const validOrderState = orderState as OrderStateValid;
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ changedMakerApprovalAmount,
+ );
+ expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ changedMakerApprovalAmount,
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, changedMakerApprovalAmount);
+ })().catch(done);
+ });
+ it('should equal balance amount when balance amount is lowest', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ 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);
- const remainingAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
- const transferAmount = makerBalance.sub(remainingAmount);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const remainingAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
+ const transferAmount = makerBalance.sub(remainingAmount);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- remainingAmount,
- );
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
- remainingAmount,
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.transferAsync(makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount);
- })().catch(done);
- });
- it('should equal remaining amount when partially cancelled and order has fees', (done: DoneCallback) => {
- (async () => {
- const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
- const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
- const feeRecipient = taker;
- signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
- makerFee,
- takerFee,
- maker,
- taker,
- fillableAmount,
- feeRecipient,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.true();
+ const validOrderState = orderState as OrderStateValid;
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ remainingAmount,
+ );
+ expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ remainingAmount,
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.transferAsync(makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount);
+ })().catch(done);
+ });
+ it('should equal remaining amount when partially cancelled and order has fees', (done: DoneCallback) => {
+ (async () => {
+ const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
+ const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
+ const feeRecipient = taker;
+ signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerToken.address,
+ takerToken.address,
+ makerFee,
+ takerFee,
+ maker,
+ taker,
+ fillableAmount,
+ feeRecipient,
+ );
- const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
- const transferTokenAmount = makerFee.sub(remainingTokenAmount);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
+ const transferTokenAmount = makerFee.sub(remainingTokenAmount);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- remainingTokenAmount,
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.exchange.cancelOrderAsync(signedOrder, transferTokenAmount);
- })().catch(done);
- });
- it('should equal ratio amount when fee balance is lowered', (done: DoneCallback) => {
- (async () => {
- const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
- const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
- const feeRecipient = taker;
- signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
- makerFee,
- takerFee,
- maker,
- taker,
- fillableAmount,
- feeRecipient,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.true();
+ const validOrderState = orderState as OrderStateValid;
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ remainingTokenAmount,
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, transferTokenAmount);
+ })().catch(done);
+ });
+ it('should equal ratio amount when fee balance is lowered', (done: DoneCallback) => {
+ (async () => {
+ const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
+ const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
+ const feeRecipient = taker;
+ signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerToken.address,
+ takerToken.address,
+ makerFee,
+ takerFee,
+ maker,
+ taker,
+ fillableAmount,
+ feeRecipient,
+ );
- const remainingFeeAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
+ const remainingFeeAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
- const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
- const transferTokenAmount = makerFee.sub(remainingTokenAmount);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
+ const transferTokenAmount = makerFee.sub(remainingTokenAmount);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- remainingFeeAmount,
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, remainingFeeAmount);
- await zeroEx.token.transferAsync(
- makerToken.address,
- maker,
- ZeroEx.NULL_ADDRESS,
- transferTokenAmount,
- );
- })().catch(done);
- });
- it('should calculate full amount when all available and non-divisible', (done: DoneCallback) => {
- (async () => {
- const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
- const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- const feeRecipient = taker;
- signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
- makerFee,
- takerFee,
- maker,
- taker,
- fillableAmount,
- feeRecipient,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ const validOrderState = orderState as OrderStateValid;
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ remainingFeeAmount,
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, remainingFeeAmount);
+ await zeroEx.token.transferAsync(
+ makerToken.address,
+ maker,
+ ZeroEx.NULL_ADDRESS,
+ transferTokenAmount,
+ );
+ })().catch(done);
+ });
+ it('should calculate full amount when all available and non-divisible', (done: DoneCallback) => {
+ (async () => {
+ const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
+ const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ const feeRecipient = taker;
+ signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerToken.address,
+ takerToken.address,
+ makerFee,
+ takerFee,
+ maker,
+ taker,
+ fillableAmount,
+ feeRecipient,
+ );
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- fillableAmount,
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.setProxyAllowanceAsync(
- makerToken.address,
- maker,
- ZeroEx.toBaseUnitAmount(new BigNumber(100), decimals),
- );
- })().catch(done);
- });
- });
- it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ const validOrderState = orderState as OrderStateValid;
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ fillableAmount,
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.setProxyAllowanceAsync(
+ makerToken.address,
+ maker,
+ ZeroEx.toBaseUnitAmount(new BigNumber(100), decimals),
+ );
+ })().catch(done);
+ });
+ });
+ it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- })().catch(done);
- });
- it('should emit orderStateInvalid when within rounding error range', (done: DoneCallback) => {
- (async () => {
- const remainingFillableAmountInBaseUnits = new BigNumber(100);
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
+ })().catch(done);
+ });
+ it('should emit orderStateInvalid when within rounding error range', (done: DoneCallback) => {
+ (async () => {
+ const remainingFillableAmountInBaseUnits = new BigNumber(100);
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderFillRoundingError);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.exchange.cancelOrderAsync(
- signedOrder,
- fillableAmount.minus(remainingFillableAmountInBaseUnits),
- );
- })().catch(done);
- });
- it('should emit orderStateValid when watched order partially cancelled', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderFillRoundingError);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.exchange.cancelOrderAsync(
+ signedOrder,
+ fillableAmount.minus(remainingFillableAmountInBaseUnits),
+ );
+ })().catch(done);
+ });
+ it('should emit orderStateValid when watched order partially cancelled', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
- const cancelAmountInBaseUnits = new BigNumber(2);
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const cancelAmountInBaseUnits = new BigNumber(2);
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- expect(validOrderState.orderHash).to.be.equal(orderHash);
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.cancelledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits);
- })().catch(done);
- });
- });
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.true();
+ const validOrderState = orderState as OrderStateValid;
+ expect(validOrderState.orderHash).to.be.equal(orderHash);
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.cancelledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits);
+ })().catch(done);
+ });
+ });
}); // tslint:disable:max-file-line-count
diff --git a/packages/0x.js/test/order_validation_test.ts b/packages/0x.js/test/order_validation_test.ts
index be3e0590c..4e023a9cc 100644
--- a/packages/0x.js/test/order_validation_test.ts
+++ b/packages/0x.js/test/order_validation_test.ts
@@ -20,455 +20,455 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
describe('OrderValidation', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let userAddresses: string[];
- let tokens: Token[];
- let tokenUtils: TokenUtils;
- let exchangeContractAddress: string;
- let zrxTokenAddress: string;
- let fillScenarios: FillScenarios;
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const fillableAmount = new BigNumber(5);
- const fillTakerAmount = new BigNumber(5);
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- exchangeContractAddress = zeroEx.exchange.getContractAddress();
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('validateOrderFillableOrThrowAsync', () => {
- it('should succeed if the order is fillable', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
- });
- it('should succeed if the order is asymmetric and fillable', async () => {
- const makerFillableAmount = fillableAmount;
- const takerFillableAmount = fillableAmount.minus(4);
- const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- makerFillableAmount,
- takerFillableAmount,
- );
- await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
- });
- it('should throw when the order is fully filled or cancelled', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
- ExchangeContractErrs.OrderRemainingFillAmountZero,
- );
- });
- it('should throw when order is expired', async () => {
- const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationInPast,
- );
- return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
- ExchangeContractErrs.OrderFillExpired,
- );
- });
- });
- describe('validateFillOrderAndThrowIfInvalidAsync', () => {
- it('should throw when the fill amount is zero', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const zeroFillAmount = new BigNumber(0);
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, zeroFillAmount, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should throw when the signature is invalid', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- // 27 <--> 28
- signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27;
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
- ).to.be.rejectedWith(ZeroExError.InvalidSignature);
- });
- it('should throw when the order is fully filled or cancelled', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero);
- });
- it('should throw when sender is not a taker', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const nonTakerAddress = userAddresses[6];
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, nonTakerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
- });
- it('should throw when order is expired', async () => {
- const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationInPast,
- );
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired);
- });
- it('should throw when there a rounding error would have occurred', async () => {
- const makerAmount = new BigNumber(3);
- const takerAmount = new BigNumber(5);
- const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- makerAmount,
- takerAmount,
- );
- const fillTakerAmountThatCausesRoundingError = new BigNumber(3);
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
- signedOrder,
- fillTakerAmountThatCausesRoundingError,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError);
- });
- });
- describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => {
- it('should throw if remaining fillAmount is less then the desired fillAmount', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const tooLargeFillAmount = new BigNumber(7);
- const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount);
- await zeroEx.token.transferAsync(takerTokenAddress, coinbase, takerAddress, fillAmountDifference);
- await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, tooLargeFillAmount);
- await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference);
- await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount);
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let userAddresses: string[];
+ let tokens: Token[];
+ let tokenUtils: TokenUtils;
+ let exchangeContractAddress: string;
+ let zrxTokenAddress: string;
+ let fillScenarios: FillScenarios;
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ let feeRecipient: string;
+ const fillableAmount = new BigNumber(5);
+ const fillTakerAmount = new BigNumber(5);
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ exchangeContractAddress = zeroEx.exchange.getContractAddress();
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('validateOrderFillableOrThrowAsync', () => {
+ it('should succeed if the order is fillable', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
+ });
+ it('should succeed if the order is asymmetric and fillable', async () => {
+ const makerFillableAmount = fillableAmount;
+ const takerFillableAmount = fillableAmount.minus(4);
+ const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ makerFillableAmount,
+ takerFillableAmount,
+ );
+ await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
+ });
+ it('should throw when the order is fully filled or cancelled', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
+ return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
+ ExchangeContractErrs.OrderRemainingFillAmountZero,
+ );
+ });
+ it('should throw when order is expired', async () => {
+ const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ expirationInPast,
+ );
+ return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
+ ExchangeContractErrs.OrderFillExpired,
+ );
+ });
+ });
+ describe('validateFillOrderAndThrowIfInvalidAsync', () => {
+ it('should throw when the fill amount is zero', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const zeroFillAmount = new BigNumber(0);
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, zeroFillAmount, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should throw when the signature is invalid', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ // 27 <--> 28
+ signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27;
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
+ ).to.be.rejectedWith(ZeroExError.InvalidSignature);
+ });
+ it('should throw when the order is fully filled or cancelled', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ });
+ it('should throw when sender is not a taker', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const nonTakerAddress = userAddresses[6];
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, nonTakerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
+ });
+ it('should throw when order is expired', async () => {
+ const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ expirationInPast,
+ );
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired);
+ });
+ it('should throw when there a rounding error would have occurred', async () => {
+ const makerAmount = new BigNumber(3);
+ const takerAmount = new BigNumber(5);
+ const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ makerAmount,
+ takerAmount,
+ );
+ const fillTakerAmountThatCausesRoundingError = new BigNumber(3);
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
+ signedOrder,
+ fillTakerAmountThatCausesRoundingError,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError);
+ });
+ });
+ describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => {
+ it('should throw if remaining fillAmount is less then the desired fillAmount', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const tooLargeFillAmount = new BigNumber(7);
+ const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount);
+ await zeroEx.token.transferAsync(takerTokenAddress, coinbase, takerAddress, fillAmountDifference);
+ await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, tooLargeFillAmount);
+ await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference);
+ await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount);
- return expect(
- zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync(
- signedOrder,
- tooLargeFillAmount,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount);
- });
- });
- describe('validateCancelOrderAndThrowIfInvalidAsync', () => {
- let signedOrder: SignedOrder;
- const cancelAmount = new BigNumber(3);
- beforeEach(async () => {
- [coinbase, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- it('should throw when cancel amount is zero', async () => {
- const zeroCancelAmount = new BigNumber(0);
- return expect(
- zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should throw when order is expired', async () => {
- const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
- const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationInPast,
- );
- return expect(
- zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired);
- });
- it('should throw when order is already cancelled or filled', async () => {
- await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- return expect(
- zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
- });
- });
- describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => {
- let exchangeTransferSimulator: ExchangeTransferSimulator;
- let transferFromAsync: Sinon.SinonSpy;
- const bigNumberMatch = (expected: BigNumber) => {
- return Sinon.match((value: BigNumber) => value.eq(expected));
- };
- beforeEach('create exchangeTransferSimulator', async () => {
- exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest);
- transferFromAsync = Sinon.spy();
- exchangeTransferSimulator.transferFromAsync = transferFromAsync as any;
- });
- it('should call exchangeTransferSimulator.transferFrom in a correct order', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- feeRecipient,
- );
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- fillableAmount,
- takerAddress,
- zrxTokenAddress,
- );
- expect(transferFromAsync.callCount).to.be.equal(4);
- expect(
- transferFromAsync
- .getCall(0)
- .calledWith(
- makerTokenAddress,
- makerAddress,
- takerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Maker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(1)
- .calledWith(
- takerTokenAddress,
- takerAddress,
- makerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Taker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(2)
- .calledWith(
- zrxTokenAddress,
- makerAddress,
- feeRecipient,
- bigNumberMatch(makerFee),
- TradeSide.Maker,
- TransferType.Fee,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(3)
- .calledWith(
- zrxTokenAddress,
- takerAddress,
- feeRecipient,
- bigNumberMatch(takerFee),
- TradeSide.Taker,
- TransferType.Fee,
- ),
- ).to.be.true();
- });
- it('should call exchangeTransferSimulator.transferFrom with correct values for an open order', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- ZeroEx.NULL_ADDRESS,
- fillableAmount,
- feeRecipient,
- );
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- fillableAmount,
- takerAddress,
- zrxTokenAddress,
- );
- expect(transferFromAsync.callCount).to.be.equal(4);
- expect(
- transferFromAsync
- .getCall(0)
- .calledWith(
- makerTokenAddress,
- makerAddress,
- takerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Maker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(1)
- .calledWith(
- takerTokenAddress,
- takerAddress,
- makerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Taker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(2)
- .calledWith(
- zrxTokenAddress,
- makerAddress,
- feeRecipient,
- bigNumberMatch(makerFee),
- TradeSide.Maker,
- TransferType.Fee,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(3)
- .calledWith(
- zrxTokenAddress,
- takerAddress,
- feeRecipient,
- bigNumberMatch(takerFee),
- TradeSide.Taker,
- TransferType.Fee,
- ),
- ).to.be.true();
- });
- it('should correctly round the fillMakerTokenAmount', async () => {
- const makerTokenAmount = new BigNumber(3);
- const takerTokenAmount = new BigNumber(1);
- const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- makerTokenAmount,
- takerTokenAmount,
- );
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- takerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- expect(transferFromAsync.callCount).to.be.equal(4);
- const makerFillAmount = transferFromAsync.getCall(0).args[3];
- expect(makerFillAmount).to.be.bignumber.equal(makerTokenAmount);
- });
- it('should correctly round the makerFeeAmount', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(4);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- ZeroEx.NULL_ADDRESS,
- );
- const fillTakerTokenAmount = fillableAmount.div(2).round(0);
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- const makerPartialFee = makerFee.div(2);
- const takerPartialFee = takerFee.div(2);
- expect(transferFromAsync.callCount).to.be.equal(4);
- const partialMakerFee = transferFromAsync.getCall(2).args[3];
- expect(partialMakerFee).to.be.bignumber.equal(makerPartialFee);
- const partialTakerFee = transferFromAsync.getCall(3).args[3];
- expect(partialTakerFee).to.be.bignumber.equal(takerPartialFee);
- });
- });
+ return expect(
+ zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync(
+ signedOrder,
+ tooLargeFillAmount,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount);
+ });
+ });
+ describe('validateCancelOrderAndThrowIfInvalidAsync', () => {
+ let signedOrder: SignedOrder;
+ const cancelAmount = new BigNumber(3);
+ beforeEach(async () => {
+ [coinbase, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ it('should throw when cancel amount is zero', async () => {
+ const zeroCancelAmount = new BigNumber(0);
+ return expect(
+ zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ it('should throw when order is expired', async () => {
+ const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
+ const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ expirationInPast,
+ );
+ return expect(
+ zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired);
+ });
+ it('should throw when order is already cancelled or filled', async () => {
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
+ return expect(
+ zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
+ });
+ });
+ describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => {
+ let exchangeTransferSimulator: ExchangeTransferSimulator;
+ let transferFromAsync: Sinon.SinonSpy;
+ const bigNumberMatch = (expected: BigNumber) => {
+ return Sinon.match((value: BigNumber) => value.eq(expected));
+ };
+ beforeEach('create exchangeTransferSimulator', async () => {
+ exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest);
+ transferFromAsync = Sinon.spy();
+ exchangeTransferSimulator.transferFromAsync = transferFromAsync as any;
+ });
+ it('should call exchangeTransferSimulator.transferFrom in a correct order', async () => {
+ const makerFee = new BigNumber(2);
+ const takerFee = new BigNumber(2);
+ const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ feeRecipient,
+ );
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTransferSimulator,
+ signedOrder,
+ fillableAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ expect(transferFromAsync.callCount).to.be.equal(4);
+ expect(
+ transferFromAsync
+ .getCall(0)
+ .calledWith(
+ makerTokenAddress,
+ makerAddress,
+ takerAddress,
+ bigNumberMatch(fillableAmount),
+ TradeSide.Maker,
+ TransferType.Trade,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(1)
+ .calledWith(
+ takerTokenAddress,
+ takerAddress,
+ makerAddress,
+ bigNumberMatch(fillableAmount),
+ TradeSide.Taker,
+ TransferType.Trade,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(2)
+ .calledWith(
+ zrxTokenAddress,
+ makerAddress,
+ feeRecipient,
+ bigNumberMatch(makerFee),
+ TradeSide.Maker,
+ TransferType.Fee,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(3)
+ .calledWith(
+ zrxTokenAddress,
+ takerAddress,
+ feeRecipient,
+ bigNumberMatch(takerFee),
+ TradeSide.Taker,
+ TransferType.Fee,
+ ),
+ ).to.be.true();
+ });
+ it('should call exchangeTransferSimulator.transferFrom with correct values for an open order', async () => {
+ const makerFee = new BigNumber(2);
+ const takerFee = new BigNumber(2);
+ const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ ZeroEx.NULL_ADDRESS,
+ fillableAmount,
+ feeRecipient,
+ );
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTransferSimulator,
+ signedOrder,
+ fillableAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ expect(transferFromAsync.callCount).to.be.equal(4);
+ expect(
+ transferFromAsync
+ .getCall(0)
+ .calledWith(
+ makerTokenAddress,
+ makerAddress,
+ takerAddress,
+ bigNumberMatch(fillableAmount),
+ TradeSide.Maker,
+ TransferType.Trade,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(1)
+ .calledWith(
+ takerTokenAddress,
+ takerAddress,
+ makerAddress,
+ bigNumberMatch(fillableAmount),
+ TradeSide.Taker,
+ TransferType.Trade,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(2)
+ .calledWith(
+ zrxTokenAddress,
+ makerAddress,
+ feeRecipient,
+ bigNumberMatch(makerFee),
+ TradeSide.Maker,
+ TransferType.Fee,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(3)
+ .calledWith(
+ zrxTokenAddress,
+ takerAddress,
+ feeRecipient,
+ bigNumberMatch(takerFee),
+ TradeSide.Taker,
+ TransferType.Fee,
+ ),
+ ).to.be.true();
+ });
+ it('should correctly round the fillMakerTokenAmount', async () => {
+ const makerTokenAmount = new BigNumber(3);
+ const takerTokenAmount = new BigNumber(1);
+ const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ makerTokenAmount,
+ takerTokenAmount,
+ );
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTransferSimulator,
+ signedOrder,
+ takerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ expect(transferFromAsync.callCount).to.be.equal(4);
+ const makerFillAmount = transferFromAsync.getCall(0).args[3];
+ expect(makerFillAmount).to.be.bignumber.equal(makerTokenAmount);
+ });
+ it('should correctly round the makerFeeAmount', async () => {
+ const makerFee = new BigNumber(2);
+ const takerFee = new BigNumber(4);
+ const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ ZeroEx.NULL_ADDRESS,
+ );
+ const fillTakerTokenAmount = fillableAmount.div(2).round(0);
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTransferSimulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ const makerPartialFee = makerFee.div(2);
+ const takerPartialFee = takerFee.div(2);
+ expect(transferFromAsync.callCount).to.be.equal(4);
+ const partialMakerFee = transferFromAsync.getCall(2).args[3];
+ expect(partialMakerFee).to.be.bignumber.equal(makerPartialFee);
+ const partialTakerFee = transferFromAsync.getCall(3).args[3];
+ expect(partialTakerFee).to.be.bignumber.equal(takerPartialFee);
+ });
+ });
});
diff --git a/packages/0x.js/test/remaining_fillable_calculator_test.ts b/packages/0x.js/test/remaining_fillable_calculator_test.ts
index 4c6b8f3ac..58e9dd93b 100644
--- a/packages/0x.js/test/remaining_fillable_calculator_test.ts
+++ b/packages/0x.js/test/remaining_fillable_calculator_test.ts
@@ -12,223 +12,223 @@ chaiSetup.configure();
const expect = chai.expect;
describe('RemainingFillableCalculator', () => {
- let calculator: RemainingFillableCalculator;
- let signedOrder: SignedOrder;
- let transferrableMakerTokenAmount: BigNumber;
- let transferrableMakerFeeTokenAmount: BigNumber;
- let remainingMakerTokenAmount: BigNumber;
- let makerAmount: BigNumber;
- let takerAmount: BigNumber;
- let makerFeeAmount: BigNumber;
- let isMakerTokenZRX: boolean;
- const makerToken: string = '0x1';
- const takerToken: string = '0x2';
- const decimals: number = 4;
- const zero: BigNumber = new BigNumber(0);
- const zeroAddress = '0x0';
- const signature: ECSignature = { v: 27, r: '', s: '' };
- beforeEach(async () => {
- [makerAmount, takerAmount, makerFeeAmount] = [
- ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals),
- ];
- [transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount] = [
- ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
- ];
- });
- function buildSignedOrder(): SignedOrder {
- return {
- ecSignature: signature,
- exchangeContractAddress: zeroAddress,
- feeRecipient: zeroAddress,
- maker: zeroAddress,
- taker: zeroAddress,
- makerFee: makerFeeAmount,
- takerFee: zero,
- makerTokenAmount: makerAmount,
- takerTokenAmount: takerAmount,
- makerTokenAddress: makerToken,
- takerTokenAddress: takerToken,
- salt: zero,
- expirationUnixTimestampSec: zero,
- };
- }
- describe('Maker token is NOT ZRX', () => {
- before(async () => {
- isMakerTokenZRX = false;
- });
- it('calculates the correct amount when unfilled and funds available', () => {
- signedOrder = buildSignedOrder();
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
- });
- it('calculates the correct amount when partially filled and funds available', () => {
- signedOrder = buildSignedOrder();
- remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
- });
- it('calculates the amount to be 0 when all fee funds are transferred', () => {
- signedOrder = buildSignedOrder();
- transferrableMakerFeeTokenAmount = zero;
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
- });
- it('calculates the correct amount when balance is less than remaining fillable', () => {
- signedOrder = buildSignedOrder();
- const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
- transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
- });
- describe('Order to Fee Ratio is < 1', () => {
- beforeEach(async () => {
- [makerAmount, takerAmount, makerFeeAmount] = [
- ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
- ];
- });
- it('calculates the correct amount when funds unavailable', () => {
- signedOrder = buildSignedOrder();
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
- });
- });
- describe('Ratio is not evenly divisble', () => {
- beforeEach(async () => {
- [makerAmount, takerAmount, makerFeeAmount] = [
- ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
- ];
- });
- it('calculates the correct amount when funds unavailable', () => {
- signedOrder = buildSignedOrder();
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
- expect(calculatedFillableAmount.lessThanOrEqualTo(transferrableMakerTokenAmount)).to.be.true();
- expect(calculatedFillableAmount).to.be.bignumber.greaterThan(new BigNumber(0));
- const orderToFeeRatio = signedOrder.makerTokenAmount.dividedBy(signedOrder.makerFee);
- const calculatedFeeAmount = calculatedFillableAmount.dividedBy(orderToFeeRatio);
- expect(calculatedFeeAmount).to.be.bignumber.lessThan(transferrableMakerFeeTokenAmount);
- });
- });
- });
- describe('Maker Token is ZRX', () => {
- before(async () => {
- isMakerTokenZRX = true;
- });
- it('calculates the correct amount when unfilled and funds available', () => {
- signedOrder = buildSignedOrder();
- transferrableMakerTokenAmount = makerAmount.plus(makerFeeAmount);
- transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
- });
- it('calculates the correct amount when partially filled and funds available', () => {
- signedOrder = buildSignedOrder();
- remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
- });
- it('calculates the amount to be 0 when all fee funds are transferred', () => {
- signedOrder = buildSignedOrder();
- transferrableMakerTokenAmount = zero;
- transferrableMakerFeeTokenAmount = zero;
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
- });
- it('calculates the correct amount when balance is less than remaining fillable', () => {
- signedOrder = buildSignedOrder();
- const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
- transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
- transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
+ let calculator: RemainingFillableCalculator;
+ let signedOrder: SignedOrder;
+ let transferrableMakerTokenAmount: BigNumber;
+ let transferrableMakerFeeTokenAmount: BigNumber;
+ let remainingMakerTokenAmount: BigNumber;
+ let makerAmount: BigNumber;
+ let takerAmount: BigNumber;
+ let makerFeeAmount: BigNumber;
+ let isMakerTokenZRX: boolean;
+ const makerToken: string = '0x1';
+ const takerToken: string = '0x2';
+ const decimals: number = 4;
+ const zero: BigNumber = new BigNumber(0);
+ const zeroAddress = '0x0';
+ const signature: ECSignature = { v: 27, r: '', s: '' };
+ beforeEach(async () => {
+ [makerAmount, takerAmount, makerFeeAmount] = [
+ ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals),
+ ];
+ [transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount] = [
+ ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
+ ];
+ });
+ function buildSignedOrder(): SignedOrder {
+ return {
+ ecSignature: signature,
+ exchangeContractAddress: zeroAddress,
+ feeRecipient: zeroAddress,
+ maker: zeroAddress,
+ taker: zeroAddress,
+ makerFee: makerFeeAmount,
+ takerFee: zero,
+ makerTokenAmount: makerAmount,
+ takerTokenAmount: takerAmount,
+ makerTokenAddress: makerToken,
+ takerTokenAddress: takerToken,
+ salt: zero,
+ expirationUnixTimestampSec: zero,
+ };
+ }
+ describe('Maker token is NOT ZRX', () => {
+ before(async () => {
+ isMakerTokenZRX = false;
+ });
+ it('calculates the correct amount when unfilled and funds available', () => {
+ signedOrder = buildSignedOrder();
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
+ });
+ it('calculates the correct amount when partially filled and funds available', () => {
+ signedOrder = buildSignedOrder();
+ remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
+ });
+ it('calculates the amount to be 0 when all fee funds are transferred', () => {
+ signedOrder = buildSignedOrder();
+ transferrableMakerFeeTokenAmount = zero;
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
+ });
+ it('calculates the correct amount when balance is less than remaining fillable', () => {
+ signedOrder = buildSignedOrder();
+ const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
+ transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
+ });
+ describe('Order to Fee Ratio is < 1', () => {
+ beforeEach(async () => {
+ [makerAmount, takerAmount, makerFeeAmount] = [
+ ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
+ ];
+ });
+ it('calculates the correct amount when funds unavailable', () => {
+ signedOrder = buildSignedOrder();
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
+ });
+ });
+ describe('Ratio is not evenly divisble', () => {
+ beforeEach(async () => {
+ [makerAmount, takerAmount, makerFeeAmount] = [
+ ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
+ ];
+ });
+ it('calculates the correct amount when funds unavailable', () => {
+ signedOrder = buildSignedOrder();
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
+ expect(calculatedFillableAmount.lessThanOrEqualTo(transferrableMakerTokenAmount)).to.be.true();
+ expect(calculatedFillableAmount).to.be.bignumber.greaterThan(new BigNumber(0));
+ const orderToFeeRatio = signedOrder.makerTokenAmount.dividedBy(signedOrder.makerFee);
+ const calculatedFeeAmount = calculatedFillableAmount.dividedBy(orderToFeeRatio);
+ expect(calculatedFeeAmount).to.be.bignumber.lessThan(transferrableMakerFeeTokenAmount);
+ });
+ });
+ });
+ describe('Maker Token is ZRX', () => {
+ before(async () => {
+ isMakerTokenZRX = true;
+ });
+ it('calculates the correct amount when unfilled and funds available', () => {
+ signedOrder = buildSignedOrder();
+ transferrableMakerTokenAmount = makerAmount.plus(makerFeeAmount);
+ transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
+ });
+ it('calculates the correct amount when partially filled and funds available', () => {
+ signedOrder = buildSignedOrder();
+ remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
+ });
+ it('calculates the amount to be 0 when all fee funds are transferred', () => {
+ signedOrder = buildSignedOrder();
+ transferrableMakerTokenAmount = zero;
+ transferrableMakerFeeTokenAmount = zero;
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
+ });
+ it('calculates the correct amount when balance is less than remaining fillable', () => {
+ signedOrder = buildSignedOrder();
+ const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
+ transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
+ transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
- const orderToFeeRatio = signedOrder.makerTokenAmount.dividedToIntegerBy(signedOrder.makerFee);
- const expectedFillableAmount = new BigNumber(450980);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
- const numberOfFillsInRatio = calculatedFillableAmount.dividedToIntegerBy(orderToFeeRatio);
- const calculatedFillableAmountPlusFees = calculatedFillableAmount.plus(numberOfFillsInRatio);
- expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(transferrableMakerTokenAmount);
- expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(remainingMakerTokenAmount);
- expect(calculatedFillableAmount).to.be.bignumber.equal(expectedFillableAmount);
- expect(numberOfFillsInRatio.decimalPlaces()).to.be.equal(0);
- });
- });
+ const orderToFeeRatio = signedOrder.makerTokenAmount.dividedToIntegerBy(signedOrder.makerFee);
+ const expectedFillableAmount = new BigNumber(450980);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
+ const numberOfFillsInRatio = calculatedFillableAmount.dividedToIntegerBy(orderToFeeRatio);
+ const calculatedFillableAmountPlusFees = calculatedFillableAmount.plus(numberOfFillsInRatio);
+ expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(transferrableMakerTokenAmount);
+ expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(remainingMakerTokenAmount);
+ expect(calculatedFillableAmount).to.be.bignumber.equal(expectedFillableAmount);
+ expect(numberOfFillsInRatio.decimalPlaces()).to.be.equal(0);
+ });
+ });
});
diff --git a/packages/0x.js/test/subscription_test.ts b/packages/0x.js/test/subscription_test.ts
index f4c6f748f..f70ecd6cd 100644
--- a/packages/0x.js/test/subscription_test.ts
+++ b/packages/0x.js/test/subscription_test.ts
@@ -17,69 +17,69 @@ chaiSetup.configure();
const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
describe('SubscriptionTest', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let userAddresses: string[];
- let tokens: Token[];
- let coinbase: string;
- let addressWithoutFunds: string;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- coinbase = userAddresses[0];
- addressWithoutFunds = userAddresses[1];
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#subscribe', () => {
- const indexFilterValues = {};
- let tokenAddress: string;
- const allowanceAmount = new BigNumber(42);
- let stubs: Sinon.SinonStub[] = [];
- before(() => {
- const token = tokens[0];
- tokenAddress = token.address;
- });
- afterEach(() => {
- zeroEx.token.unsubscribeAll();
- _.each(stubs, s => s.restore());
- stubs = [];
- });
- it('Should receive the Error when an error occurs while fetching the block', (done: DoneCallback) => {
- (async () => {
- const errMsg = 'Error fetching block';
- const callback = assertNodeCallbackError(done, errMsg);
- stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error(errMsg))];
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
- await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
- })().catch(done);
- });
- it('Should receive the Error when an error occurs while reconciling the new block', (done: DoneCallback) => {
- (async () => {
- const errMsg = 'Error fetching logs';
- const callback = assertNodeCallbackError(done, errMsg);
- stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getLogsAsync').throws(new Error(errMsg))];
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
- await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
- })().catch(done);
- });
- it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => {
- (async () => {
- const callback = (err: Error | null, logEvent?: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop;
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
- stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error('JSON RPC error'))];
- zeroEx.token.unsubscribeAll();
- done();
- })().catch(done);
- });
- });
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let userAddresses: string[];
+ let tokens: Token[];
+ let coinbase: string;
+ let addressWithoutFunds: string;
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ coinbase = userAddresses[0];
+ addressWithoutFunds = userAddresses[1];
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#subscribe', () => {
+ const indexFilterValues = {};
+ let tokenAddress: string;
+ const allowanceAmount = new BigNumber(42);
+ let stubs: Sinon.SinonStub[] = [];
+ before(() => {
+ const token = tokens[0];
+ tokenAddress = token.address;
+ });
+ afterEach(() => {
+ zeroEx.token.unsubscribeAll();
+ _.each(stubs, s => s.restore());
+ stubs = [];
+ });
+ it('Should receive the Error when an error occurs while fetching the block', (done: DoneCallback) => {
+ (async () => {
+ const errMsg = 'Error fetching block';
+ const callback = assertNodeCallbackError(done, errMsg);
+ stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error(errMsg))];
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
+ await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
+ })().catch(done);
+ });
+ it('Should receive the Error when an error occurs while reconciling the new block', (done: DoneCallback) => {
+ (async () => {
+ const errMsg = 'Error fetching logs';
+ const callback = assertNodeCallbackError(done, errMsg);
+ stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getLogsAsync').throws(new Error(errMsg))];
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
+ await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
+ })().catch(done);
+ });
+ it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => {
+ (async () => {
+ const callback = (err: Error | null, logEvent?: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop;
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
+ stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error('JSON RPC error'))];
+ zeroEx.token.unsubscribeAll();
+ done();
+ })().catch(done);
+ });
+ });
});
diff --git a/packages/0x.js/test/token_registry_wrapper_test.ts b/packages/0x.js/test/token_registry_wrapper_test.ts
index 0a170db8f..c798c00d7 100644
--- a/packages/0x.js/test/token_registry_wrapper_test.ts
+++ b/packages/0x.js/test/token_registry_wrapper_test.ts
@@ -17,113 +17,113 @@ const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
const TOKEN_REGISTRY_SIZE_AFTER_MIGRATION = 7;
describe('TokenRegistryWrapper', () => {
- let zeroEx: ZeroEx;
- let tokens: Token[];
- const tokenAddressBySymbol: { [symbol: string]: string } = {};
- const tokenAddressByName: { [symbol: string]: string } = {};
- const tokenBySymbol: { [symbol: string]: Token } = {};
- const tokenByName: { [symbol: string]: Token } = {};
- const registeredSymbol = 'ZRX';
- const registeredName = '0x Protocol Token';
- const unregisteredSymbol = 'MAL';
- const unregisteredName = 'Malicious Token';
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- const web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- _.map(tokens, token => {
- tokenAddressBySymbol[token.symbol] = token.address;
- tokenAddressByName[token.name] = token.address;
- tokenBySymbol[token.symbol] = token;
- tokenByName[token.name] = token;
- });
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#getTokensAsync', () => {
- it('should return all the tokens added to the tokenRegistry during the migration', async () => {
- expect(tokens).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
+ let zeroEx: ZeroEx;
+ let tokens: Token[];
+ const tokenAddressBySymbol: { [symbol: string]: string } = {};
+ const tokenAddressByName: { [symbol: string]: string } = {};
+ const tokenBySymbol: { [symbol: string]: Token } = {};
+ const tokenByName: { [symbol: string]: Token } = {};
+ const registeredSymbol = 'ZRX';
+ const registeredName = '0x Protocol Token';
+ const unregisteredSymbol = 'MAL';
+ const unregisteredName = 'Malicious Token';
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ const web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ _.map(tokens, token => {
+ tokenAddressBySymbol[token.symbol] = token.address;
+ tokenAddressByName[token.name] = token.address;
+ tokenBySymbol[token.symbol] = token;
+ tokenByName[token.name] = token;
+ });
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#getTokensAsync', () => {
+ it('should return all the tokens added to the tokenRegistry during the migration', async () => {
+ expect(tokens).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
- const schemaValidator = new SchemaValidator();
- _.each(tokens, token => {
- const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
- expect(validationResult.errors).to.have.lengthOf(0);
- });
- });
- });
- describe('#getTokenAddressesAsync', () => {
- it('should return all the token addresses added to the tokenRegistry during the migration', async () => {
- const tokenAddresses = await zeroEx.tokenRegistry.getTokenAddressesAsync();
- expect(tokenAddresses).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
+ const schemaValidator = new SchemaValidator();
+ _.each(tokens, token => {
+ const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
+ expect(validationResult.errors).to.have.lengthOf(0);
+ });
+ });
+ });
+ describe('#getTokenAddressesAsync', () => {
+ it('should return all the token addresses added to the tokenRegistry during the migration', async () => {
+ const tokenAddresses = await zeroEx.tokenRegistry.getTokenAddressesAsync();
+ expect(tokenAddresses).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
- const schemaValidator = new SchemaValidator();
- _.each(tokenAddresses, tokenAddress => {
- const validationResult = schemaValidator.validate(tokenAddress, schemas.addressSchema);
- expect(validationResult.errors).to.have.lengthOf(0);
- expect(tokenAddress).to.not.be.equal(ZeroEx.NULL_ADDRESS);
- });
- });
- });
- describe('#getTokenAddressBySymbol', () => {
- it('should return correct address for a token in the registry', async () => {
- const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(registeredSymbol);
- expect(tokenAddress).to.be.equal(tokenAddressBySymbol[registeredSymbol]);
- });
- it('should return undefined for a token out of registry', async () => {
- const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(unregisteredSymbol);
- expect(tokenAddress).to.be.undefined();
- });
- });
- describe('#getTokenAddressByName', () => {
- it('should return correct address for a token in the registry', async () => {
- const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync(registeredName);
- expect(tokenAddress).to.be.equal(tokenAddressByName[registeredName]);
- });
- it('should return undefined for a token out of registry', async () => {
- const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync(unregisteredName);
- expect(tokenAddress).to.be.undefined();
- });
- });
- describe('#getTokenBySymbol', () => {
- it('should return correct token for a token in the registry', async () => {
- const token = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync(registeredSymbol);
- expect(token).to.be.deep.equal(tokenBySymbol[registeredSymbol]);
- });
- it('should return undefined for a token out of registry', async () => {
- const token = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync(unregisteredSymbol);
- expect(token).to.be.undefined();
- });
- });
- describe('#getTokenByName', () => {
- it('should return correct token for a token in the registry', async () => {
- const token = await zeroEx.tokenRegistry.getTokenByNameIfExistsAsync(registeredName);
- expect(token).to.be.deep.equal(tokenByName[registeredName]);
- });
- it('should return undefined for a token out of registry', async () => {
- const token = await zeroEx.tokenRegistry.getTokenByNameIfExistsAsync(unregisteredName);
- expect(token).to.be.undefined();
- });
- });
- describe('#getTokenIfExistsAsync', () => {
- it('should return the token added to the tokenRegistry during the migration', async () => {
- const aToken = tokens[0];
+ const schemaValidator = new SchemaValidator();
+ _.each(tokenAddresses, tokenAddress => {
+ const validationResult = schemaValidator.validate(tokenAddress, schemas.addressSchema);
+ expect(validationResult.errors).to.have.lengthOf(0);
+ expect(tokenAddress).to.not.be.equal(ZeroEx.NULL_ADDRESS);
+ });
+ });
+ });
+ describe('#getTokenAddressBySymbol', () => {
+ it('should return correct address for a token in the registry', async () => {
+ const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(registeredSymbol);
+ expect(tokenAddress).to.be.equal(tokenAddressBySymbol[registeredSymbol]);
+ });
+ it('should return undefined for a token out of registry', async () => {
+ const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(unregisteredSymbol);
+ expect(tokenAddress).to.be.undefined();
+ });
+ });
+ describe('#getTokenAddressByName', () => {
+ it('should return correct address for a token in the registry', async () => {
+ const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync(registeredName);
+ expect(tokenAddress).to.be.equal(tokenAddressByName[registeredName]);
+ });
+ it('should return undefined for a token out of registry', async () => {
+ const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync(unregisteredName);
+ expect(tokenAddress).to.be.undefined();
+ });
+ });
+ describe('#getTokenBySymbol', () => {
+ it('should return correct token for a token in the registry', async () => {
+ const token = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync(registeredSymbol);
+ expect(token).to.be.deep.equal(tokenBySymbol[registeredSymbol]);
+ });
+ it('should return undefined for a token out of registry', async () => {
+ const token = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync(unregisteredSymbol);
+ expect(token).to.be.undefined();
+ });
+ });
+ describe('#getTokenByName', () => {
+ it('should return correct token for a token in the registry', async () => {
+ const token = await zeroEx.tokenRegistry.getTokenByNameIfExistsAsync(registeredName);
+ expect(token).to.be.deep.equal(tokenByName[registeredName]);
+ });
+ it('should return undefined for a token out of registry', async () => {
+ const token = await zeroEx.tokenRegistry.getTokenByNameIfExistsAsync(unregisteredName);
+ expect(token).to.be.undefined();
+ });
+ });
+ describe('#getTokenIfExistsAsync', () => {
+ it('should return the token added to the tokenRegistry during the migration', async () => {
+ const aToken = tokens[0];
- const token = await zeroEx.tokenRegistry.getTokenIfExistsAsync(aToken.address);
- const schemaValidator = new SchemaValidator();
- const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
- expect(validationResult.errors).to.have.lengthOf(0);
- });
- it('should return return undefined when passed a token address not in the tokenRegistry', async () => {
- const unregisteredTokenAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
- const tokenIfExists = await zeroEx.tokenRegistry.getTokenIfExistsAsync(unregisteredTokenAddress);
- expect(tokenIfExists).to.be.undefined();
- });
- });
+ const token = await zeroEx.tokenRegistry.getTokenIfExistsAsync(aToken.address);
+ const schemaValidator = new SchemaValidator();
+ const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
+ expect(validationResult.errors).to.have.lengthOf(0);
+ });
+ it('should return return undefined when passed a token address not in the tokenRegistry', async () => {
+ const unregisteredTokenAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
+ const tokenIfExists = await zeroEx.tokenRegistry.getTokenIfExistsAsync(unregisteredTokenAddress);
+ expect(tokenIfExists).to.be.undefined();
+ });
+ });
});
diff --git a/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts b/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts
index 15bd7a8ba..622919be2 100644
--- a/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts
+++ b/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts
@@ -10,27 +10,27 @@ chaiSetup.configure();
const expect = chai.expect;
describe('TokenTransferProxyWrapper', () => {
- let zeroEx: ZeroEx;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- const web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- });
- describe('#isAuthorizedAsync', () => {
- it('should return false if the address is not authorized', async () => {
- const isAuthorized = await zeroEx.proxy.isAuthorizedAsync(ZeroEx.NULL_ADDRESS);
- expect(isAuthorized).to.be.false();
- });
- });
- describe('#getAuthorizedAddressesAsync', () => {
- it('should return the list of authorized addresses', async () => {
- const authorizedAddresses = await zeroEx.proxy.getAuthorizedAddressesAsync();
- for (const authorizedAddress of authorizedAddresses) {
- const isAuthorized = await zeroEx.proxy.isAuthorizedAsync(authorizedAddress);
- expect(isAuthorized).to.be.true();
- }
- });
- });
+ let zeroEx: ZeroEx;
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ const web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ });
+ describe('#isAuthorizedAsync', () => {
+ it('should return false if the address is not authorized', async () => {
+ const isAuthorized = await zeroEx.proxy.isAuthorizedAsync(ZeroEx.NULL_ADDRESS);
+ expect(isAuthorized).to.be.false();
+ });
+ });
+ describe('#getAuthorizedAddressesAsync', () => {
+ it('should return the list of authorized addresses', async () => {
+ const authorizedAddresses = await zeroEx.proxy.getAuthorizedAddressesAsync();
+ for (const authorizedAddress of authorizedAddresses) {
+ const isAuthorized = await zeroEx.proxy.isAuthorizedAsync(authorizedAddress);
+ expect(isAuthorized).to.be.true();
+ }
+ });
+ });
});
diff --git a/packages/0x.js/test/token_wrapper_test.ts b/packages/0x.js/test/token_wrapper_test.ts
index 4ba1f07c5..6919183cb 100644
--- a/packages/0x.js/test/token_wrapper_test.ts
+++ b/packages/0x.js/test/token_wrapper_test.ts
@@ -6,15 +6,15 @@ import 'mocha';
import * as Web3 from 'web3';
import {
- ApprovalContractEventArgs,
- BlockParamLiteral,
- BlockRange,
- DecodedLogEvent,
- Token,
- TokenEvents,
- TransferContractEventArgs,
- ZeroEx,
- ZeroExError,
+ ApprovalContractEventArgs,
+ BlockParamLiteral,
+ BlockRange,
+ DecodedLogEvent,
+ Token,
+ TokenEvents,
+ TransferContractEventArgs,
+ ZeroEx,
+ ZeroExError,
} from '../src';
import { DoneCallback } from '../src/types';
@@ -29,493 +29,493 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
describe('TokenWrapper', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let userAddresses: string[];
- let tokens: Token[];
- let tokenUtils: TokenUtils;
- let coinbase: string;
- let addressWithoutFunds: string;
- let web3Wrapper: Web3Wrapper;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- web3Wrapper = new Web3Wrapper(web3.currentProvider);
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- coinbase = userAddresses[0];
- addressWithoutFunds = userAddresses[1];
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#transferAsync', () => {
- let token: Token;
- let transferAmount: BigNumber;
- before(() => {
- token = tokens[0];
- transferAmount = new BigNumber(42);
- });
- it('should successfully transfer tokens', async () => {
- const fromAddress = coinbase;
- const toAddress = addressWithoutFunds;
- const preBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
- expect(preBalance).to.be.bignumber.equal(0);
- await zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount);
- const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
- return expect(postBalance).to.be.bignumber.equal(transferAmount);
- });
- it('should fail to transfer tokens if fromAddress has an insufficient balance', async () => {
- const fromAddress = addressWithoutFunds;
- const toAddress = coinbase;
- return expect(
- zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount),
- ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
- });
- it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
- const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
- const fromAddress = coinbase;
- const toAddress = coinbase;
- return expect(
- zeroEx.token.transferAsync(nonExistentTokenAddress, fromAddress, toAddress, transferAmount),
- ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist);
- });
- });
- describe('#transferFromAsync', () => {
- let token: Token;
- let toAddress: string;
- let senderAddress: string;
- before(async () => {
- token = tokens[0];
- toAddress = addressWithoutFunds;
- senderAddress = userAddresses[2];
- });
- it('should fail to transfer tokens if fromAddress has insufficient allowance set', async () => {
- const fromAddress = coinbase;
- const transferAmount = new BigNumber(42);
-
- const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress);
- expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount);
-
- const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(token.address, fromAddress, toAddress);
- expect(fromAddressAllowance).to.be.bignumber.equal(0);
-
- return expect(
- zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
- ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
- });
- it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress', async () => {
- const fromAddress = coinbase;
- const transferAmount = new BigNumber(42);
-
- await zeroEx.token.setAllowanceAsync(token.address, fromAddress, toAddress, transferAmount);
-
- return expect(
- zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
- ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
- });
- it('should fail to transfer tokens if fromAddress has insufficient balance', async () => {
- const fromAddress = addressWithoutFunds;
- const transferAmount = new BigNumber(42);
-
- const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress);
- expect(fromAddressBalance).to.be.bignumber.equal(0);
-
- await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
- const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(
- token.address,
- fromAddress,
- senderAddress,
- );
- expect(fromAddressAllowance).to.be.bignumber.equal(transferAmount);
-
- return expect(
- zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
- ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
- });
- it('should successfully transfer tokens', async () => {
- const fromAddress = coinbase;
-
- const preBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
- expect(preBalance).to.be.bignumber.equal(0);
-
- const transferAmount = new BigNumber(42);
- await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
-
- await zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount);
- const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
- return expect(postBalance).to.be.bignumber.equal(transferAmount);
- });
- it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
- const fromAddress = coinbase;
- const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
- return expect(
- zeroEx.token.transferFromAsync(
- nonExistentTokenAddress,
- fromAddress,
- toAddress,
- senderAddress,
- new BigNumber(42),
- ),
- ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist);
- });
- });
- describe('#getBalanceAsync', () => {
- describe('With web3 provider with accounts', () => {
- it('should return the balance for an existing ERC20 token', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const balance = await zeroEx.token.getBalanceAsync(token.address, ownerAddress);
- const expectedBalance = new BigNumber('1000000000000000000000000000');
- return expect(balance).to.be.bignumber.equal(expectedBalance);
- });
- it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
- const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
- const ownerAddress = coinbase;
- return expect(zeroEx.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress)).to.be.rejectedWith(
- ZeroExError.TokenContractDoesNotExist,
- );
- });
- it('should return a balance of 0 for a non-existent owner address', async () => {
- const token = tokens[0];
- const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593';
- const balance = await zeroEx.token.getBalanceAsync(token.address, nonExistentOwner);
- const expectedBalance = new BigNumber(0);
- return expect(balance).to.be.bignumber.equal(expectedBalance);
- });
- });
- describe('With web3 provider without accounts', () => {
- let zeroExWithoutAccounts: ZeroEx;
- before(async () => {
- const hasAddresses = false;
- const web3WithoutAccounts = web3Factory.create(hasAddresses);
- zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider, config);
- });
- it('should return balance even when called with Web3 provider instance without addresses', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const balance = await zeroExWithoutAccounts.token.getBalanceAsync(token.address, ownerAddress);
- const expectedBalance = new BigNumber('1000000000000000000000000000');
- return expect(balance).to.be.bignumber.equal(expectedBalance);
- });
- });
- });
- describe('#setAllowanceAsync', () => {
- it("should set the spender's allowance", async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const spenderAddress = addressWithoutFunds;
-
- const allowanceBeforeSet = await zeroEx.token.getAllowanceAsync(
- token.address,
- ownerAddress,
- spenderAddress,
- );
- const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
- expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
-
- const amountInBaseUnits = new BigNumber(50);
- await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
-
- const allowanceAfterSet = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
- const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
- return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
- });
- });
- describe('#setUnlimitedAllowanceAsync', () => {
- it("should set the unlimited spender's allowance", async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const spenderAddress = addressWithoutFunds;
-
- await zeroEx.token.setUnlimitedAllowanceAsync(token.address, ownerAddress, spenderAddress);
- const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
- return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- it('should reduce the gas cost for transfers including tokens with unlimited allowance support', async () => {
- const transferAmount = new BigNumber(5);
- const zrx = tokenUtils.getProtocolTokenOrThrow();
- const [, userWithNormalAllowance, userWithUnlimitedAllowance] = userAddresses;
- await zeroEx.token.setAllowanceAsync(zrx.address, coinbase, userWithNormalAllowance, transferAmount);
- await zeroEx.token.setUnlimitedAllowanceAsync(zrx.address, coinbase, userWithUnlimitedAllowance);
-
- const initBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
- const initBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
- userWithUnlimitedAllowance,
- );
-
- await zeroEx.token.transferFromAsync(
- zrx.address,
- coinbase,
- userWithNormalAllowance,
- userWithNormalAllowance,
- transferAmount,
- );
- await zeroEx.token.transferFromAsync(
- zrx.address,
- coinbase,
- userWithUnlimitedAllowance,
- userWithUnlimitedAllowance,
- transferAmount,
- );
-
- const finalBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
- const finalBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
- userWithUnlimitedAllowance,
- );
-
- const normalGasCost = initBalanceWithNormalAllowance.minus(finalBalanceWithNormalAllowance);
- const unlimitedGasCost = initBalanceWithUnlimitedAllowance.minus(finalBalanceWithUnlimitedAllowance);
-
- // In theory the gas cost with unlimited allowance should be smaller, but with testrpc it's actually bigger.
- // This needs to be investigated in ethereumjs-vm. This test is essentially a repro.
- // TODO: Make this test pass with inverted assertion.
- expect(unlimitedGasCost.toNumber()).to.be.gt(normalGasCost.toNumber());
- });
- });
- describe('#getAllowanceAsync', () => {
- describe('With web3 provider with accounts', () => {
- it('should get the proxy allowance', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const spenderAddress = addressWithoutFunds;
-
- const amountInBaseUnits = new BigNumber(50);
- await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
-
- const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
- const expectedAllowance = amountInBaseUnits;
- return expect(allowance).to.be.bignumber.equal(expectedAllowance);
- });
- it('should return 0 if no allowance set yet', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const spenderAddress = addressWithoutFunds;
- const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
- const expectedAllowance = new BigNumber(0);
- return expect(allowance).to.be.bignumber.equal(expectedAllowance);
- });
- });
- describe('With web3 provider without accounts', () => {
- let zeroExWithoutAccounts: ZeroEx;
- before(async () => {
- const hasAddresses = false;
- const web3WithoutAccounts = web3Factory.create(hasAddresses);
- zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider, config);
- });
- it('should get the proxy allowance', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const spenderAddress = addressWithoutFunds;
-
- const amountInBaseUnits = new BigNumber(50);
- await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
-
- const allowance = await zeroExWithoutAccounts.token.getAllowanceAsync(
- token.address,
- ownerAddress,
- spenderAddress,
- );
- const expectedAllowance = amountInBaseUnits;
- return expect(allowance).to.be.bignumber.equal(expectedAllowance);
- });
- });
- });
- describe('#getProxyAllowanceAsync', () => {
- it('should get the proxy allowance', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
-
- const amountInBaseUnits = new BigNumber(50);
- await zeroEx.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits);
-
- const allowance = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
- const expectedAllowance = amountInBaseUnits;
- return expect(allowance).to.be.bignumber.equal(expectedAllowance);
- });
- });
- describe('#setProxyAllowanceAsync', () => {
- it('should set the proxy allowance', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
-
- const allowanceBeforeSet = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
- const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
- expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
-
- const amountInBaseUnits = new BigNumber(50);
- await zeroEx.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits);
-
- const allowanceAfterSet = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
- const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
- return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
- });
- });
- describe('#setUnlimitedProxyAllowanceAsync', () => {
- it('should set the unlimited proxy allowance', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
-
- await zeroEx.token.setUnlimitedProxyAllowanceAsync(token.address, ownerAddress);
- const allowance = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
- return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- });
- describe('#subscribe', () => {
- const indexFilterValues = {};
- let tokenAddress: string;
- const transferAmount = new BigNumber(42);
- const allowanceAmount = new BigNumber(42);
- before(() => {
- const token = tokens[0];
- tokenAddress = token.address;
- });
- afterEach(() => {
- zeroEx.token.unsubscribeAll();
- });
- // Hack: Mocha does not allow a test to be both async and have a `done` callback
- // Since we need to await the receipt of the event in the `subscribe` callback,
- // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
- // wrap the rest of the test in an async block
- // Source: https://github.com/mochajs/mocha/issues/2407
- it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<TransferContractEventArgs>) => {
- expect(logEvent.isRemoved).to.be.false();
- expect(logEvent.log.logIndex).to.be.equal(0);
- expect(logEvent.log.transactionIndex).to.be.equal(0);
- expect(logEvent.log.blockNumber).to.be.a('number');
- const args = logEvent.log.args;
- expect(args._from).to.be.equal(coinbase);
- expect(args._to).to.be.equal(addressWithoutFunds);
- expect(args._value).to.be.bignumber.equal(transferAmount);
- },
- );
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callback);
- await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
- })().catch(done);
- });
- it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- expect(logEvent).to.not.be.undefined();
- expect(logEvent.isRemoved).to.be.false();
- const args = logEvent.log.args;
- expect(args._owner).to.be.equal(coinbase);
- expect(args._spender).to.be.equal(addressWithoutFunds);
- expect(args._value).to.be.bignumber.equal(allowanceAmount);
- },
- );
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
- await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
- })().catch(done);
- });
- it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled);
- const callbackToBeCalled = reportNodeCallbackErrors(done)();
- const newProvider = web3Factory.getRpcProvider();
- zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackToBeCalled);
- await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
- })().catch(done);
- });
- it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- const subscriptionToken = zeroEx.token.subscribe(
- tokenAddress,
- TokenEvents.Transfer,
- indexFilterValues,
- callbackNeverToBeCalled,
- );
- zeroEx.token.unsubscribe(subscriptionToken);
- await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
- done();
- })().catch(done);
- });
- });
- describe('#getLogsAsync', () => {
- let tokenAddress: string;
- let tokenTransferProxyAddress: string;
- const blockRange: BlockRange = {
- fromBlock: 0,
- toBlock: BlockParamLiteral.Latest,
- };
- let txHash: string;
- before(() => {
- const token = tokens[0];
- tokenAddress = token.address;
- tokenTransferProxyAddress = zeroEx.proxy.getContractAddress();
- });
- it('should get logs with decoded args emitted by Approval', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = TokenEvents.Approval;
- const indexFilterValues = {};
- const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>(
- tokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(logs[0].event).to.be.equal(eventName);
- expect(args._owner).to.be.equal(coinbase);
- expect(args._spender).to.be.equal(tokenTransferProxyAddress);
- expect(args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- it('should only get the logs with the correct event name', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const differentEventName = TokenEvents.Transfer;
- const indexFilterValues = {};
- const logs = await zeroEx.token.getLogsAsync(
- tokenAddress,
- differentEventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(0);
- });
- it('should only get the logs with the correct indexed fields', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, addressWithoutFunds);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = TokenEvents.Approval;
- const indexFilterValues = {
- _owner: coinbase,
- };
- const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>(
- tokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(args._owner).to.be.equal(coinbase);
- });
- });
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let userAddresses: string[];
+ let tokens: Token[];
+ let tokenUtils: TokenUtils;
+ let coinbase: string;
+ let addressWithoutFunds: string;
+ let web3Wrapper: Web3Wrapper;
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ web3Wrapper = new Web3Wrapper(web3.currentProvider);
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ coinbase = userAddresses[0];
+ addressWithoutFunds = userAddresses[1];
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#transferAsync', () => {
+ let token: Token;
+ let transferAmount: BigNumber;
+ before(() => {
+ token = tokens[0];
+ transferAmount = new BigNumber(42);
+ });
+ it('should successfully transfer tokens', async () => {
+ const fromAddress = coinbase;
+ const toAddress = addressWithoutFunds;
+ const preBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
+ expect(preBalance).to.be.bignumber.equal(0);
+ await zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount);
+ const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
+ return expect(postBalance).to.be.bignumber.equal(transferAmount);
+ });
+ it('should fail to transfer tokens if fromAddress has an insufficient balance', async () => {
+ const fromAddress = addressWithoutFunds;
+ const toAddress = coinbase;
+ return expect(
+ zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount),
+ ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
+ });
+ it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
+ const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
+ const fromAddress = coinbase;
+ const toAddress = coinbase;
+ return expect(
+ zeroEx.token.transferAsync(nonExistentTokenAddress, fromAddress, toAddress, transferAmount),
+ ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist);
+ });
+ });
+ describe('#transferFromAsync', () => {
+ let token: Token;
+ let toAddress: string;
+ let senderAddress: string;
+ before(async () => {
+ token = tokens[0];
+ toAddress = addressWithoutFunds;
+ senderAddress = userAddresses[2];
+ });
+ it('should fail to transfer tokens if fromAddress has insufficient allowance set', async () => {
+ const fromAddress = coinbase;
+ const transferAmount = new BigNumber(42);
+
+ const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress);
+ expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount);
+
+ const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(token.address, fromAddress, toAddress);
+ expect(fromAddressAllowance).to.be.bignumber.equal(0);
+
+ return expect(
+ zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
+ ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
+ });
+ it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress', async () => {
+ const fromAddress = coinbase;
+ const transferAmount = new BigNumber(42);
+
+ await zeroEx.token.setAllowanceAsync(token.address, fromAddress, toAddress, transferAmount);
+
+ return expect(
+ zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
+ ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
+ });
+ it('should fail to transfer tokens if fromAddress has insufficient balance', async () => {
+ const fromAddress = addressWithoutFunds;
+ const transferAmount = new BigNumber(42);
+
+ const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress);
+ expect(fromAddressBalance).to.be.bignumber.equal(0);
+
+ await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
+ const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(
+ token.address,
+ fromAddress,
+ senderAddress,
+ );
+ expect(fromAddressAllowance).to.be.bignumber.equal(transferAmount);
+
+ return expect(
+ zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
+ ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
+ });
+ it('should successfully transfer tokens', async () => {
+ const fromAddress = coinbase;
+
+ const preBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
+ expect(preBalance).to.be.bignumber.equal(0);
+
+ const transferAmount = new BigNumber(42);
+ await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
+
+ await zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount);
+ const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
+ return expect(postBalance).to.be.bignumber.equal(transferAmount);
+ });
+ it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
+ const fromAddress = coinbase;
+ const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
+ return expect(
+ zeroEx.token.transferFromAsync(
+ nonExistentTokenAddress,
+ fromAddress,
+ toAddress,
+ senderAddress,
+ new BigNumber(42),
+ ),
+ ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist);
+ });
+ });
+ describe('#getBalanceAsync', () => {
+ describe('With web3 provider with accounts', () => {
+ it('should return the balance for an existing ERC20 token', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const balance = await zeroEx.token.getBalanceAsync(token.address, ownerAddress);
+ const expectedBalance = new BigNumber('1000000000000000000000000000');
+ return expect(balance).to.be.bignumber.equal(expectedBalance);
+ });
+ it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
+ const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
+ const ownerAddress = coinbase;
+ return expect(zeroEx.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress)).to.be.rejectedWith(
+ ZeroExError.TokenContractDoesNotExist,
+ );
+ });
+ it('should return a balance of 0 for a non-existent owner address', async () => {
+ const token = tokens[0];
+ const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593';
+ const balance = await zeroEx.token.getBalanceAsync(token.address, nonExistentOwner);
+ const expectedBalance = new BigNumber(0);
+ return expect(balance).to.be.bignumber.equal(expectedBalance);
+ });
+ });
+ describe('With web3 provider without accounts', () => {
+ let zeroExWithoutAccounts: ZeroEx;
+ before(async () => {
+ const hasAddresses = false;
+ const web3WithoutAccounts = web3Factory.create(hasAddresses);
+ zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider, config);
+ });
+ it('should return balance even when called with Web3 provider instance without addresses', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const balance = await zeroExWithoutAccounts.token.getBalanceAsync(token.address, ownerAddress);
+ const expectedBalance = new BigNumber('1000000000000000000000000000');
+ return expect(balance).to.be.bignumber.equal(expectedBalance);
+ });
+ });
+ });
+ describe('#setAllowanceAsync', () => {
+ it("should set the spender's allowance", async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const spenderAddress = addressWithoutFunds;
+
+ const allowanceBeforeSet = await zeroEx.token.getAllowanceAsync(
+ token.address,
+ ownerAddress,
+ spenderAddress,
+ );
+ const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
+ expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
+
+ const amountInBaseUnits = new BigNumber(50);
+ await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
+
+ const allowanceAfterSet = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
+ const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
+ return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
+ });
+ });
+ describe('#setUnlimitedAllowanceAsync', () => {
+ it("should set the unlimited spender's allowance", async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const spenderAddress = addressWithoutFunds;
+
+ await zeroEx.token.setUnlimitedAllowanceAsync(token.address, ownerAddress, spenderAddress);
+ const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
+ return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ it('should reduce the gas cost for transfers including tokens with unlimited allowance support', async () => {
+ const transferAmount = new BigNumber(5);
+ const zrx = tokenUtils.getProtocolTokenOrThrow();
+ const [, userWithNormalAllowance, userWithUnlimitedAllowance] = userAddresses;
+ await zeroEx.token.setAllowanceAsync(zrx.address, coinbase, userWithNormalAllowance, transferAmount);
+ await zeroEx.token.setUnlimitedAllowanceAsync(zrx.address, coinbase, userWithUnlimitedAllowance);
+
+ const initBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
+ const initBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
+ userWithUnlimitedAllowance,
+ );
+
+ await zeroEx.token.transferFromAsync(
+ zrx.address,
+ coinbase,
+ userWithNormalAllowance,
+ userWithNormalAllowance,
+ transferAmount,
+ );
+ await zeroEx.token.transferFromAsync(
+ zrx.address,
+ coinbase,
+ userWithUnlimitedAllowance,
+ userWithUnlimitedAllowance,
+ transferAmount,
+ );
+
+ const finalBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
+ const finalBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
+ userWithUnlimitedAllowance,
+ );
+
+ const normalGasCost = initBalanceWithNormalAllowance.minus(finalBalanceWithNormalAllowance);
+ const unlimitedGasCost = initBalanceWithUnlimitedAllowance.minus(finalBalanceWithUnlimitedAllowance);
+
+ // In theory the gas cost with unlimited allowance should be smaller, but with testrpc it's actually bigger.
+ // This needs to be investigated in ethereumjs-vm. This test is essentially a repro.
+ // TODO: Make this test pass with inverted assertion.
+ expect(unlimitedGasCost.toNumber()).to.be.gt(normalGasCost.toNumber());
+ });
+ });
+ describe('#getAllowanceAsync', () => {
+ describe('With web3 provider with accounts', () => {
+ it('should get the proxy allowance', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const spenderAddress = addressWithoutFunds;
+
+ const amountInBaseUnits = new BigNumber(50);
+ await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
+
+ const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
+ const expectedAllowance = amountInBaseUnits;
+ return expect(allowance).to.be.bignumber.equal(expectedAllowance);
+ });
+ it('should return 0 if no allowance set yet', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const spenderAddress = addressWithoutFunds;
+ const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
+ const expectedAllowance = new BigNumber(0);
+ return expect(allowance).to.be.bignumber.equal(expectedAllowance);
+ });
+ });
+ describe('With web3 provider without accounts', () => {
+ let zeroExWithoutAccounts: ZeroEx;
+ before(async () => {
+ const hasAddresses = false;
+ const web3WithoutAccounts = web3Factory.create(hasAddresses);
+ zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider, config);
+ });
+ it('should get the proxy allowance', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const spenderAddress = addressWithoutFunds;
+
+ const amountInBaseUnits = new BigNumber(50);
+ await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
+
+ const allowance = await zeroExWithoutAccounts.token.getAllowanceAsync(
+ token.address,
+ ownerAddress,
+ spenderAddress,
+ );
+ const expectedAllowance = amountInBaseUnits;
+ return expect(allowance).to.be.bignumber.equal(expectedAllowance);
+ });
+ });
+ });
+ describe('#getProxyAllowanceAsync', () => {
+ it('should get the proxy allowance', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+
+ const amountInBaseUnits = new BigNumber(50);
+ await zeroEx.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits);
+
+ const allowance = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
+ const expectedAllowance = amountInBaseUnits;
+ return expect(allowance).to.be.bignumber.equal(expectedAllowance);
+ });
+ });
+ describe('#setProxyAllowanceAsync', () => {
+ it('should set the proxy allowance', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+
+ const allowanceBeforeSet = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
+ const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
+ expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
+
+ const amountInBaseUnits = new BigNumber(50);
+ await zeroEx.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits);
+
+ const allowanceAfterSet = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
+ const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
+ return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
+ });
+ });
+ describe('#setUnlimitedProxyAllowanceAsync', () => {
+ it('should set the unlimited proxy allowance', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+
+ await zeroEx.token.setUnlimitedProxyAllowanceAsync(token.address, ownerAddress);
+ const allowance = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
+ return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ });
+ describe('#subscribe', () => {
+ const indexFilterValues = {};
+ let tokenAddress: string;
+ const transferAmount = new BigNumber(42);
+ const allowanceAmount = new BigNumber(42);
+ before(() => {
+ const token = tokens[0];
+ tokenAddress = token.address;
+ });
+ afterEach(() => {
+ zeroEx.token.unsubscribeAll();
+ });
+ // Hack: Mocha does not allow a test to be both async and have a `done` callback
+ // Since we need to await the receipt of the event in the `subscribe` callback,
+ // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
+ // wrap the rest of the test in an async block
+ // Source: https://github.com/mochajs/mocha/issues/2407
+ it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<TransferContractEventArgs>) => {
+ expect(logEvent.isRemoved).to.be.false();
+ expect(logEvent.log.logIndex).to.be.equal(0);
+ expect(logEvent.log.transactionIndex).to.be.equal(0);
+ expect(logEvent.log.blockNumber).to.be.a('number');
+ const args = logEvent.log.args;
+ expect(args._from).to.be.equal(coinbase);
+ expect(args._to).to.be.equal(addressWithoutFunds);
+ expect(args._value).to.be.bignumber.equal(transferAmount);
+ },
+ );
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callback);
+ await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
+ })().catch(done);
+ });
+ it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ expect(logEvent).to.not.be.undefined();
+ expect(logEvent.isRemoved).to.be.false();
+ const args = logEvent.log.args;
+ expect(args._owner).to.be.equal(coinbase);
+ expect(args._spender).to.be.equal(addressWithoutFunds);
+ expect(args._value).to.be.bignumber.equal(allowanceAmount);
+ },
+ );
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
+ await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
+ })().catch(done);
+ });
+ it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled);
+ const callbackToBeCalled = reportNodeCallbackErrors(done)();
+ const newProvider = web3Factory.getRpcProvider();
+ zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackToBeCalled);
+ await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
+ })().catch(done);
+ });
+ it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ const subscriptionToken = zeroEx.token.subscribe(
+ tokenAddress,
+ TokenEvents.Transfer,
+ indexFilterValues,
+ callbackNeverToBeCalled,
+ );
+ zeroEx.token.unsubscribe(subscriptionToken);
+ await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
+ done();
+ })().catch(done);
+ });
+ });
+ describe('#getLogsAsync', () => {
+ let tokenAddress: string;
+ let tokenTransferProxyAddress: string;
+ const blockRange: BlockRange = {
+ fromBlock: 0,
+ toBlock: BlockParamLiteral.Latest,
+ };
+ let txHash: string;
+ before(() => {
+ const token = tokens[0];
+ tokenAddress = token.address;
+ tokenTransferProxyAddress = zeroEx.proxy.getContractAddress();
+ });
+ it('should get logs with decoded args emitted by Approval', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const eventName = TokenEvents.Approval;
+ const indexFilterValues = {};
+ const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>(
+ tokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(logs[0].event).to.be.equal(eventName);
+ expect(args._owner).to.be.equal(coinbase);
+ expect(args._spender).to.be.equal(tokenTransferProxyAddress);
+ expect(args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ it('should only get the logs with the correct event name', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const differentEventName = TokenEvents.Transfer;
+ const indexFilterValues = {};
+ const logs = await zeroEx.token.getLogsAsync(
+ tokenAddress,
+ differentEventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(0);
+ });
+ it('should only get the logs with the correct indexed fields', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, addressWithoutFunds);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const eventName = TokenEvents.Approval;
+ const indexFilterValues = {
+ _owner: coinbase,
+ };
+ const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>(
+ tokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(args._owner).to.be.equal(coinbase);
+ });
+ });
});
// tslint:disable:max-file-line-count
diff --git a/packages/0x.js/test/utils/chai_setup.ts b/packages/0x.js/test/utils/chai_setup.ts
index 078edd309..e156b5f7c 100644
--- a/packages/0x.js/test/utils/chai_setup.ts
+++ b/packages/0x.js/test/utils/chai_setup.ts
@@ -4,10 +4,10 @@ import ChaiBigNumber = require('chai-bignumber');
import * as dirtyChai from 'dirty-chai';
export const chaiSetup = {
- configure() {
- chai.config.includeStack = true;
- chai.use(ChaiBigNumber());
- chai.use(dirtyChai);
- chai.use(chaiAsPromised);
- },
+ configure() {
+ chai.config.includeStack = true;
+ chai.use(ChaiBigNumber());
+ chai.use(dirtyChai);
+ chai.use(chaiAsPromised);
+ },
};
diff --git a/packages/0x.js/test/utils/constants.ts b/packages/0x.js/test/utils/constants.ts
index a9e665c25..b7161e9ab 100644
--- a/packages/0x.js/test/utils/constants.ts
+++ b/packages/0x.js/test/utils/constants.ts
@@ -1,11 +1,11 @@
export const constants = {
- NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
- RPC_URL: 'http://localhost:8545',
- ROPSTEN_NETWORK_ID: 3,
- KOVAN_NETWORK_ID: 42,
- TESTRPC_NETWORK_ID: 50,
- KOVAN_RPC_URL: 'https://kovan.infura.io/',
- ROPSTEN_RPC_URL: 'https://ropsten.infura.io/',
- ZRX_DECIMALS: 18,
- GAS_ESTIMATE: 500000,
+ NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
+ RPC_URL: 'http://localhost:8545',
+ ROPSTEN_NETWORK_ID: 3,
+ KOVAN_NETWORK_ID: 42,
+ TESTRPC_NETWORK_ID: 50,
+ KOVAN_RPC_URL: 'https://kovan.infura.io/',
+ ROPSTEN_RPC_URL: 'https://ropsten.infura.io/',
+ ZRX_DECIMALS: 18,
+ GAS_ESTIMATE: 500000,
};
diff --git a/packages/0x.js/test/utils/fill_scenarios.ts b/packages/0x.js/test/utils/fill_scenarios.ts
index 1a61487f4..1d4b8b6f4 100644
--- a/packages/0x.js/test/utils/fill_scenarios.ts
+++ b/packages/0x.js/test/utils/fill_scenarios.ts
@@ -11,192 +11,192 @@ import { constants } from './constants';
const INITIAL_COINBASE_TOKEN_SUPPLY_IN_UNITS = new BigNumber(100);
export class FillScenarios {
- private _zeroEx: ZeroEx;
- private _userAddresses: string[];
- private _tokens: Token[];
- private _coinbase: string;
- private _zrxTokenAddress: string;
- private _exchangeContractAddress: string;
- constructor(
- zeroEx: ZeroEx,
- userAddresses: string[],
- tokens: Token[],
- zrxTokenAddress: string,
- exchangeContractAddress: string,
- ) {
- this._zeroEx = zeroEx;
- this._userAddresses = userAddresses;
- this._tokens = tokens;
- this._coinbase = userAddresses[0];
- this._zrxTokenAddress = zrxTokenAddress;
- this._exchangeContractAddress = exchangeContractAddress;
- }
- public async initTokenBalancesAsync() {
- const web3Wrapper = (this._zeroEx as any)._web3Wrapper as Web3Wrapper;
- for (const token of this._tokens) {
- if (token.symbol !== 'ZRX' && token.symbol !== 'WETH') {
- const contractInstance = web3Wrapper.getContractInstance(
- artifacts.DummyTokenArtifact.abi,
- token.address,
- );
- const defaults = {};
- const dummyToken = new DummyTokenContract(contractInstance, defaults);
- const tokenSupply = ZeroEx.toBaseUnitAmount(INITIAL_COINBASE_TOKEN_SUPPLY_IN_UNITS, token.decimals);
- const txHash = await dummyToken.setBalance.sendTransactionAsync(this._coinbase, tokenSupply, {
- from: this._coinbase,
- });
- await this._zeroEx.awaitTransactionMinedAsync(txHash);
- }
- }
- }
- public async createFillableSignedOrderAsync(
- makerTokenAddress: string,
- takerTokenAddress: string,
- makerAddress: string,
- takerAddress: string,
- fillableAmount: BigNumber,
- expirationUnixTimestampSec?: BigNumber,
- ): Promise<SignedOrder> {
- return this.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- fillableAmount,
- expirationUnixTimestampSec,
- );
- }
- public async createFillableSignedOrderWithFeesAsync(
- makerTokenAddress: string,
- takerTokenAddress: string,
- makerFee: BigNumber,
- takerFee: BigNumber,
- makerAddress: string,
- takerAddress: string,
- fillableAmount: BigNumber,
- feeRecepient: string,
- expirationUnixTimestampSec?: BigNumber,
- ): Promise<SignedOrder> {
- return this._createAsymmetricFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- fillableAmount,
- feeRecepient,
- expirationUnixTimestampSec,
- );
- }
- public async createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress: string,
- takerTokenAddress: string,
- makerAddress: string,
- takerAddress: string,
- makerFillableAmount: BigNumber,
- takerFillableAmount: BigNumber,
- expirationUnixTimestampSec?: BigNumber,
- ): Promise<SignedOrder> {
- const makerFee = new BigNumber(0);
- const takerFee = new BigNumber(0);
- const feeRecepient = constants.NULL_ADDRESS;
- return this._createAsymmetricFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- makerFillableAmount,
- takerFillableAmount,
- feeRecepient,
- expirationUnixTimestampSec,
- );
- }
- public async createPartiallyFilledSignedOrderAsync(
- makerTokenAddress: string,
- takerTokenAddress: string,
- takerAddress: string,
- fillableAmount: BigNumber,
- partialFillAmount: BigNumber,
- ) {
- const [makerAddress] = this._userAddresses;
- const signedOrder = await this.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- fillableAmount,
- );
- const shouldThrowOnInsufficientBalanceOrAllowance = false;
- await this._zeroEx.exchange.fillOrderAsync(
- signedOrder,
- partialFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- return signedOrder;
- }
- private async _createAsymmetricFillableSignedOrderWithFeesAsync(
- makerTokenAddress: string,
- takerTokenAddress: string,
- makerFee: BigNumber,
- takerFee: BigNumber,
- makerAddress: string,
- takerAddress: string,
- makerFillableAmount: BigNumber,
- takerFillableAmount: BigNumber,
- feeRecepient: string,
- expirationUnixTimestampSec?: BigNumber,
- ): Promise<SignedOrder> {
- await Promise.all([
- this._increaseBalanceAndAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount),
- this._increaseBalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount),
- ]);
- await Promise.all([
- this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee),
- this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, takerAddress, takerFee),
- ]);
+ private _zeroEx: ZeroEx;
+ private _userAddresses: string[];
+ private _tokens: Token[];
+ private _coinbase: string;
+ private _zrxTokenAddress: string;
+ private _exchangeContractAddress: string;
+ constructor(
+ zeroEx: ZeroEx,
+ userAddresses: string[],
+ tokens: Token[],
+ zrxTokenAddress: string,
+ exchangeContractAddress: string,
+ ) {
+ this._zeroEx = zeroEx;
+ this._userAddresses = userAddresses;
+ this._tokens = tokens;
+ this._coinbase = userAddresses[0];
+ this._zrxTokenAddress = zrxTokenAddress;
+ this._exchangeContractAddress = exchangeContractAddress;
+ }
+ public async initTokenBalancesAsync() {
+ const web3Wrapper = (this._zeroEx as any)._web3Wrapper as Web3Wrapper;
+ for (const token of this._tokens) {
+ if (token.symbol !== 'ZRX' && token.symbol !== 'WETH') {
+ const contractInstance = web3Wrapper.getContractInstance(
+ artifacts.DummyTokenArtifact.abi,
+ token.address,
+ );
+ const defaults = {};
+ const dummyToken = new DummyTokenContract(contractInstance, defaults);
+ const tokenSupply = ZeroEx.toBaseUnitAmount(INITIAL_COINBASE_TOKEN_SUPPLY_IN_UNITS, token.decimals);
+ const txHash = await dummyToken.setBalance.sendTransactionAsync(this._coinbase, tokenSupply, {
+ from: this._coinbase,
+ });
+ await this._zeroEx.awaitTransactionMinedAsync(txHash);
+ }
+ }
+ }
+ public async createFillableSignedOrderAsync(
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ makerAddress: string,
+ takerAddress: string,
+ fillableAmount: BigNumber,
+ expirationUnixTimestampSec?: BigNumber,
+ ): Promise<SignedOrder> {
+ return this.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ fillableAmount,
+ expirationUnixTimestampSec,
+ );
+ }
+ public async createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ makerFee: BigNumber,
+ takerFee: BigNumber,
+ makerAddress: string,
+ takerAddress: string,
+ fillableAmount: BigNumber,
+ feeRecepient: string,
+ expirationUnixTimestampSec?: BigNumber,
+ ): Promise<SignedOrder> {
+ return this._createAsymmetricFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ fillableAmount,
+ feeRecepient,
+ expirationUnixTimestampSec,
+ );
+ }
+ public async createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ makerAddress: string,
+ takerAddress: string,
+ makerFillableAmount: BigNumber,
+ takerFillableAmount: BigNumber,
+ expirationUnixTimestampSec?: BigNumber,
+ ): Promise<SignedOrder> {
+ const makerFee = new BigNumber(0);
+ const takerFee = new BigNumber(0);
+ const feeRecepient = constants.NULL_ADDRESS;
+ return this._createAsymmetricFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ makerFillableAmount,
+ takerFillableAmount,
+ feeRecepient,
+ expirationUnixTimestampSec,
+ );
+ }
+ public async createPartiallyFilledSignedOrderAsync(
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ takerAddress: string,
+ fillableAmount: BigNumber,
+ partialFillAmount: BigNumber,
+ ) {
+ const [makerAddress] = this._userAddresses;
+ const signedOrder = await this.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ fillableAmount,
+ );
+ const shouldThrowOnInsufficientBalanceOrAllowance = false;
+ await this._zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ partialFillAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ return signedOrder;
+ }
+ private async _createAsymmetricFillableSignedOrderWithFeesAsync(
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ makerFee: BigNumber,
+ takerFee: BigNumber,
+ makerAddress: string,
+ takerAddress: string,
+ makerFillableAmount: BigNumber,
+ takerFillableAmount: BigNumber,
+ feeRecepient: string,
+ expirationUnixTimestampSec?: BigNumber,
+ ): Promise<SignedOrder> {
+ await Promise.all([
+ this._increaseBalanceAndAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount),
+ this._increaseBalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount),
+ ]);
+ await Promise.all([
+ this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee),
+ this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, takerAddress, takerFee),
+ ]);
- const signedOrder = await orderFactory.createSignedOrderAsync(
- this._zeroEx,
- makerAddress,
- takerAddress,
- makerFee,
- takerFee,
- makerFillableAmount,
- makerTokenAddress,
- takerFillableAmount,
- takerTokenAddress,
- this._exchangeContractAddress,
- feeRecepient,
- expirationUnixTimestampSec,
- );
- return signedOrder;
- }
- private async _increaseBalanceAndAllowanceAsync(
- tokenAddress: string,
- address: string,
- amount: BigNumber,
- ): Promise<void> {
- if (amount.isZero() || address === ZeroEx.NULL_ADDRESS) {
- return; // noop
- }
- await Promise.all([
- this._increaseBalanceAsync(tokenAddress, address, amount),
- this._increaseAllowanceAsync(tokenAddress, address, amount),
- ]);
- }
- private async _increaseBalanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
- await this._zeroEx.token.transferAsync(tokenAddress, this._coinbase, address, amount);
- }
- private async _increaseAllowanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
- const oldMakerAllowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, address);
- const newMakerAllowance = oldMakerAllowance.plus(amount);
- await this._zeroEx.token.setProxyAllowanceAsync(tokenAddress, address, newMakerAllowance);
- }
+ const signedOrder = await orderFactory.createSignedOrderAsync(
+ this._zeroEx,
+ makerAddress,
+ takerAddress,
+ makerFee,
+ takerFee,
+ makerFillableAmount,
+ makerTokenAddress,
+ takerFillableAmount,
+ takerTokenAddress,
+ this._exchangeContractAddress,
+ feeRecepient,
+ expirationUnixTimestampSec,
+ );
+ return signedOrder;
+ }
+ private async _increaseBalanceAndAllowanceAsync(
+ tokenAddress: string,
+ address: string,
+ amount: BigNumber,
+ ): Promise<void> {
+ if (amount.isZero() || address === ZeroEx.NULL_ADDRESS) {
+ return; // noop
+ }
+ await Promise.all([
+ this._increaseBalanceAsync(tokenAddress, address, amount),
+ this._increaseAllowanceAsync(tokenAddress, address, amount),
+ ]);
+ }
+ private async _increaseBalanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
+ await this._zeroEx.token.transferAsync(tokenAddress, this._coinbase, address, amount);
+ }
+ private async _increaseAllowanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
+ const oldMakerAllowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, address);
+ const newMakerAllowance = oldMakerAllowance.plus(amount);
+ await this._zeroEx.token.setProxyAllowanceAsync(tokenAddress, address, newMakerAllowance);
+ }
}
diff --git a/packages/0x.js/test/utils/order_factory.ts b/packages/0x.js/test/utils/order_factory.ts
index 08f2081a4..b65920e64 100644
--- a/packages/0x.js/test/utils/order_factory.ts
+++ b/packages/0x.js/test/utils/order_factory.ts
@@ -6,41 +6,41 @@ import { SignedOrder, ZeroEx } from '../../src';
const SHOULD_ADD_PERSONAL_MESSAGE_PREFIX = false;
export const orderFactory = {
- async createSignedOrderAsync(
- zeroEx: ZeroEx,
- maker: string,
- taker: string,
- makerFee: BigNumber,
- takerFee: BigNumber,
- makerTokenAmount: BigNumber,
- makerTokenAddress: string,
- takerTokenAmount: BigNumber,
- takerTokenAddress: string,
- exchangeContractAddress: string,
- feeRecipient: string,
- expirationUnixTimestampSecIfExists?: BigNumber,
- ): Promise<SignedOrder> {
- const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite
- const expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSecIfExists)
- ? defaultExpirationUnixTimestampSec
- : expirationUnixTimestampSecIfExists;
- const order = {
- maker,
- taker,
- makerFee,
- takerFee,
- makerTokenAmount,
- takerTokenAmount,
- makerTokenAddress,
- takerTokenAddress,
- salt: ZeroEx.generatePseudoRandomSalt(),
- exchangeContractAddress,
- feeRecipient,
- expirationUnixTimestampSec,
- };
- const orderHash = ZeroEx.getOrderHashHex(order);
- const ecSignature = await zeroEx.signOrderHashAsync(orderHash, maker, SHOULD_ADD_PERSONAL_MESSAGE_PREFIX);
- const signedOrder: SignedOrder = _.assign(order, { ecSignature });
- return signedOrder;
- },
+ async createSignedOrderAsync(
+ zeroEx: ZeroEx,
+ maker: string,
+ taker: string,
+ makerFee: BigNumber,
+ takerFee: BigNumber,
+ makerTokenAmount: BigNumber,
+ makerTokenAddress: string,
+ takerTokenAmount: BigNumber,
+ takerTokenAddress: string,
+ exchangeContractAddress: string,
+ feeRecipient: string,
+ expirationUnixTimestampSecIfExists?: BigNumber,
+ ): Promise<SignedOrder> {
+ const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite
+ const expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSecIfExists)
+ ? defaultExpirationUnixTimestampSec
+ : expirationUnixTimestampSecIfExists;
+ const order = {
+ maker,
+ taker,
+ makerFee,
+ takerFee,
+ makerTokenAmount,
+ takerTokenAmount,
+ makerTokenAddress,
+ takerTokenAddress,
+ salt: ZeroEx.generatePseudoRandomSalt(),
+ exchangeContractAddress,
+ feeRecipient,
+ expirationUnixTimestampSec,
+ };
+ const orderHash = ZeroEx.getOrderHashHex(order);
+ const ecSignature = await zeroEx.signOrderHashAsync(orderHash, maker, SHOULD_ADD_PERSONAL_MESSAGE_PREFIX);
+ const signedOrder: SignedOrder = _.assign(order, { ecSignature });
+ return signedOrder;
+ },
};
diff --git a/packages/0x.js/test/utils/report_callback_errors.ts b/packages/0x.js/test/utils/report_callback_errors.ts
index 27c9745c9..0b619f73e 100644
--- a/packages/0x.js/test/utils/report_callback_errors.ts
+++ b/packages/0x.js/test/utils/report_callback_errors.ts
@@ -6,61 +6,61 @@ import { DoneCallback } from '../../src/types';
const expect = chai.expect;
export const reportNoErrorCallbackErrors = (done: DoneCallback, expectToBeCalledOnce = true) => {
- return <T>(f?: (value: T) => void) => {
- const wrapped = (value: T) => {
- if (_.isUndefined(f)) {
- done();
- return;
- }
- try {
- f(value);
- if (expectToBeCalledOnce) {
- done();
- }
- } catch (err) {
- done(err);
- }
- };
- return wrapped;
- };
+ return <T>(f?: (value: T) => void) => {
+ const wrapped = (value: T) => {
+ if (_.isUndefined(f)) {
+ done();
+ return;
+ }
+ try {
+ f(value);
+ if (expectToBeCalledOnce) {
+ done();
+ }
+ } catch (err) {
+ done(err);
+ }
+ };
+ return wrapped;
+ };
};
export const reportNodeCallbackErrors = (done: DoneCallback, expectToBeCalledOnce = true) => {
- return <T>(f?: (value: T) => void) => {
- const wrapped = (error: Error | null, value: T | undefined) => {
- if (!_.isNull(error)) {
- done(error);
- } else {
- if (_.isUndefined(f)) {
- done();
- return;
- }
- try {
- f(value as T);
- if (expectToBeCalledOnce) {
- done();
- }
- } catch (err) {
- done(err);
- }
- }
- };
- return wrapped;
- };
+ return <T>(f?: (value: T) => void) => {
+ const wrapped = (error: Error | null, value: T | undefined) => {
+ if (!_.isNull(error)) {
+ done(error);
+ } else {
+ if (_.isUndefined(f)) {
+ done();
+ return;
+ }
+ try {
+ f(value as T);
+ if (expectToBeCalledOnce) {
+ done();
+ }
+ } catch (err) {
+ done(err);
+ }
+ }
+ };
+ return wrapped;
+ };
};
export const assertNodeCallbackError = (done: DoneCallback, errMsg: string) => {
- const wrapped = <T>(error: Error | null, value: T | undefined) => {
- if (_.isNull(error)) {
- done(new Error('Expected callback to receive an error'));
- } else {
- try {
- expect(error.message).to.be.equal(errMsg);
- done();
- } catch (err) {
- done(err);
- }
- }
- };
- return wrapped;
+ const wrapped = <T>(error: Error | null, value: T | undefined) => {
+ if (_.isNull(error)) {
+ done(new Error('Expected callback to receive an error'));
+ } else {
+ try {
+ expect(error.message).to.be.equal(errMsg);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ }
+ };
+ return wrapped;
};
diff --git a/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts b/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts
index 53f2be83d..466b0f350 100644
--- a/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts
+++ b/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts
@@ -6,22 +6,22 @@ import { JSONRPCPayload } from '../../../src/types';
* Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class EmptyWalletSubprovider {
- // This method needs to be here to satisfy the interface but linter wants it to be static.
- // tslint:disable-next-line:prefer-function-over-method
- public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) {
- switch (payload.method) {
- case 'eth_accounts':
- end(null, []);
- return;
+ // This method needs to be here to satisfy the interface but linter wants it to be static.
+ // tslint:disable-next-line:prefer-function-over-method
+ public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) {
+ switch (payload.method) {
+ case 'eth_accounts':
+ end(null, []);
+ return;
- default:
- next();
- return;
- }
- }
- // Required to implement this method despite not needing it for this subprovider
- // tslint:disable-next-line:prefer-function-over-method
- public setEngine(engine: any) {
- // noop
- }
+ default:
+ next();
+ return;
+ }
+ }
+ // Required to implement this method despite not needing it for this subprovider
+ // tslint:disable-next-line:prefer-function-over-method
+ public setEngine(engine: any) {
+ // noop
+ }
}
diff --git a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts
index e1113a851..307bfb188 100644
--- a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts
+++ b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts
@@ -9,26 +9,26 @@ import { JSONRPCPayload } from '../../../src/types';
* Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class FakeGasEstimateSubprovider {
- private _constantGasAmount: number;
- constructor(constantGasAmount: number) {
- this._constantGasAmount = constantGasAmount;
- }
- // This method needs to be here to satisfy the interface but linter wants it to be static.
- // tslint:disable-next-line:prefer-function-over-method
- public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) {
- switch (payload.method) {
- case 'eth_estimateGas':
- end(null, this._constantGasAmount);
- return;
+ private _constantGasAmount: number;
+ constructor(constantGasAmount: number) {
+ this._constantGasAmount = constantGasAmount;
+ }
+ // This method needs to be here to satisfy the interface but linter wants it to be static.
+ // tslint:disable-next-line:prefer-function-over-method
+ public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) {
+ switch (payload.method) {
+ case 'eth_estimateGas':
+ end(null, this._constantGasAmount);
+ return;
- default:
- next();
- return;
- }
- }
- // Required to implement this method despite not needing it for this subprovider
- // tslint:disable-next-line:prefer-function-over-method
- public setEngine(engine: any) {
- // noop
- }
+ default:
+ next();
+ return;
+ }
+ }
+ // Required to implement this method despite not needing it for this subprovider
+ // tslint:disable-next-line:prefer-function-over-method
+ public setEngine(engine: any) {
+ // noop
+ }
}
diff --git a/packages/0x.js/test/utils/token_utils.ts b/packages/0x.js/test/utils/token_utils.ts
index d3fc22ff4..9cbefcf8e 100644
--- a/packages/0x.js/test/utils/token_utils.ts
+++ b/packages/0x.js/test/utils/token_utils.ts
@@ -6,28 +6,28 @@ const PROTOCOL_TOKEN_SYMBOL = 'ZRX';
const WETH_TOKEN_SYMBOL = 'WETH';
export class TokenUtils {
- private _tokens: Token[];
- constructor(tokens: Token[]) {
- this._tokens = tokens;
- }
- public getProtocolTokenOrThrow(): Token {
- const zrxToken = _.find(this._tokens, { symbol: PROTOCOL_TOKEN_SYMBOL });
- if (_.isUndefined(zrxToken)) {
- throw new Error(InternalZeroExError.ZrxNotInTokenRegistry);
- }
- return zrxToken;
- }
- public getWethTokenOrThrow(): Token {
- const wethToken = _.find(this._tokens, { symbol: WETH_TOKEN_SYMBOL });
- if (_.isUndefined(wethToken)) {
- throw new Error(InternalZeroExError.WethNotInTokenRegistry);
- }
- return wethToken;
- }
- public getDummyTokens(): Token[] {
- const dummyTokens = _.filter(this._tokens, token => {
- return !_.includes([PROTOCOL_TOKEN_SYMBOL, WETH_TOKEN_SYMBOL], token.symbol);
- });
- return dummyTokens;
- }
+ private _tokens: Token[];
+ constructor(tokens: Token[]) {
+ this._tokens = tokens;
+ }
+ public getProtocolTokenOrThrow(): Token {
+ const zrxToken = _.find(this._tokens, { symbol: PROTOCOL_TOKEN_SYMBOL });
+ if (_.isUndefined(zrxToken)) {
+ throw new Error(InternalZeroExError.ZrxNotInTokenRegistry);
+ }
+ return zrxToken;
+ }
+ public getWethTokenOrThrow(): Token {
+ const wethToken = _.find(this._tokens, { symbol: WETH_TOKEN_SYMBOL });
+ if (_.isUndefined(wethToken)) {
+ throw new Error(InternalZeroExError.WethNotInTokenRegistry);
+ }
+ return wethToken;
+ }
+ public getDummyTokens(): Token[] {
+ const dummyTokens = _.filter(this._tokens, token => {
+ return !_.includes([PROTOCOL_TOKEN_SYMBOL, WETH_TOKEN_SYMBOL], token.symbol);
+ });
+ return dummyTokens;
+ }
}
diff --git a/packages/0x.js/test/utils/web3_factory.ts b/packages/0x.js/test/utils/web3_factory.ts
index 26c26e03d..640b96256 100644
--- a/packages/0x.js/test/utils/web3_factory.ts
+++ b/packages/0x.js/test/utils/web3_factory.ts
@@ -19,24 +19,24 @@ import { constants } from './constants';
import * as Web3 from 'web3';
export const web3Factory = {
- create(hasAddresses: boolean = true): Web3 {
- const provider = this.getRpcProvider(hasAddresses);
- const web3 = new Web3();
- web3.setProvider(provider);
- return web3;
- },
- getRpcProvider(hasAddresses: boolean = true): Web3.Provider {
- const provider = new ProviderEngine();
- if (!hasAddresses) {
- provider.addProvider(new EmptyWalletSubprovider());
- }
- provider.addProvider(new FakeGasEstimateSubprovider(constants.GAS_ESTIMATE));
- provider.addProvider(
- new RpcSubprovider({
- rpcUrl: constants.RPC_URL,
- }),
- );
- provider.start();
- return provider;
- },
+ create(hasAddresses: boolean = true): Web3 {
+ const provider = this.getRpcProvider(hasAddresses);
+ const web3 = new Web3();
+ web3.setProvider(provider);
+ return web3;
+ },
+ getRpcProvider(hasAddresses: boolean = true): Web3.Provider {
+ const provider = new ProviderEngine();
+ if (!hasAddresses) {
+ provider.addProvider(new EmptyWalletSubprovider());
+ }
+ provider.addProvider(new FakeGasEstimateSubprovider(constants.GAS_ESTIMATE));
+ provider.addProvider(
+ new RpcSubprovider({
+ rpcUrl: constants.RPC_URL,
+ }),
+ );
+ provider.start();
+ return provider;
+ },
};
diff --git a/packages/0x.js/tsconfig.json b/packages/0x.js/tsconfig.json
index 117f51e83..0666fbd82 100644
--- a/packages/0x.js/tsconfig.json
+++ b/packages/0x.js/tsconfig.json
@@ -1,16 +1,16 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib",
- "noImplicitThis": false
- },
- "include": [
- "./src/**/*",
- "./test/**/*",
- "../../node_modules/types-bn/index.d.ts",
- "../../node_modules/types-ethereumjs-util/index.d.ts",
- "../../node_modules/web3-typescript-typings/index.d.ts",
- "../../node_modules/chai-typescript-typings/index.d.ts",
- "../../node_modules/chai-as-promised-typescript-typings/index.d.ts"
- ]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib",
+ "noImplicitThis": false
+ },
+ "include": [
+ "./src/**/*",
+ "./test/**/*",
+ "../../node_modules/types-bn/index.d.ts",
+ "../../node_modules/types-ethereumjs-util/index.d.ts",
+ "../../node_modules/web3-typescript-typings/index.d.ts",
+ "../../node_modules/chai-typescript-typings/index.d.ts",
+ "../../node_modules/chai-as-promised-typescript-typings/index.d.ts"
+ ]
}
diff --git a/packages/0x.js/tslint.json b/packages/0x.js/tslint.json
index ffaefe83a..e63054bfc 100644
--- a/packages/0x.js/tslint.json
+++ b/packages/0x.js/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}