aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/0x.js.ts23
-rw-r--r--src/artifacts/EtherToken.json2
-rw-r--r--src/artifacts/Mintable.json2
-rw-r--r--src/artifacts/Proxy.json2
-rw-r--r--src/artifacts/Token.json2
-rw-r--r--src/artifacts/TokenRegistry.json2
-rw-r--r--src/contract_wrappers/exchange_wrapper.ts85
-rw-r--r--src/contract_wrappers/token_wrapper.ts4
-rw-r--r--src/types.ts54
-rw-r--r--src/utils/assert.ts4
-rw-r--r--src/utils/utils.ts7
-rw-r--r--test/0x.js_test.ts6
-rw-r--r--test/exchange_wrapper_test.ts166
-rw-r--r--test/utils/fill_scenarios.ts22
14 files changed, 354 insertions, 27 deletions
diff --git a/src/0x.js.ts b/src/0x.js.ts
index 7cf313666..850827fee 100644
--- a/src/0x.js.ts
+++ b/src/0x.js.ts
@@ -17,7 +17,7 @@ import {TokenRegistryWrapper} from './contract_wrappers/token_registry_wrapper';
import {ecSignatureSchema} from './schemas/ec_signature_schema';
import {TokenWrapper} from './contract_wrappers/token_wrapper';
import {SolidityTypes, ECSignature, ZeroExError} from './types';
-import {Order} from './types';
+import {Order, SignedOrder} from './types';
import {orderSchema} from './schemas/order_schemas';
import * as ExchangeArtifacts from './artifacts/Exchange.json';
@@ -69,11 +69,16 @@ export class ZeroEx {
const salt = randomNumber.times(factor).round();
return salt;
}
- /** Checks if order hash is valid */
- public static isValidOrderHash(orderHash: string): boolean {
- assert.isString('orderHash', orderHash);
- const isValid = /^0x[0-9A-F]{64}$/i.test(orderHash);
- return isValid;
+ /**
+ * 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.
+ */
+ public static isValidOrderHash(orderHashHex: 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('orderHashHex', orderHashHex);
+ const isValidOrderHash = utils.isValidOrderHash(orderHashHex);
+ return isValidOrderHash;
}
/**
* A unit amount is defined as the amount of a token above the specified decimal places (integer part).
@@ -110,9 +115,9 @@ export class ZeroEx {
/**
* Sets a new provider for the web3 instance used by 0x.js
*/
- public setProvider(provider: Web3.Provider) {
+ public async setProviderAsync(provider: Web3.Provider) {
this.web3Wrapper.setProvider(provider);
- this.exchange.invalidateContractInstance();
+ await this.exchange.invalidateContractInstanceAsync();
this.tokenRegistry.invalidateContractInstance();
this.token.invalidateContractInstances();
}
@@ -132,7 +137,7 @@ export class ZeroEx {
/**
* Computes the orderHash for a given order and returns it as a hex encoded string.
*/
- public async getOrderHashHexAsync(order: Order): Promise<string> {
+ public async getOrderHashHexAsync(order: Order|SignedOrder): Promise<string> {
const exchangeContractAddr = await this.getExchangeAddressAsync();
assert.doesConformToSchema('order',
SchemaValidator.convertToJSONSchemaCompatibleObject(order as object),
diff --git a/src/artifacts/EtherToken.json b/src/artifacts/EtherToken.json
index 0593cdc95..ae79d6bf5 100644
--- a/src/artifacts/EtherToken.json
+++ b/src/artifacts/EtherToken.json
@@ -336,4 +336,4 @@
},
"schema_version": "0.0.5",
"updated_at": 1495042008609
-} \ No newline at end of file
+}
diff --git a/src/artifacts/Mintable.json b/src/artifacts/Mintable.json
index 026f294f2..b508eaee7 100644
--- a/src/artifacts/Mintable.json
+++ b/src/artifacts/Mintable.json
@@ -186,4 +186,4 @@
"networks": {},
"schema_version": "0.0.5",
"updated_at": 1495030728122
-} \ No newline at end of file
+}
diff --git a/src/artifacts/Proxy.json b/src/artifacts/Proxy.json
index 0d6faef35..d02804096 100644
--- a/src/artifacts/Proxy.json
+++ b/src/artifacts/Proxy.json
@@ -264,4 +264,4 @@
},
"schema_version": "0.0.5",
"updated_at": 1495042008598
-} \ No newline at end of file
+}
diff --git a/src/artifacts/Token.json b/src/artifacts/Token.json
index 3bbcead41..4843cc2ca 100644
--- a/src/artifacts/Token.json
+++ b/src/artifacts/Token.json
@@ -173,4 +173,4 @@
"networks": {},
"schema_version": "0.0.5",
"updated_at": 1495030728125
-} \ No newline at end of file
+}
diff --git a/src/artifacts/TokenRegistry.json b/src/artifacts/TokenRegistry.json
index f9f4fafbc..6ecdfa079 100644
--- a/src/artifacts/TokenRegistry.json
+++ b/src/artifacts/TokenRegistry.json
@@ -999,4 +999,4 @@
},
"schema_version": "0.0.5",
"updated_at": 1495042008599
-} \ No newline at end of file
+}
diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts
index 4aa532bdd..b67fd33ac 100644
--- a/src/contract_wrappers/exchange_wrapper.ts
+++ b/src/contract_wrappers/exchange_wrapper.ts
@@ -1,4 +1,6 @@
import * as _ from 'lodash';
+import * as BigNumber from 'bignumber.js';
+import promisify = require('es6-promisify');
import {Web3Wrapper} from '../web3_wrapper';
import {
ECSignature,
@@ -9,9 +11,17 @@ import {
OrderAddresses,
SignedOrder,
ContractEvent,
+ ZeroExError,
+ ExchangeEvents,
+ SubscriptionOpts,
+ IndexFilterValues,
+ CreateContractEvent,
+ ContractEventObj,
+ EventCallback,
ContractResponse,
} from '../types';
import {assert} from '../utils/assert';
+import {utils} from '../utils/utils';
import {ContractWrapper} from './contract_wrapper';
import * as ExchangeArtifacts from '../artifacts/Exchange.json';
import {ecSignatureSchema} from '../schemas/ec_signature_schema';
@@ -30,12 +40,15 @@ export class ExchangeWrapper extends ContractWrapper {
[ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FILL_BALANCE_ALLOWANCE_ERROR,
};
private exchangeContractIfExists?: ExchangeContract;
+ private exchangeLogEventObjs: ContractEventObj[];
private tokenWrapper: TokenWrapper;
constructor(web3Wrapper: Web3Wrapper, tokenWrapper: TokenWrapper) {
super(web3Wrapper);
this.tokenWrapper = tokenWrapper;
+ this.exchangeLogEventObjs = [];
}
- public invalidateContractInstance(): void {
+ public async invalidateContractInstanceAsync(): Promise<void> {
+ await this.stopWatchingExchangeLogEventsAsync();
delete this.exchangeContractIfExists;
}
public async isValidSignatureAsync(dataHex: string, ecSignature: ECSignature,
@@ -60,6 +73,44 @@ export class ExchangeWrapper extends ContractWrapper {
return isValidSignature;
}
/**
+ * 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.
+ */
+ public async getUnavailableTakerAmountAsync(orderHashHex: string): Promise<BigNumber.BigNumber> {
+ assert.isValidOrderHash('orderHashHex', orderHashHex);
+
+ const exchangeContract = await this.getExchangeContractAsync();
+ let unavailableAmountInBaseUnits = await exchangeContract.getUnavailableValueT.call(orderHashHex);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ unavailableAmountInBaseUnits = new BigNumber(unavailableAmountInBaseUnits);
+ return unavailableAmountInBaseUnits;
+ }
+ /**
+ * Retrieve the takerAmount of an order that has already been filled.
+ */
+ public async getFilledTakerAmountAsync(orderHashHex: string): Promise<BigNumber.BigNumber> {
+ assert.isValidOrderHash('orderHashHex', orderHashHex);
+
+ const exchangeContract = await this.getExchangeContractAsync();
+ let fillAmountInBaseUnits = await exchangeContract.filled.call(orderHashHex);
+ // 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.
+ */
+ public async getCanceledTakerAmountAsync(orderHashHex: string): Promise<BigNumber.BigNumber> {
+ assert.isValidOrderHash('orderHashHex', orderHashHex);
+
+ const exchangeContract = await this.getExchangeContractAsync();
+ let cancelledAmountInBaseUnits = await exchangeContract.cancelled.call(orderHashHex);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ cancelledAmountInBaseUnits = new BigNumber(cancelledAmountInBaseUnits);
+ return cancelledAmountInBaseUnits;
+ }
+ /**
* Fills a signed order with a fillAmount 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,
@@ -121,6 +172,38 @@ export class ExchangeWrapper extends ContractWrapper {
);
this.throwErrorLogsAsErrors(response.logs);
}
+ /**
+ * Subscribe to an event type emitted by the Exchange smart contract
+ */
+ public async subscribeAsync(eventName: ExchangeEvents, subscriptionOpts: SubscriptionOpts,
+ indexFilterValues: IndexFilterValues, callback: EventCallback) {
+ const exchangeContract = await this.getExchangeContractAsync();
+ let createLogEvent: CreateContractEvent;
+ switch (eventName) {
+ case ExchangeEvents.LogFill:
+ createLogEvent = exchangeContract.LogFill;
+ break;
+ case ExchangeEvents.LogError:
+ createLogEvent = exchangeContract.LogError;
+ break;
+ case ExchangeEvents.LogCancel:
+ createLogEvent = exchangeContract.LogCancel;
+ break;
+ default:
+ utils.spawnSwitchErr('ExchangeEvents', eventName);
+ return;
+ }
+
+ const logEventObj: ContractEventObj = createLogEvent(indexFilterValues, subscriptionOpts);
+ logEventObj.watch(callback);
+ this.exchangeLogEventObjs.push(logEventObj);
+ }
+ private async stopWatchingExchangeLogEventsAsync() {
+ for (const logEventObj of this.exchangeLogEventObjs) {
+ await promisify(logEventObj.stopWatching, logEventObj)();
+ }
+ this.exchangeLogEventObjs = [];
+ }
private async validateFillOrderAndThrowIfInvalidAsync(signedOrder: SignedOrder,
fillTakerAmount: BigNumber.BigNumber,
senderAddress: string): Promise<void> {
diff --git a/src/contract_wrappers/token_wrapper.ts b/src/contract_wrappers/token_wrapper.ts
index cedbfbdae..69bcc9024 100644
--- a/src/contract_wrappers/token_wrapper.ts
+++ b/src/contract_wrappers/token_wrapper.ts
@@ -28,8 +28,7 @@ export class TokenWrapper extends ContractWrapper {
const tokenContract = await this.getTokenContractAsync(tokenAddress);
let balance = await tokenContract.balanceOf.call(ownerAddress);
- // The BigNumber instance returned by Web3 is of a much older version then our own, we therefore
- // should always re-instantiate the returned BigNumber after retrieval.
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
balance = new BigNumber(balance);
return balance;
}
@@ -44,6 +43,7 @@ export class TokenWrapper extends ContractWrapper {
const tokenContract = await this.getTokenContractAsync(tokenAddress);
const proxyAddress = await this.getProxyAddressAsync();
let allowanceInBaseUnits = await tokenContract.allowance.call(ownerAddress, proxyAddress);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
allowanceInBaseUnits = new BigNumber(allowanceInBaseUnits);
return allowanceInBaseUnits;
}
diff --git a/src/types.ts b/src/types.ts
index 3da24abc1..b5430a783 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -35,6 +35,9 @@ export type OrderValues = [BigNumber.BigNumber, BigNumber.BigNumber, BigNumber.B
export interface ExchangeContract {
isValidSignature: any;
+ getUnavailableValueT: {
+ call: (orderHash: string) => BigNumber.BigNumber;
+ };
isRoundingError: {
call: (takerTokenAmount: BigNumber.BigNumber, fillTakerAmount: BigNumber.BigNumber,
makerTokenAmount: BigNumber.BigNumber, txOpts: TxOpts) => Promise<boolean>;
@@ -45,6 +48,28 @@ export interface ExchangeContract {
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues, fillAmount: BigNumber.BigNumber,
shouldCheckTransfer: boolean, v: number, r: string, s: string, txOpts: TxOpts) => number;
};
+ filled: {
+ call: (orderHash: string) => BigNumber.BigNumber;
+ };
+ cancelled: {
+ call: (orderHash: string) => BigNumber.BigNumber;
+ };
+}
+
+export type EventCallbackAsync = (err: Error, event: ContractEvent) => Promise<void>;
+export type EventCallbackSync = (err: Error, event: ContractEvent) => void;
+export type EventCallback = EventCallbackSync|EventCallbackAsync;
+export interface ContractEventObj {
+ watch: (eventWatch: EventCallback) => void;
+ stopWatching: () => void;
+}
+export type CreateContractEvent = (indexFilterValues: IndexFilterValues,
+ subscriptionOpts: SubscriptionOpts) => ContractEventObj;
+export interface ExchangeContract {
+ isValidSignature: any;
+ LogFill: CreateContractEvent;
+ LogCancel: CreateContractEvent;
+ LogError: CreateContractEvent;
ZRX: {
call: () => Promise<string>;
};
@@ -108,6 +133,13 @@ export interface ContractResponse {
}
export interface ContractEvent {
+ logIndex: number;
+ transactionIndex: number;
+ transactionHash: string;
+ blockHash: string;
+ blockNumber: number;
+ address: string;
+ type: string;
event: string;
args: any;
}
@@ -145,3 +177,25 @@ export interface TxOpts {
from: string;
gas?: number;
}
+
+export interface TokenAddressBySymbol {
+ [symbol: string]: string;
+}
+
+export const ExchangeEvents = strEnum([
+ 'LogFill',
+ 'LogCancel',
+ 'LogError',
+]);
+export type ExchangeEvents = keyof typeof ExchangeEvents;
+
+export interface IndexFilterValues {
+ [index: string]: any;
+}
+
+export interface SubscriptionOpts {
+ fromBlock: string|number;
+ toBlock: string|number;
+}
+
+export type DoneCallback = (err?: Error) => void;
diff --git a/src/utils/assert.ts b/src/utils/assert.ts
index aeed1c6dc..406f2b149 100644
--- a/src/utils/assert.ts
+++ b/src/utils/assert.ts
@@ -2,6 +2,7 @@ import * as _ from 'lodash';
import * as BigNumber from 'bignumber.js';
import * as Web3 from 'web3';
import {SchemaValidator} from './schema_validator';
+import {utils} from './utils';
const HEX_REGEX = /^0x[0-9A-F]*$/i;
@@ -27,6 +28,9 @@ export const assert = {
isNumber(variableName: string, value: number): void {
this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value));
},
+ isValidOrderHash(variableName: string, value: string): void {
+ this.assert(utils.isValidOrderHash(value), this.typeAssertionMessage(variableName, 'orderHash', value));
+ },
isBoolean(variableName: string, value: boolean): void {
this.assert(_.isBoolean(value), this.typeAssertionMessage(variableName, 'boolean', value));
},
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index 336eaf7bb..114b46f6c 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -18,4 +18,11 @@ export const utils = {
isParityNode(nodeVersion: string): boolean {
return _.includes(nodeVersion, 'Parity');
},
+ isValidOrderHash(orderHashHex: string) {
+ const isValid = /^0x[0-9A-F]{64}$/i.test(orderHashHex);
+ return isValid;
+ },
+ spawnSwitchErr(name: string, value: any) {
+ return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
+ },
};
diff --git a/test/0x.js_test.ts b/test/0x.js_test.ts
index 9196d3df3..8686b42eb 100644
--- a/test/0x.js_test.ts
+++ b/test/0x.js_test.ts
@@ -8,7 +8,7 @@ import * as Sinon from 'sinon';
import {ZeroEx} from '../src/0x.js';
import {constants} from './utils/constants';
import {web3Factory} from './utils/web3_factory';
-import {Order} from '../src/types';
+import {Order, DoneCallback} from '../src/types';
chai.config.includeStack = true;
chai.use(ChaiBigNumber());
@@ -17,7 +17,7 @@ const expect = chai.expect;
describe('ZeroEx library', () => {
describe('#setProvider', () => {
- it('overrides the provider in the nested web3 instance and invalidates contractInstances', async () => {
+ it('overrides provider in nested web3s and invalidates contractInstances', async () => {
const web3 = web3Factory.create();
const zeroEx = new ZeroEx(web3);
// Instantiate the contract instances with the current provider
@@ -29,7 +29,7 @@ describe('ZeroEx library', () => {
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);
+ await zeroEx.setProviderAsync(newProvider);
// Check that contractInstances with old provider are removed after provider update
expect((zeroEx.exchange as any).exchangeContractIfExists).to.be.undefined();
diff --git a/test/exchange_wrapper_test.ts b/test/exchange_wrapper_test.ts
index 607cdf052..ab6f2df67 100644
--- a/test/exchange_wrapper_test.ts
+++ b/test/exchange_wrapper_test.ts
@@ -9,7 +9,15 @@ import promisify = require('es6-promisify');
import {web3Factory} from './utils/web3_factory';
import {ZeroEx} from '../src/0x.js';
import {BlockchainLifecycle} from './utils/blockchain_lifecycle';
-import {ExchangeContractErrs, SignedOrder, Token} from '../src/types';
+import {
+ Token,
+ SignedOrder,
+ SubscriptionOpts,
+ ExchangeEvents,
+ ContractEvent,
+ DoneCallback,
+ ExchangeContractErrs,
+} from '../src/types';
import {FillScenarios} from './utils/fill_scenarios';
import {TokenUtils} from './utils/token_utils';
@@ -19,14 +27,24 @@ chai.use(ChaiBigNumber());
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle();
+const NON_EXISTENT_ORDER_HASH = '0x79370342234e7acd6bbeac335bd3bb1d368383294b64b8160a00f4060e4d3777';
+
describe('ExchangeWrapper', () => {
let web3: Web3;
let zeroEx: ZeroEx;
let userAddresses: string[];
+ let tokenUtils: TokenUtils;
+ let tokens: Token[];
+ let fillScenarios: FillScenarios;
+ let zrxTokenAddress: string;
before(async () => {
web3 = web3Factory.create();
zeroEx = new ZeroEx(web3);
userAddresses = await promisify(web3.eth.getAccounts)();
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -105,26 +123,20 @@ describe('ExchangeWrapper', () => {
});
});
describe('#fillOrderAsync', () => {
- let tokens: Token[];
let makerTokenAddress: string;
let takerTokenAddress: string;
- let fillScenarios: FillScenarios;
let coinbase: string;
let makerAddress: string;
let takerAddress: string;
let feeRecipient: string;
- let zrxTokenAddress: string;
const fillTakerAmount = new BigNumber(5);
const shouldCheckTransfer = false;
before(async () => {
[coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
tokens = await zeroEx.tokenRegistry.getTokensAsync();
- const tokenUtils = new TokenUtils(tokens);
const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens();
makerTokenAddress = makerToken.address;
takerTokenAddress = takerToken.address;
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress);
});
afterEach('reset default account', () => {
zeroEx.setTransactionSenderAccount(userAddresses[0]);
@@ -326,4 +338,144 @@ describe('ExchangeWrapper', () => {
});
});
});
+ describe('tests that require partially filled order', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let takerAddress: string;
+ let fillableAmount: BigNumber.BigNumber;
+ let partialFillAmount: BigNumber.BigNumber;
+ let signedOrder: SignedOrder;
+ before(() => {
+ takerAddress = userAddresses[1];
+ const [makerToken, takerToken] = tokens;
+ 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,
+ );
+ });
+ describe('#getUnavailableTakerAmountAsync', () => {
+ it ('should throw if passed an invalid orderHash', async () => {
+ const invalidOrderHashHex = '0x123';
+ 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 orderHash = await zeroEx.getOrderHashHexAsync(signedOrder);
+ 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';
+ 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 orderHash = await zeroEx.getOrderHashHexAsync(signedOrder);
+ const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(orderHash);
+ expect(filledValueT).to.be.bignumber.equal(partialFillAmount);
+ });
+ });
+ describe('#getCanceledTakerAmountAsync', () => {
+ it ('should throw if passed an invalid orderHash', async () => {
+ const invalidOrderHashHex = '0x123';
+ expect(zeroEx.exchange.getCanceledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
+ });
+ it ('should return zero if passed a valid but non-existent orderHash', async () => {
+ const cancelledValueT = await zeroEx.exchange.getCanceledTakerAmountAsync(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 orderHash = await zeroEx.getOrderHashHexAsync(signedOrder);
+ const cancelledValueT = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHash);
+ expect(cancelledValueT).to.be.bignumber.equal(0);
+ });
+ });
+ });
+ describe('#subscribeAsync', () => {
+ const indexFilterValues = {};
+ const shouldCheckTransfer = false;
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let takerAddress: string;
+ let makerAddress: string;
+ let fillableAmount: BigNumber.BigNumber;
+ let signedOrder: SignedOrder;
+ before(() => {
+ [coinbase, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokens;
+ 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 as any).stopWatchingExchangeLogEventsAsync();
+ });
+ // 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 `subscribeAsync` 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 subscriptionOpts: SubscriptionOpts = {
+ fromBlock: 0,
+ toBlock: 'latest',
+ };
+ await zeroEx.exchange.subscribeAsync(ExchangeEvents.LogFill, subscriptionOpts,
+ indexFilterValues, (err: Error, event: ContractEvent) => {
+ expect(err).to.be.null();
+ expect(event).to.not.be.undefined();
+ done();
+ });
+ const fillTakerAmountInBaseUnits = new BigNumber(1);
+ zeroEx.setTransactionSenderAccount(takerAddress);
+ await zeroEx.exchange.fillOrderAsync(signedOrder, fillTakerAmountInBaseUnits, shouldCheckTransfer);
+ })();
+ });
+ it('Outstanding subscriptions are cancelled when zeroEx.setProviderAsync called', (done: DoneCallback) => {
+ (async () => {
+ const subscriptionOpts: SubscriptionOpts = {
+ fromBlock: 0,
+ toBlock: 'latest',
+ };
+ await zeroEx.exchange.subscribeAsync(ExchangeEvents.LogFill, subscriptionOpts,
+ indexFilterValues, (err: Error, event: ContractEvent) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ });
+
+ const newProvider = web3Factory.getRpcProvider();
+ await zeroEx.setProviderAsync(newProvider);
+
+ await zeroEx.exchange.subscribeAsync(ExchangeEvents.LogFill, subscriptionOpts,
+ indexFilterValues, (err: Error, event: ContractEvent) => {
+ expect(err).to.be.null();
+ expect(event).to.not.be.undefined();
+ done();
+ });
+
+ const fillTakerAmountInBaseUnits = new BigNumber(1);
+ zeroEx.setTransactionSenderAccount(takerAddress);
+ await zeroEx.exchange.fillOrderAsync(signedOrder, fillTakerAmountInBaseUnits, shouldCheckTransfer);
+ })();
+ });
+ });
});
diff --git a/test/utils/fill_scenarios.ts b/test/utils/fill_scenarios.ts
index a44d6b18a..ea5eb77eb 100644
--- a/test/utils/fill_scenarios.ts
+++ b/test/utils/fill_scenarios.ts
@@ -51,6 +51,28 @@ export class FillScenarios {
makerFillableAmount, takerFillableAmount, feeRecepient, expirationUnixTimestampSec,
);
}
+ public async createPartiallyFilledSignedOrderAsync(makerTokenAddress: string, takerTokenAddress: string,
+ takerAddress: string, fillableAmount: BigNumber.BigNumber,
+ partialFillAmount: BigNumber.BigNumber) {
+ const prevSenderAccount = await this.zeroEx.getTransactionSenderAccountIfExistsAsync();
+ const [makerAddress] = this.userAddresses;
+ await this.zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, fillableAmount);
+ await this.zeroEx.token.transferAsync(takerTokenAddress, makerAddress, takerAddress, fillableAmount);
+ await this.zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, fillableAmount);
+
+ const signedOrder = await this.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
+ fillableAmount, fillableAmount,
+ );
+
+ this.zeroEx.setTransactionSenderAccount(takerAddress);
+ const shouldCheckTransfer = false;
+ await this.zeroEx.exchange.fillOrderAsync(signedOrder, partialFillAmount, shouldCheckTransfer);
+
+ // Re-set sender account so as to avoid introducing side-effects
+ this.zeroEx.setTransactionSenderAccount(prevSenderAccount as string);
+ return signedOrder;
+ }
private async createAsymmetricFillableSignedOrderWithFeesAsync(
makerTokenAddress: string, takerTokenAddress: string,
makerFee: BigNumber.BigNumber, takerFee: BigNumber.BigNumber,