diff options
-rw-r--r-- | CHANGELOG.md | 18 | ||||
-rw-r--r-- | package-lock.json | 2 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/contract_wrappers/contract_wrapper.ts | 29 | ||||
-rw-r--r-- | src/contract_wrappers/exchange_wrapper.ts | 27 | ||||
-rw-r--r-- | src/contract_wrappers/token_wrapper.ts | 15 | ||||
-rw-r--r-- | src/index.ts | 2 | ||||
-rw-r--r-- | src/types.ts | 16 | ||||
-rw-r--r-- | src/utils/abi_decoder.ts | 5 | ||||
-rw-r--r-- | src/utils/order_validation_utils.ts | 6 | ||||
-rw-r--r-- | src/web3_wrapper.ts | 15 | ||||
-rw-r--r-- | test/0x.js_test.ts | 6 | ||||
-rw-r--r-- | test/ether_token_wrapper_test.ts | 1 | ||||
-rw-r--r-- | test/exchange_transfer_simulator_test.ts | 4 | ||||
-rw-r--r-- | test/exchange_wrapper_test.ts | 22 | ||||
-rw-r--r-- | test/order_validation_test.ts | 38 | ||||
-rw-r--r-- | test/token_wrapper_test.ts | 29 | ||||
-rw-r--r-- | test/utils/fill_scenarios.ts | 2 |
18 files changed, 156 insertions, 83 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aae081bf..255f413ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,22 @@ # CHANGELOG -v0.21.0 - _TBD, 2017_ +v0.21.4 - _October 13, 2017_ +------------------------ + * Made 0x.js more type-safe by making `getLogsAsync` and `subscribe/subscribeAsync` generics parametrized with arg type (#194) + +v0.21.3 - _October 12, 2017_ +------------------------ + * Fixed a bug causing order fills to throw `INSUFFICIENT_TAKER_ALLOWANCE` (#193) + +v0.21.2 - _October 11, 2017_ +------------------------ + * Exported `ContractEventArg` as a public type (#190) + +v0.21.1 - _October 11, 2017_ +------------------------ + * Fixed a bug in subscriptions (#189) + +v0.21.0 - _October 10, 2017_ ------------------------ * Complete rewrite of subscription logic (#182) * Subscriptions no longer return historical logs. If you want them - use `getLogsAsync` diff --git a/package-lock.json b/package-lock.json index 1a46f7975..e11072e4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "0x.js", - "version": "0.21.0", + "version": "0.21.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 3bdaa59aa..c08a61463 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "0x.js", - "version": "0.21.0", + "version": "0.21.4", "description": "A javascript library for interacting with the 0x protocol", "keywords": [ "0x.js", diff --git a/src/contract_wrappers/contract_wrapper.ts b/src/contract_wrappers/contract_wrapper.ts index f6ccfdee4..19dccc6f2 100644 --- a/src/contract_wrappers/contract_wrapper.ts +++ b/src/contract_wrappers/contract_wrapper.ts @@ -14,6 +14,7 @@ import { IndexedFilterValues, EventCallback, BlockParamLiteral, + ContractEventArgs, } from '../types'; import {constants} from '../utils/constants'; import {intervalUtils} from '../utils/interval_utils'; @@ -25,7 +26,7 @@ export class ContractWrapper { private _blockAndLogStreamer: BlockAndLogStreamer|undefined; private _blockAndLogStreamInterval: NodeJS.Timer; private _filters: {[filterToken: string]: Web3.FilterObject}; - private _filterCallbacks: {[filterToken: string]: EventCallback}; + private _filterCallbacks: {[filterToken: string]: EventCallback<ContractEventArgs>}; private _onLogAddedSubscriptionToken: string|undefined; private _onLogRemovedSubscriptionToken: string|undefined; constructor(web3Wrapper: Web3Wrapper, abiDecoder?: AbiDecoder) { @@ -37,9 +38,9 @@ export class ContractWrapper { this._onLogAddedSubscriptionToken = undefined; this._onLogRemovedSubscriptionToken = undefined; } - protected _subscribe(address: string, eventName: ContractEvents, - indexFilterValues: IndexedFilterValues, abi: Web3.ContractAbi, - callback: EventCallback): string { + 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._blockAndLogStreamer)) { this._startBlockAndLogStream(); @@ -59,32 +60,32 @@ export class ContractWrapper { this._stopBlockAndLogStream(); } } - protected async _getLogsAsync(address: string, eventName: ContractEvents, subscriptionOpts: SubscriptionOpts, - indexFilterValues: IndexedFilterValues, - abi: Web3.ContractAbi): Promise<LogWithDecodedArgs[]> { + protected async _getLogsAsync<ArgsType extends ContractEventArgs>( + address: string, eventName: ContractEvents, subscriptionOpts: SubscriptionOpts, + indexFilterValues: IndexedFilterValues, abi: Web3.ContractAbi): Promise<Array<LogWithDecodedArgs<ArgsType>>> { const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, subscriptionOpts); const logs = await this._web3Wrapper.getLogsAsync(filter); const logsWithDecodedArguments = _.map(logs, this._tryToDecodeLogOrNoop.bind(this)); return logsWithDecodedArguments; } - protected _tryToDecodeLogOrNoop(log: Web3.LogEntry): LogWithDecodedArgs|RawLog { + 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<A extends Web3.ContractInstance>(artifact: Artifact, - addressIfExists?: string, - ): Promise<A> { + protected async _instantiateContractIfExistsAsync<ContractType extends Web3.ContractInstance>( + artifact: Artifact, addressIfExists?: string): Promise<ContractType> { const contractInstance = - await this._web3Wrapper.getContractInstanceFromArtifactAsync<A>(artifact, addressIfExists); + await this._web3Wrapper.getContractInstanceFromArtifactAsync<ContractType>(artifact, addressIfExists); return contractInstance; } - private _onLogStateChanged(removed: boolean, log: Web3.LogEntry): void { + private _onLogStateChanged<ArgsType extends ContractEventArgs>(removed: 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; + const decodedLog = this._tryToDecodeLogOrNoop(log) as LogWithDecodedArgs<ArgsType>; const logEvent = { ...decodedLog, removed, diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index e7b4a437f..bc0ac0634 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -1,4 +1,5 @@ import * as _ from 'lodash'; +import * as Web3 from 'web3'; import * as BigNumber from 'bignumber.js'; import {schemas} from '0x-json-schemas'; import {Web3Wrapper} from '../web3_wrapper'; @@ -27,6 +28,8 @@ import { OrderTransactionOpts, RawLog, EventCallback, + ExchangeContractEventArgs, + DecodedLogArgs, } from '../types'; import {assert} from '../utils/assert'; import {utils} from '../utils/utils'; @@ -653,13 +656,14 @@ export class ExchangeWrapper extends ContractWrapper { * @param callback Callback that gets called when a log is added/removed * @return Subscription token used later to unsubscribe */ - public async subscribeAsync(eventName: ExchangeEvents, indexFilterValues: IndexedFilterValues, - callback: EventCallback): Promise<string> { + public async subscribeAsync<ArgsType extends ExchangeContractEventArgs>( + eventName: ExchangeEvents, indexFilterValues: IndexedFilterValues, + callback: EventCallback<ArgsType>): Promise<string> { assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.isFunction('callback', callback); const exchangeContractAddress = await this.getContractAddressAsync(); - const subscriptionToken = this._subscribe( + const subscriptionToken = this._subscribe<ArgsType>( exchangeContractAddress, eventName, indexFilterValues, artifacts.ExchangeArtifact.abi, callback, ); this._activeSubscriptions.push(subscriptionToken); @@ -681,13 +685,14 @@ export class ExchangeWrapper extends ContractWrapper { * the value is the value you are interested in. E.g `{_from: aUserAddressHex}` * @return Array of logs that match the parameters */ - public async getLogsAsync(eventName: ExchangeEvents, subscriptionOpts: SubscriptionOpts, - indexFilterValues: IndexedFilterValues): Promise<LogWithDecodedArgs[]> { + public async getLogsAsync<ArgsType extends ExchangeContractEventArgs>( + eventName: ExchangeEvents, subscriptionOpts: SubscriptionOpts, indexFilterValues: IndexedFilterValues, + ): Promise<Array<LogWithDecodedArgs<ArgsType>>> { assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents); assert.doesConformToSchema('subscriptionOpts', subscriptionOpts, schemas.subscriptionOptsSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); const exchangeContractAddress = await this.getContractAddressAsync(); - const logs = await this._getLogsAsync( + const logs = await this._getLogsAsync<ArgsType>( exchangeContractAddress, eventName, subscriptionOpts, indexFilterValues, artifacts.ExchangeArtifact.abi, ); return logs; @@ -798,12 +803,14 @@ export class ExchangeWrapper extends ContractWrapper { } /** * Checks if logs contain LogError, which is emmited by Exchange contract on transaction failure. - * @param logsWithDecodedArgs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync` + * @param logs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync` */ - public throwLogErrorsAsErrors(logsWithDecodedArgs: LogWithDecodedArgs[]): void { - const errLog = _.find(logsWithDecodedArgs, {event: ExchangeEvents.LogError}); + 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: LogErrorContractEventArgs = errLog.args as any; + const logArgs = errLog.args; const errCode = logArgs.errorId.toNumber(); const errMessage = this._exchangeContractErrCodesToMsg[errCode]; throw new Error(errMessage); diff --git a/src/contract_wrappers/token_wrapper.ts b/src/contract_wrappers/token_wrapper.ts index abd090f7e..8dc4e61c5 100644 --- a/src/contract_wrappers/token_wrapper.ts +++ b/src/contract_wrappers/token_wrapper.ts @@ -16,6 +16,7 @@ import { MethodOpts, LogWithDecodedArgs, EventCallback, + TokenContractEventArgs, } from '../types'; const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 47155; @@ -251,13 +252,14 @@ export class TokenWrapper extends ContractWrapper { * @param callback Callback that gets called when a log is added/removed * @return Subscription token used later to unsubscribe */ - public subscribe(tokenAddress: string, eventName: TokenEvents, indexFilterValues: IndexedFilterValues, - callback: EventCallback): string { + 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( + const subscriptionToken = this._subscribe<ArgsType>( tokenAddress, eventName, indexFilterValues, artifacts.TokenArtifact.abi, callback, ); this._activeSubscriptions.push(subscriptionToken); @@ -280,13 +282,14 @@ export class TokenWrapper extends ContractWrapper { * the value is the value you are interested in. E.g `{_from: aUserAddressHex}` * @return Array of logs that match the parameters */ - public async getLogsAsync(tokenAddress: string, eventName: TokenEvents, subscriptionOpts: SubscriptionOpts, - indexFilterValues: IndexedFilterValues): Promise<LogWithDecodedArgs[]> { + public async getLogsAsync<ArgsType extends TokenContractEventArgs>( + tokenAddress: string, eventName: TokenEvents, subscriptionOpts: SubscriptionOpts, + indexFilterValues: IndexedFilterValues): Promise<Array<LogWithDecodedArgs<ArgsType>>> { assert.isETHAddressHex('tokenAddress', tokenAddress); assert.doesBelongToStringEnum('eventName', eventName, TokenEvents); assert.doesConformToSchema('subscriptionOpts', subscriptionOpts, schemas.subscriptionOptsSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); - const logs = await this._getLogsAsync( + const logs = await this._getLogsAsync<ArgsType>( tokenAddress, eventName, subscriptionOpts, indexFilterValues, artifacts.TokenArtifact.abi, ); return logs; diff --git a/src/index.ts b/src/index.ts index b9f50b0d6..249c20519 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,11 +26,11 @@ export { ApprovalContractEventArgs, TokenContractEventArgs, ContractEventArgs, + ContractEventArg, Web3Provider, ZeroExConfig, TransactionReceiptWithDecodedLogs, LogWithDecodedArgs, - DecodedLogArgs, MethodOpts, OrderTransactionOpts, FilterObject, diff --git a/src/types.ts b/src/types.ts index 4d0b477dd..cb5fe4405 100644 --- a/src/types.ts +++ b/src/types.ts @@ -36,12 +36,12 @@ export type OrderAddresses = [string, string, string, string, string]; export type OrderValues = [BigNumber.BigNumber, BigNumber.BigNumber, BigNumber.BigNumber, BigNumber.BigNumber, BigNumber.BigNumber, BigNumber.BigNumber]; -export interface LogEvent extends LogWithDecodedArgs { +export interface LogEvent<ArgsType> extends LogWithDecodedArgs<ArgsType> { removed: boolean; } -export type EventCallbackAsync = (log: LogEvent) => Promise<void>; -export type EventCallbackSync = (log: LogEvent) => void; -export type EventCallback = EventCallbackSync|EventCallbackAsync; +export type EventCallbackAsync<ArgsType> = (log: LogEvent<ArgsType>) => Promise<void>; +export type EventCallbackSync<ArgsType> = (log: LogEvent<ArgsType>) => void; +export type EventCallback<ArgsType> = EventCallbackSync<ArgsType>|EventCallbackAsync<ArgsType>; export interface ExchangeContract extends Web3.ContractInstance { isValidSignature: { callAsync: (signerAddressHex: string, dataHex: string, v: number, r: string, s: string, @@ -414,15 +414,15 @@ export interface DecodedLogArgs { [argName: string]: ContractEventArg; } -export interface DecodedArgs { - args: DecodedLogArgs; +export interface DecodedArgs<ArgsType> { + args: ArgsType; event: string; } -export interface LogWithDecodedArgs extends Web3.LogEntry, DecodedArgs {} +export interface LogWithDecodedArgs<ArgsType> extends Web3.LogEntry, DecodedArgs<ArgsType> {} export interface TransactionReceiptWithDecodedLogs extends Web3.TransactionReceipt { - logs: Array<LogWithDecodedArgs|Web3.LogEntry>; + logs: Array<LogWithDecodedArgs<DecodedLogArgs>|Web3.LogEntry>; } export interface Artifact { diff --git a/src/utils/abi_decoder.ts b/src/utils/abi_decoder.ts index 52b114c12..a6c45bee7 100644 --- a/src/utils/abi_decoder.ts +++ b/src/utils/abi_decoder.ts @@ -1,7 +1,7 @@ import * as Web3 from 'web3'; import * as _ from 'lodash'; import * as BigNumber from 'bignumber.js'; -import {AbiType, DecodedLogArgs, LogWithDecodedArgs, RawLog, SolidityTypes} from '../types'; +import {AbiType, DecodedLogArgs, LogWithDecodedArgs, RawLog, SolidityTypes, ContractEventArgs} from '../types'; import * as SolidityCoder from 'web3/lib/solidity/coder'; export class AbiDecoder { @@ -11,7 +11,8 @@ export class AbiDecoder { _.map(abiArrays, this.addABI.bind(this)); } // This method can only decode logs from the 0x smart contracts - public tryToDecodeLogOrNoop(log: Web3.LogEntry): LogWithDecodedArgs|RawLog { + public tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>( + log: Web3.LogEntry): LogWithDecodedArgs<ArgsType>|RawLog { const methodId = log.topics[0]; const event = this.methodIds[methodId]; if (_.isUndefined(event)) { diff --git a/src/utils/order_validation_utils.ts b/src/utils/order_validation_utils.ts index 5d14602db..1d9aac884 100644 --- a/src/utils/order_validation_utils.ts +++ b/src/utils/order_validation_utils.ts @@ -102,11 +102,11 @@ export class OrderValidationUtils { fillTakerTokenAmount: BigNumber.BigNumber, senderAddress: string, zrxTokenAddress: string): Promise<void> { const fillMakerTokenAmount = this.getFillMakerTokenAmount(signedOrder, fillTakerTokenAmount); await exchangeTradeEmulator.transferFromAsync( - signedOrder.makerTokenAddress, signedOrder.maker, signedOrder.taker, fillMakerTokenAmount, + signedOrder.makerTokenAddress, signedOrder.maker, senderAddress, fillMakerTokenAmount, TradeSide.Maker, TransferType.Trade, ); await exchangeTradeEmulator.transferFromAsync( - signedOrder.takerTokenAddress, signedOrder.taker, signedOrder.maker, fillTakerTokenAmount, + signedOrder.takerTokenAddress, senderAddress, signedOrder.maker, fillTakerTokenAmount, TradeSide.Taker, TransferType.Trade, ); await exchangeTradeEmulator.transferFromAsync( @@ -114,7 +114,7 @@ export class OrderValidationUtils { TransferType.Fee, ); await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, signedOrder.taker, signedOrder.feeRecipient, signedOrder.takerFee, TradeSide.Taker, + zrxTokenAddress, senderAddress, signedOrder.feeRecipient, signedOrder.takerFee, TradeSide.Taker, TransferType.Fee, ); } diff --git a/src/web3_wrapper.ts b/src/web3_wrapper.ts index 9de75c809..ff5d25d9c 100644 --- a/src/web3_wrapper.ts +++ b/src/web3_wrapper.ts @@ -107,11 +107,24 @@ export class Web3Wrapper { return addresses; } public async getLogsAsync(filter: Web3.FilterObject): Promise<Web3.LogEntry[]> { + let fromBlock = filter.fromBlock; + if (_.isNumber(fromBlock)) { + fromBlock = this.web3.toHex(fromBlock); + } + let toBlock = filter.toBlock; + if (_.isNumber(toBlock)) { + toBlock = this.web3.toHex(toBlock); + } + const serializedFilter = { + ...filter, + fromBlock, + toBlock, + }; const payload = { jsonrpc: '2.0', id: this.jsonRpcRequestId++, method: 'eth_getLogs', - params: [filter], + params: [serializedFilter], }; const logs = await this.sendRawPayloadAsync(payload); return logs; diff --git a/test/0x.js_test.ts b/test/0x.js_test.ts index cc6b91e99..dd5dd19b1 100644 --- a/test/0x.js_test.ts +++ b/test/0x.js_test.ts @@ -4,7 +4,7 @@ import {chaiSetup} from './utils/chai_setup'; import 'mocha'; import * as BigNumber from 'bignumber.js'; import * as Sinon from 'sinon'; -import {ZeroEx, Order, ZeroExError, LogWithDecodedArgs} from '../src'; +import {ZeroEx, Order, ZeroExError, LogWithDecodedArgs, ApprovalContractEventArgs, TokenEvents} from '../src'; import {constants} from './utils/constants'; import {TokenUtils} from './utils/token_utils'; import {web3Factory} from './utils/web3_factory'; @@ -223,8 +223,8 @@ describe('ZeroEx library', () => { const proxyAddress = await zeroEx.proxy.getContractAddressAsync(); const txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(zrxTokenAddress, coinbase); const txReceiptWithDecodedLogs = await zeroEx.awaitTransactionMinedAsync(txHash); - const log = txReceiptWithDecodedLogs.logs[0] as LogWithDecodedArgs; - expect(log.event).to.be.equal('Approval'); + 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); diff --git a/test/ether_token_wrapper_test.ts b/test/ether_token_wrapper_test.ts index f50aac48b..389ec1619 100644 --- a/test/ether_token_wrapper_test.ts +++ b/test/ether_token_wrapper_test.ts @@ -3,7 +3,6 @@ import * as chai from 'chai'; import {chaiSetup} from './utils/chai_setup'; import * as Web3 from 'web3'; import * as BigNumber from 'bignumber.js'; -import promisify = require('es6-promisify'); import {web3Factory} from './utils/web3_factory'; import {ZeroEx, ZeroExError} from '../src'; import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; diff --git a/test/exchange_transfer_simulator_test.ts b/test/exchange_transfer_simulator_test.ts index 6ad2c007c..ee24f4fe4 100644 --- a/test/exchange_transfer_simulator_test.ts +++ b/test/exchange_transfer_simulator_test.ts @@ -1,12 +1,10 @@ import * as chai from 'chai'; import * as BigNumber from 'bignumber.js'; -import * as Web3 from 'web3'; import {chaiSetup} from './utils/chai_setup'; import {web3Factory} from './utils/web3_factory'; -import {ZeroEx, SignedOrder, ExchangeContractErrs, Token} from '../src'; +import {ZeroEx, ExchangeContractErrs, Token} from '../src'; import {TradeSide, TransferType} from '../src/types'; import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {FillScenarios} from './utils/fill_scenarios'; import {ExchangeTransferSimulator} from '../src/utils/exchange_transfer_simulator'; chaiSetup.configure(); diff --git a/test/exchange_wrapper_test.ts b/test/exchange_wrapper_test.ts index 8c5344b37..f3f16643e 100644 --- a/test/exchange_wrapper_test.ts +++ b/test/exchange_wrapper_test.ts @@ -3,8 +3,6 @@ import * as chai from 'chai'; import * as Web3 from 'web3'; import * as BigNumber from 'bignumber.js'; import {chaiSetup} from './utils/chai_setup'; -import ChaiBigNumber = require('chai-bignumber'); -import promisify = require('es6-promisify'); import {web3Factory} from './utils/web3_factory'; import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; import { @@ -16,15 +14,14 @@ import { ContractEvent, ExchangeContractErrs, OrderCancellationRequest, - LogFillContractEventArgs, OrderFillRequest, + LogFillContractEventArgs, + LogCancelContractEventArgs, LogEvent, } from '../src'; import {DoneCallback, BlockParamLiteral} from '../src/types'; import {FillScenarios} from './utils/fill_scenarios'; import {TokenUtils} from './utils/token_utils'; -import {assert} from '../src/utils/assert'; -import {TokenTransferProxyWrapper} from '../src/contract_wrappers/token_transfer_proxy_wrapper'; chaiSetup.configure(); const expect = chai.expect; @@ -650,7 +647,7 @@ describe('ExchangeWrapper', () => { // Source: https://github.com/mochajs/mocha/issues/2407 it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => { (async () => { - const callback = (logEvent: LogEvent) => { + const callback = (logEvent: LogEvent<LogFillContractEventArgs>) => { expect(logEvent.event).to.be.equal(ExchangeEvents.LogFill); done(); }; @@ -664,7 +661,7 @@ describe('ExchangeWrapper', () => { }); it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => { (async () => { - const callback = (logEvent: LogEvent) => { + const callback = (logEvent: LogEvent<LogCancelContractEventArgs>) => { expect(logEvent.event).to.be.equal(ExchangeEvents.LogCancel); done(); }; @@ -676,7 +673,7 @@ describe('ExchangeWrapper', () => { }); it('Outstanding subscriptions are cancelled when zeroEx.setProviderAsync called', (done: DoneCallback) => { (async () => { - const callbackNeverToBeCalled = (logEvent: LogEvent) => { + const callbackNeverToBeCalled = (logEvent: LogEvent<LogFillContractEventArgs>) => { done(new Error('Expected this subscription to have been cancelled')); }; await zeroEx.exchange.subscribeAsync( @@ -686,7 +683,7 @@ describe('ExchangeWrapper', () => { const newProvider = web3Factory.getRpcProvider(); await zeroEx.setProviderAsync(newProvider); - const callback = (logEvent: LogEvent) => { + const callback = (logEvent: LogEvent<LogFillContractEventArgs>) => { expect(logEvent.event).to.be.equal(ExchangeEvents.LogFill); done(); }; @@ -700,7 +697,7 @@ describe('ExchangeWrapper', () => { }); it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { (async () => { - const callbackNeverToBeCalled = (logEvent: LogEvent) => { + const callbackNeverToBeCalled = (logEvent: LogEvent<LogFillContractEventArgs>) => { done(new Error('Expected this subscription to have been cancelled')); }; const subscriptionToken = await zeroEx.exchange.subscribeAsync( @@ -810,11 +807,12 @@ describe('ExchangeWrapper', () => { const indexFilterValues = { maker: differentMakerAddress, }; - const logs = await zeroEx.exchange.getLogsAsync( + const logs = await zeroEx.exchange.getLogsAsync<LogFillContractEventArgs>( eventName, subscriptionOpts, indexFilterValues, ); expect(logs).to.have.length(1); - expect(logs[0].args.maker).to.be.equal(differentMakerAddress); + const args = logs[0].args; + expect(args.maker).to.be.equal(differentMakerAddress); }); }); }); diff --git a/test/order_validation_test.ts b/test/order_validation_test.ts index 6f9388a69..784fa9ec4 100644 --- a/test/order_validation_test.ts +++ b/test/order_validation_test.ts @@ -1,7 +1,6 @@ import * as chai from 'chai'; import * as Web3 from 'web3'; import * as BigNumber from 'bignumber.js'; -import promisify = require('es6-promisify'); import * as Sinon from 'sinon'; import {chaiSetup} from './utils/chai_setup'; import {web3Factory} from './utils/web3_factory'; @@ -35,7 +34,6 @@ describe('OrderValidation', () => { let orderValidationUtils: OrderValidationUtils; const fillableAmount = new BigNumber(5); const fillTakerAmount = new BigNumber(5); - const shouldThrowOnInsufficientBalanceOrAllowance = false; before(async () => { web3 = web3Factory.create(); zeroEx = new ZeroEx(web3.currentProvider); @@ -257,5 +255,41 @@ describe('OrderValidation', () => { ), ).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(); + }); }); }); diff --git a/test/token_wrapper_test.ts b/test/token_wrapper_test.ts index 50f2db2ac..f1f6543b7 100644 --- a/test/token_wrapper_test.ts +++ b/test/token_wrapper_test.ts @@ -14,6 +14,7 @@ import { ContractEvent, TransferContractEventArgs, ApprovalContractEventArgs, + TokenContractEventArgs, LogWithDecodedArgs, LogEvent, } from '../src'; @@ -357,9 +358,9 @@ describe('TokenWrapper', () => { // Source: https://github.com/mochajs/mocha/issues/2407 it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => { (async () => { - const callback = (logEvent: LogEvent) => { + const callback = (logEvent: LogEvent<TransferContractEventArgs>) => { expect(logEvent).to.not.be.undefined(); - const args = logEvent.args as any as TransferContractEventArgs; + const args = logEvent.args; expect(args._from).to.be.equal(coinbase); expect(args._to).to.be.equal(addressWithoutFunds); expect(args._value).to.be.bignumber.equal(transferAmount); @@ -372,9 +373,9 @@ describe('TokenWrapper', () => { }); it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => { (async () => { - const callback = (logEvent: LogEvent) => { + const callback = (logEvent: LogEvent<ApprovalContractEventArgs>) => { expect(logEvent).to.not.be.undefined(); - const args = logEvent.args as any as ApprovalContractEventArgs; + const args = logEvent.args; expect(args._owner).to.be.equal(coinbase); expect(args._spender).to.be.equal(addressWithoutFunds); expect(args._value).to.be.bignumber.equal(allowanceAmount); @@ -387,13 +388,13 @@ describe('TokenWrapper', () => { }); it('Outstanding subscriptions are cancelled when zeroEx.setProviderAsync called', (done: DoneCallback) => { (async () => { - const callbackNeverToBeCalled = (logEvent: LogEvent) => { + const callbackNeverToBeCalled = (logEvent: LogEvent<TransferContractEventArgs>) => { done(new Error('Expected this subscription to have been cancelled')); }; zeroEx.token.subscribe( tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled, ); - const callbackToBeCalled = (logEvent: LogEvent) => { + const callbackToBeCalled = (logEvent: LogEvent<TransferContractEventArgs>) => { done(); }; const newProvider = web3Factory.getRpcProvider(); @@ -406,7 +407,7 @@ describe('TokenWrapper', () => { }); it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { (async () => { - const callbackNeverToBeCalled = (logEvent: LogEvent) => { + const callbackNeverToBeCalled = (logEvent: LogEvent<TokenContractEventArgs>) => { done(new Error('Expected this subscription to have been cancelled')); }; const subscriptionToken = zeroEx.token.subscribe( @@ -435,14 +436,15 @@ describe('TokenWrapper', () => { await zeroEx.awaitTransactionMinedAsync(txHash); const eventName = TokenEvents.Approval; const indexFilterValues = {}; - const logs = await zeroEx.token.getLogsAsync( + const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>( tokenAddress, eventName, subscriptionOpts, indexFilterValues, ); expect(logs).to.have.length(1); + const args = logs[0].args; expect(logs[0].event).to.be.equal(eventName); - expect(logs[0].args._owner).to.be.equal(coinbase); - expect(logs[0].args._spender).to.be.equal(tokenTransferProxyAddress); - expect(logs[0].args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + 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); @@ -463,11 +465,12 @@ describe('TokenWrapper', () => { const indexFilterValues = { _owner: coinbase, }; - const logs = await zeroEx.token.getLogsAsync( + const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>( tokenAddress, eventName, subscriptionOpts, indexFilterValues, ); expect(logs).to.have.length(1); - expect(logs[0].args._owner).to.be.equal(coinbase); + const args = logs[0].args; + expect(args._owner).to.be.equal(coinbase); }); }); }); diff --git a/test/utils/fill_scenarios.ts b/test/utils/fill_scenarios.ts index e305759f6..c399c7bf4 100644 --- a/test/utils/fill_scenarios.ts +++ b/test/utils/fill_scenarios.ts @@ -91,7 +91,7 @@ export class FillScenarios { } private async increaseBalanceAndAllowanceAsync( tokenAddress: string, address: string, amount: BigNumber.BigNumber): Promise<void> { - if (amount.isZero()) { + if (amount.isZero() || address === ZeroEx.NULL_ADDRESS) { return; // noop } await Promise.all([ |