aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--package.json6
-rw-r--r--src/0x.ts25
-rw-r--r--src/contract_wrappers/contract_wrapper.ts66
-rw-r--r--src/contract_wrappers/exchange_wrapper.ts23
-rw-r--r--src/contract_wrappers/token_wrapper.ts24
-rw-r--r--src/index.ts1
-rw-r--r--src/types.ts14
-rw-r--r--src/utils/abi_decoder.ts64
-rw-r--r--src/web3_wrapper.ts22
-rw-r--r--test/exchange_wrapper_test.ts74
-rw-r--r--test/token_wrapper_test.ts54
-rw-r--r--test/utils/token_utils.ts4
-rw-r--r--yarn.lock151
14 files changed, 367 insertions, 165 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c1072f24..b3ce29f82 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# CHANGELOG
+v0.20.0 - _October 3, 2017_
+ * Add `zeroEx.token.getLogsAsync` (#178)
+ * Add `zeroEx.exchange.getLogsAsync` (#178)
+
v0.19.0 - _September 29, 2017_
* Made order validation optional (#172)
* Added Ropsten testnet support (#173)
diff --git a/package.json b/package.json
index 6a179f97e..a1382fb82 100644
--- a/package.json
+++ b/package.json
@@ -82,15 +82,15 @@
"tslint-config-0xproject": "^0.0.2",
"typedoc": "^0.8.0",
"types-bn": "^0.0.1",
- "types-ethereumjs-util": "^0.0.5",
+ "types-ethereumjs-util": "0xProject/types-ethereumjs-util",
"typescript": "^2.4.1",
"web3-provider-engine": "^13.0.1",
- "web3-typescript-typings": "^0.6.0",
+ "web3-typescript-typings": "^0.6.2",
"webpack": "^3.1.0"
},
"dependencies": {
"@types/bignumber.js": "^4.0.2",
- "0x-json-schemas": "^0.6.0",
+ "0x-json-schemas": "^0.6.1",
"bignumber.js": "^4.0.2",
"compare-versions": "^3.0.1",
"es6-promisify": "^5.0.0",
diff --git a/src/0x.ts b/src/0x.ts
index e6fdf68e1..3180c52f6 100644
--- a/src/0x.ts
+++ b/src/0x.ts
@@ -31,6 +31,8 @@ import {
DecodedLogArgs,
TransactionReceiptWithDecodedLogs,
LogWithDecodedArgs,
+ FilterObject,
+ RawLog,
} from './types';
import {zeroExConfigSchema} from './schemas/zero_ex_config_schema';
@@ -200,10 +202,16 @@ export class ZeroEx {
this._web3Wrapper = new Web3Wrapper(provider, defaults);
this.token = new TokenWrapper(
this._web3Wrapper,
+ this._abiDecoder,
this._getTokenTransferProxyAddressAsync.bind(this),
);
const exchageContractAddressIfExists = _.isUndefined(config) ? undefined : config.exchangeContractAddress;
- this.exchange = new ExchangeWrapper(this._web3Wrapper, this.token, exchageContractAddressIfExists);
+ this.exchange = new ExchangeWrapper(
+ this._web3Wrapper,
+ this._abiDecoder,
+ this.token,
+ exchageContractAddressIfExists,
+ );
this.proxy = new TokenTransferProxyWrapper(
this._web3Wrapper,
this._getTokenTransferProxyAddressAsync.bind(this),
@@ -300,17 +308,10 @@ export class ZeroEx {
const transactionReceipt = await this._web3Wrapper.getTransactionReceiptAsync(txHash);
if (!_.isNull(transactionReceipt)) {
intervalUtils.clearAsyncExcludingInterval(intervalId);
- const logsWithDecodedArgs = _.map(transactionReceipt.logs, (log: Web3.LogEntry) => {
- const decodedLog = this._abiDecoder.decodeLog(log);
- if (_.isUndefined(decodedLog)) {
- return log;
- }
- const logWithDecodedArgs: LogWithDecodedArgs = {
- ...log,
- ...decodedLog,
- };
- return logWithDecodedArgs;
- });
+ const logsWithDecodedArgs = _.map(
+ transactionReceipt.logs,
+ this._abiDecoder.tryToDecodeLogOrNoop.bind(this._abiDecoder),
+ );
const transactionReceiptWithDecodedLogArgs: TransactionReceiptWithDecodedLogs = {
...transactionReceipt,
logs: logsWithDecodedArgs,
diff --git a/src/contract_wrappers/contract_wrapper.ts b/src/contract_wrappers/contract_wrapper.ts
index 2a55b53d9..743dfc9b2 100644
--- a/src/contract_wrappers/contract_wrapper.ts
+++ b/src/contract_wrappers/contract_wrapper.ts
@@ -1,13 +1,52 @@
import * as _ from 'lodash';
import * as Web3 from 'web3';
+import * as ethUtil from 'ethereumjs-util';
import {Web3Wrapper} from '../web3_wrapper';
-import {ZeroExError, Artifact} from '../types';
+import {AbiDecoder} from '../utils/abi_decoder';
+import {
+ InternalZeroExError,
+ Artifact,
+ LogWithDecodedArgs,
+ RawLog,
+ ContractEvents,
+ SubscriptionOpts,
+ IndexedFilterValues,
+} from '../types';
import {utils} from '../utils/utils';
+const TOPIC_LENGTH = 32;
+
export class ContractWrapper {
protected _web3Wrapper: Web3Wrapper;
- constructor(web3Wrapper: Web3Wrapper) {
+ private _abiDecoder?: AbiDecoder;
+ constructor(web3Wrapper: Web3Wrapper, abiDecoder?: AbiDecoder) {
this._web3Wrapper = web3Wrapper;
+ this._abiDecoder = abiDecoder;
+ }
+ protected async _getLogsAsync(address: string, eventName: ContractEvents, subscriptionOpts: SubscriptionOpts,
+ indexFilterValues: IndexedFilterValues,
+ abi: Web3.ContractAbi): Promise<LogWithDecodedArgs[]> {
+ const eventAbi = _.find(abi, {name: eventName}) as Web3.EventAbi;
+ const eventSignature = this._getEventSignatureFromAbiByName(eventAbi, eventName);
+ const topicForEventSignature = this._web3Wrapper.keccak256(eventSignature);
+ const topicsForIndexedArgs = this._getTopicsForIndexedArgs(eventAbi, indexFilterValues);
+ const topics = [topicForEventSignature, ...topicsForIndexedArgs];
+ const filter = {
+ fromBlock: subscriptionOpts.fromBlock,
+ toBlock: subscriptionOpts.toBlock,
+ address,
+ topics,
+ };
+ const logs = await this._web3Wrapper.getLogsAsync(filter);
+ const logsWithDecodedArguments = _.map(logs, this._tryToDecodeLogOrNoop.bind(this));
+ return logsWithDecodedArguments;
+ }
+ protected _tryToDecodeLogOrNoop(log: Web3.LogEntry): LogWithDecodedArgs|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,
@@ -16,4 +55,27 @@ export class ContractWrapper {
await this._web3Wrapper.getContractInstanceFromArtifactAsync<A>(artifact, addressIfExists);
return contractInstance;
}
+ protected _getEventSignatureFromAbiByName(eventAbi: Web3.EventAbi, eventName: ContractEvents): string {
+ const types = _.map(eventAbi.inputs, 'type');
+ const signature = `${eventAbi.name}(${types.join(',')})`;
+ return signature;
+ }
+ private _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])) {
+ 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;
+ }
}
diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts
index d02a6e642..b3a35d5bf 100644
--- a/src/contract_wrappers/exchange_wrapper.ts
+++ b/src/contract_wrappers/exchange_wrapper.ts
@@ -30,6 +30,7 @@ import {
MethodOpts,
ValidateOrderFillableOpts,
OrderTransactionOpts,
+ RawLog,
} from '../types';
import {assert} from '../utils/assert';
import {utils} from '../utils/utils';
@@ -39,6 +40,7 @@ import {ContractWrapper} from './contract_wrapper';
import {constants} from '../utils/constants';
import {TokenWrapper} from './token_wrapper';
import {decorators} from '../utils/decorators';
+import {AbiDecoder} from '../utils/abi_decoder';
import {artifacts} from '../artifacts';
const SHOULD_VALIDATE_BY_DEFAULT = true;
@@ -79,8 +81,9 @@ export class ExchangeWrapper extends ContractWrapper {
];
return [orderAddresses, orderValues];
}
- constructor(web3Wrapper: Web3Wrapper, tokenWrapper: TokenWrapper, contractAddressIfExists?: string) {
- super(web3Wrapper);
+ constructor(web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder,
+ tokenWrapper: TokenWrapper, contractAddressIfExists?: string) {
+ super(web3Wrapper, abiDecoder);
this._tokenWrapper = tokenWrapper;
this._orderValidationUtils = new OrderValidationUtils(tokenWrapper, this);
this._exchangeLogEventEmitters = [];
@@ -656,6 +659,22 @@ export class ExchangeWrapper extends ContractWrapper {
return eventEmitter;
}
/**
+ * Gets historical logs without creating a subscription
+ * @param eventName The exchange contract event you would like to subscribe to.
+ * @param subscriptionOpts Subscriptions options that let you configure the subscription.
+ * @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(eventName: ExchangeEvents, subscriptionOpts: SubscriptionOpts,
+ indexFilterValues: IndexedFilterValues): Promise<LogWithDecodedArgs[]> {
+ const exchangeContractAddress = await this.getContractAddressAsync();
+ const logs = await this._getLogsAsync(
+ exchangeContractAddress, eventName, subscriptionOpts, indexFilterValues, artifacts.ExchangeArtifact.abi,
+ );
+ return logs;
+ }
+ /**
* Stops watching for all exchange events
*/
public async stopWatchingAllEventsAsync(): Promise<void> {
diff --git a/src/contract_wrappers/token_wrapper.ts b/src/contract_wrappers/token_wrapper.ts
index f7f0a0ce3..91af223e4 100644
--- a/src/contract_wrappers/token_wrapper.ts
+++ b/src/contract_wrappers/token_wrapper.ts
@@ -7,6 +7,7 @@ import {utils} from '../utils/utils';
import {eventUtils} from '../utils/event_utils';
import {constants} from '../utils/constants';
import {ContractWrapper} from './contract_wrapper';
+import {AbiDecoder} from '../utils/abi_decoder';
import {artifacts} from '../artifacts';
import {
TokenContract,
@@ -18,6 +19,8 @@ import {
ContractEventEmitter,
ContractEventObj,
MethodOpts,
+ LogWithDecodedArgs,
+ RawLog,
} from '../types';
const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 47155;
@@ -32,8 +35,9 @@ export class TokenWrapper extends ContractWrapper {
private _tokenContractsByAddress: {[address: string]: TokenContract};
private _tokenLogEventEmitters: ContractEventEmitter[];
private _tokenTransferProxyContractAddressFetcher: () => Promise<string>;
- constructor(web3Wrapper: Web3Wrapper, tokenTransferProxyContractAddressFetcher: () => Promise<string>) {
- super(web3Wrapper);
+ constructor(web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder,
+ tokenTransferProxyContractAddressFetcher: () => Promise<string>) {
+ super(web3Wrapper, abiDecoder);
this._tokenContractsByAddress = {};
this._tokenLogEventEmitters = [];
this._tokenTransferProxyContractAddressFetcher = tokenTransferProxyContractAddressFetcher;
@@ -277,6 +281,22 @@ export class TokenWrapper extends ContractWrapper {
return eventEmitter;
}
/**
+ * 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 subscriptionOpts Subscriptions options that let you configure the subscription.
+ * @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(tokenAddress: string, eventName: TokenEvents, subscriptionOpts: SubscriptionOpts,
+ indexFilterValues: IndexedFilterValues): Promise<LogWithDecodedArgs[]> {
+ const logs = await this._getLogsAsync(
+ tokenAddress, eventName, subscriptionOpts, indexFilterValues, artifacts.TokenArtifact.abi,
+ );
+ return logs;
+ }
+ /**
* Stops watching for all token events
*/
public async stopWatchingAllEventsAsync(): Promise<void> {
diff --git a/src/index.ts b/src/index.ts
index 9730d3fef..3359743e9 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -35,4 +35,5 @@ export {
DecodedLogArgs,
MethodOpts,
OrderTransactionOpts,
+ FilterObject,
} from './types';
diff --git a/src/types.ts b/src/types.ts
index 02230b0ab..35bb6af78 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -7,7 +7,6 @@ export enum ZeroExError {
UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
InvalidSignature = 'INVALID_SIGNATURE',
ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
- ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY',
InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
@@ -17,6 +16,11 @@ export enum ZeroExError {
NoNetworkId = 'NO_NETWORK_ID',
}
+export enum InternalZeroExError {
+ NoAbiDecoder = 'NO_ABI_DECODER',
+ ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY',
+}
+
/**
* Elliptic Curve signature
*/
@@ -198,6 +202,8 @@ export interface TokenTransferProxyContract extends Web3.ContractInstance {
export enum SolidityTypes {
Address = 'address',
Uint256 = 'uint256',
+ Uint8 = 'uint8',
+ Uint = 'uint',
}
export enum ExchangeContractErrCodes {
@@ -234,6 +240,8 @@ export enum ExchangeContractErrs {
BatchOrdersMustHaveAtLeastOneItem = 'BATCH_ORDERS_MUST_HAVE_AT_LEAST_ONE_ITEM',
}
+export type RawLog = Web3.LogEntry;
+
export interface ContractEvent {
logIndex: number;
transactionIndex: number;
@@ -338,6 +346,8 @@ export enum TokenEvents {
Approval = 'Approval',
}
+export type ContractEvents = TokenEvents|ExchangeEvents;
+
export interface IndexedFilterValues {
[index: string]: ContractEventArg;
}
@@ -460,3 +470,5 @@ export interface MethodOpts {
export interface OrderTransactionOpts {
shouldValidate: boolean;
}
+
+export type FilterObject = Web3.FilterObject;
diff --git a/src/utils/abi_decoder.ts b/src/utils/abi_decoder.ts
index 61c8eecd4..542591251 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, DecodedArgs} from '../types';
+import {AbiType, DecodedLogArgs, DecodedArgs, LogWithDecodedArgs, RawLog, SolidityTypes} from '../types';
import * as SolidityCoder from 'web3/lib/solidity/coder';
export class AbiDecoder {
@@ -10,40 +10,40 @@ export class AbiDecoder {
constructor(abiArrays: Web3.AbiDefinition[][]) {
_.map(abiArrays, this.addABI.bind(this));
}
- public decodeLog(logItem: Web3.LogEntry): DecodedArgs|undefined {
- const methodId = logItem.topics[0];
+ // This method can only decode logs from the 0x smart contracts
+ public tryToDecodeLogOrNoop(log: Web3.LogEntry): LogWithDecodedArgs|RawLog {
+ const methodId = log.topics[0];
const event = this.methodIds[methodId];
- if (!_.isUndefined(event)) {
- const logData = logItem.data;
- const decodedParams: DecodedLogArgs = {};
- let dataIndex = 0;
- let topicsIndex = 1;
+ 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(2));
- _.map(event.inputs, (param: Web3.EventParameter) => {
- let value;
- if (param.indexed) {
- value = logItem.topics[topicsIndex];
- topicsIndex++;
- } else {
- value = decodedData[dataIndex];
- dataIndex++;
- }
- if (param.type === 'address') {
- value = this.padZeros(new BigNumber(value).toString(16));
- } else if (param.type === 'uint256' || param.type === 'uint8' || param.type === 'int' ) {
- value = new BigNumber(value);
- }
- decodedParams[param.name] = value;
- });
+ const nonIndexedInputs = _.filter(event.inputs, input => !input.indexed);
+ const dataTypes = _.map(nonIndexedInputs, input => input.type);
+ const decodedData = SolidityCoder.decodeParams(dataTypes, logData.slice('0x'.length));
- return {
- event: event.name,
- args: decodedParams,
- };
- }
+ _.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 = this.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) => {
diff --git a/src/web3_wrapper.ts b/src/web3_wrapper.ts
index 0bf80a6e4..7576f3d40 100644
--- a/src/web3_wrapper.ts
+++ b/src/web3_wrapper.ts
@@ -9,10 +9,12 @@ export class Web3Wrapper {
private web3: Web3;
private defaults: Partial<Web3.TxData>;
private networkIdIfExists?: number;
+ private jsonRpcRequestId: number;
constructor(provider: Web3.Provider, defaults: Partial<Web3.TxData>) {
this.web3 = new Web3();
this.web3.setProvider(provider);
this.defaults = defaults;
+ this.jsonRpcRequestId = 0;
}
public setProvider(provider: Web3.Provider) {
delete this.networkIdIfExists;
@@ -100,6 +102,20 @@ export class Web3Wrapper {
const addresses: string[] = await promisify(this.web3.eth.getAccounts)();
return addresses;
}
+ public async getLogsAsync(filter: Web3.FilterObject): Promise<Web3.LogEntry[]> {
+ const payload = {
+ jsonrpc: '2.0',
+ id: this.jsonRpcRequestId++,
+ method: 'eth_getLogs',
+ params: [filter],
+ };
+ const logs = await this.sendRawPayloadAsync(payload);
+ return logs;
+ }
+ public keccak256(data: string): string {
+ const hash = this.web3.sha3(data);
+ return hash;
+ }
private getContractInstance<A extends Web3.ContractInstance>(abi: Web3.ContractAbi, address: string): A {
const web3ContractInstance = this.web3.eth.contract(abi).at(address);
const contractInstance = new Contract(web3ContractInstance, this.defaults) as any as A;
@@ -109,4 +125,10 @@ export class Web3Wrapper {
const networkId = await promisify(this.web3.version.getNetwork)();
return networkId;
}
+ private async sendRawPayloadAsync(payload: Web3.JSONRPCRequestPayload): Promise<any> {
+ const sendAsync = this.web3.currentProvider.sendAsync.bind(this.web3.currentProvider);
+ const response = await promisify(sendAsync)(payload);
+ const result = response.result;
+ return result;
+ }
}
diff --git a/test/exchange_wrapper_test.ts b/test/exchange_wrapper_test.ts
index 45a2d3907..71c5713ad 100644
--- a/test/exchange_wrapper_test.ts
+++ b/test/exchange_wrapper_test.ts
@@ -771,4 +771,78 @@ describe('ExchangeWrapper', () => {
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 subscriptionOpts: SubscriptionOpts = {
+ fromBlock: 'earliest',
+ toBlock: 'latest',
+ };
+ let txHash: string;
+ before(async () => {
+ [, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens();
+ 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, subscriptionOpts, 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, subscriptionOpts, 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 eventName = ExchangeEvents.LogFill;
+ const indexFilterValues = {
+ maker: differentMakerAddress,
+ };
+ const logs = await zeroEx.exchange.getLogsAsync(
+ eventName, subscriptionOpts, indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ expect(logs[0].args.maker).to.be.equal(differentMakerAddress);
+ });
+ });
});
diff --git a/test/token_wrapper_test.ts b/test/token_wrapper_test.ts
index 68dca0769..da020f714 100644
--- a/test/token_wrapper_test.ts
+++ b/test/token_wrapper_test.ts
@@ -14,6 +14,7 @@ import {
ContractEvent,
TransferContractEventArgs,
ApprovalContractEventArgs,
+ LogWithDecodedArgs,
} from '../src';
import {BlockchainLifecycle} from './utils/blockchain_lifecycle';
import {TokenUtils} from './utils/token_utils';
@@ -435,4 +436,57 @@ describe('TokenWrapper', () => {
})().catch(done);
});
});
+ describe('#getLogsAsync', () => {
+ let tokenAddress: string;
+ let tokenTransferProxyAddress: string;
+ const subscriptionOpts: SubscriptionOpts = {
+ fromBlock: 'earliest',
+ toBlock: 'latest',
+ };
+ let txHash: string;
+ before(async () => {
+ const token = tokens[0];
+ tokenAddress = token.address;
+ tokenTransferProxyAddress = await zeroEx.proxy.getContractAddressAsync();
+ });
+ 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(
+ tokenAddress, eventName, subscriptionOpts, indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ 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);
+ });
+ 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, subscriptionOpts, 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(
+ tokenAddress, eventName, subscriptionOpts, indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ expect(logs[0].args._owner).to.be.equal(coinbase);
+ });
+ });
});
diff --git a/test/utils/token_utils.ts b/test/utils/token_utils.ts
index 60cf4527b..51cb9411c 100644
--- a/test/utils/token_utils.ts
+++ b/test/utils/token_utils.ts
@@ -1,5 +1,5 @@
import * as _ from 'lodash';
-import {Token, ZeroExError} from '../../src';
+import {Token, InternalZeroExError} from '../../src/types';
const PROTOCOL_TOKEN_SYMBOL = 'ZRX';
@@ -11,7 +11,7 @@ export class TokenUtils {
public getProtocolTokenOrThrow(): Token {
const zrxToken = _.find(this.tokens, {symbol: PROTOCOL_TOKEN_SYMBOL});
if (_.isUndefined(zrxToken)) {
- throw new Error(ZeroExError.ZrxNotInTokenRegistry);
+ throw new Error(InternalZeroExError.ZrxNotInTokenRegistry);
}
return zrxToken;
}
diff --git a/yarn.lock b/yarn.lock
index c3b7f66e4..2d3fbb2fc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,9 +2,9 @@
# yarn lockfile v1
-"0x-json-schemas@^0.5.1":
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/0x-json-schemas/-/0x-json-schemas-0.5.1.tgz#fce6c2962e44930884a838e01f5b605ddd217686"
+"0x-json-schemas@^0.6.1":
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/0x-json-schemas/-/0x-json-schemas-0.6.1.tgz#ff1b09542422dd9292dd7618a7c5c21d41c99cbf"
dependencies:
jsonschema "^1.2.0"
@@ -1000,10 +1000,6 @@ camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
-caseless@~0.11.0:
- version "0.11.0"
- resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
-
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@@ -1046,7 +1042,7 @@ chai@^4.0.1:
pathval "^1.0.0"
type-detect "^4.0.0"
-chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
+chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
dependencies:
@@ -1222,15 +1218,15 @@ core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
-coveralls@^2.13.1:
- version "2.13.1"
- resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-2.13.1.tgz#d70bb9acc1835ec4f063ff9dac5423c17b11f178"
+coveralls@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.0.tgz#22ef730330538080d29b8c151dc9146afde88a99"
dependencies:
- js-yaml "3.6.1"
- lcov-parse "0.0.10"
- log-driver "1.2.5"
- minimist "1.2.0"
- request "2.79.0"
+ js-yaml "^3.6.1"
+ lcov-parse "^0.0.10"
+ log-driver "^1.2.5"
+ minimist "^1.2.0"
+ request "^2.79.0"
create-ecdh@^4.0.0:
version "4.0.0"
@@ -1608,9 +1604,9 @@ escope@^3.6.0:
esrecurse "^4.1.0"
estraverse "^4.1.1"
-esprima@^2.6.0:
- version "2.7.3"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
+esprima@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
esrecurse@^4.1.0:
version "4.2.0"
@@ -2050,16 +2046,6 @@ gauge@~2.7.3:
strip-ansi "^3.0.1"
wide-align "^1.1.0"
-generate-function@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74"
-
-generate-object-property@^1.1.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0"
- dependencies:
- is-property "^1.0.0"
-
get-caller-file@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
@@ -2185,15 +2171,6 @@ har-schema@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
-har-validator@~2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d"
- dependencies:
- chalk "^1.1.1"
- commander "^2.9.0"
- is-my-json-valid "^2.12.4"
- pinkie-promise "^2.0.0"
-
har-validator@~4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
@@ -2494,15 +2471,6 @@ is-hex-prefixed@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554"
-is-my-json-valid@^2.12.4:
- version "2.16.0"
- resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693"
- dependencies:
- generate-function "^2.0.0"
- generate-object-property "^1.1.0"
- jsonpointer "^4.0.0"
- xtend "^4.0.0"
-
is-number@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
@@ -2535,10 +2503,6 @@ is-primitive@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
-is-property@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
-
is-regex@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
@@ -2653,12 +2617,12 @@ js-tokens@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
-js-yaml@3.6.1:
- version "3.6.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30"
+js-yaml@^3.6.1:
+ version "3.10.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc"
dependencies:
argparse "^1.0.7"
- esprima "^2.6.0"
+ esprima "^4.0.0"
jsbn@~0.1.0:
version "0.1.1"
@@ -2728,10 +2692,6 @@ jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
-jsonpointer@^4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
-
jsonschema@*, jsonschema@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.0.tgz#d6ebaf70798db7b3a20c544f6c9ef9319b077de2"
@@ -2800,7 +2760,7 @@ lcid@^1.0.0:
dependencies:
invert-kv "^1.0.0"
-lcov-parse@0.0.10:
+lcov-parse@^0.0.10:
version "0.0.10"
resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3"
@@ -2915,6 +2875,10 @@ lodash.create@3.1.1:
lodash._basecreate "^3.0.0"
lodash._isiterateecall "^3.0.0"
+lodash.get@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+
lodash.isarguments@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
@@ -2939,7 +2903,7 @@ lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
-log-driver@1.2.5:
+log-driver@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.5.tgz#7ae4ec257302fd790d557cb10c97100d857b0056"
@@ -3162,7 +3126,7 @@ minimist@0.0.8, minimist@~0.0.1:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-minimist@1.2.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@~1.2.0:
+minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
@@ -3236,14 +3200,15 @@ native-promise-only@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/native-promise-only/-/native-promise-only-0.8.1.tgz#20a318c30cb45f71fe7adfbf7b21c99c1472ef11"
-nise@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/nise/-/nise-1.0.1.tgz#0da92b10a854e97c0f496f6c2845a301280b3eef"
+nise@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/nise/-/nise-1.1.0.tgz#37e41b9bf0041ccb83d1bf03e79440bbc0db10ad"
dependencies:
formatio "^1.2.0"
just-extend "^1.1.22"
lolex "^1.6.0"
path-to-regexp "^1.7.0"
+ text-encoding "^0.6.4"
node-abi@^2.0.0:
version "2.0.3"
@@ -3781,10 +3746,6 @@ punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
-qs@~6.3.0:
- version "6.3.2"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c"
-
qs@~6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
@@ -3997,32 +3958,7 @@ request-promise-native@^1.0.4:
stealthy-require "^1.1.0"
tough-cookie ">=2.3.0"
-request@2.79.0:
- version "2.79.0"
- resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
- dependencies:
- aws-sign2 "~0.6.0"
- aws4 "^1.2.1"
- caseless "~0.11.0"
- combined-stream "~1.0.5"
- extend "~3.0.0"
- forever-agent "~0.6.1"
- form-data "~2.1.1"
- har-validator "~2.0.6"
- hawk "~3.1.3"
- http-signature "~1.1.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.7"
- oauth-sign "~0.8.1"
- qs "~6.3.0"
- stringstream "~0.0.4"
- tough-cookie "~2.3.0"
- tunnel-agent "~0.4.1"
- uuid "^3.0.0"
-
-request@^2.54.0, request@^2.67.0, request@^2.81.0:
+request@^2.54.0, request@^2.67.0, request@^2.79.0, request@^2.81.0:
version "2.81.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
dependencies:
@@ -4255,15 +4191,16 @@ single-line-log@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/single-line-log/-/single-line-log-0.4.1.tgz#87a55649f749d783ec0dcd804e8140d9873c7cee"
-sinon@^3.0.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/sinon/-/sinon-3.2.0.tgz#8848a66ab6e8b80b5532e3824f59f83ea2628c77"
+sinon@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.0.0.tgz#a54a5f0237aa1dd2215e5e81c89b42b50c4fdb6b"
dependencies:
diff "^3.1.0"
formatio "1.2.0"
+ lodash.get "^4.4.2"
lolex "^2.1.2"
native-promise-only "^0.8.1"
- nise "^1.0.1"
+ nise "^1.1.0"
path-to-regexp "^1.7.0"
samsam "^1.1.3"
text-encoding "0.6.4"
@@ -4637,7 +4574,7 @@ test-exclude@^4.1.1:
read-pkg-up "^1.0.1"
require-main-filename "^1.0.1"
-text-encoding@0.6.4:
+text-encoding@0.6.4, text-encoding@^0.6.4:
version "0.6.4"
resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
@@ -4781,10 +4718,6 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
-tunnel-agent@~0.4.1:
- version "0.4.3"
- resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
-
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
@@ -4829,9 +4762,9 @@ types-bn@^0.0.1:
dependencies:
bn.js "4.11.7"
-types-ethereumjs-util@^0.0.5:
+types-ethereumjs-util@0xProject/types-ethereumjs-util:
version "0.0.5"
- resolved "https://registry.yarnpkg.com/types-ethereumjs-util/-/types-ethereumjs-util-0.0.5.tgz#a65060741c73d1ee5157b9ba2d502b4fe4a19d1c"
+ resolved "https://codeload.github.com/0xProject/types-ethereumjs-util/tar.gz/b9ae55d2c2711d89f63f7fc53a78579f2d4fbd74"
dependencies:
bn.js "^4.11.7"
buffer "^5.0.6"
@@ -5005,9 +4938,9 @@ web3-provider-engine@^8.4.0:
xhr "^2.2.0"
xtend "^4.0.1"
-web3-typescript-typings@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/web3-typescript-typings/-/web3-typescript-typings-0.6.0.tgz#ed8f01cfa3ef2c7ff2f1b0cafe7c682d0c6dc333"
+web3-typescript-typings@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/web3-typescript-typings/-/web3-typescript-typings-0.6.2.tgz#5dd9bf4dcd1d6dd6897c87d055d1f5cc8f98dfbd"
dependencies:
bignumber.js "^4.0.2"