diff options
-rw-r--r-- | src/0x.ts | 24 | ||||
-rw-r--r-- | src/types.ts | 4 | ||||
-rw-r--r-- | src/utils/abi_decoder.ts | 66 |
3 files changed, 79 insertions, 15 deletions
@@ -12,6 +12,7 @@ import {constants} from './utils/constants'; import {utils} from './utils/utils'; import {signatureUtils} from './utils/signature_utils'; import {assert} from './utils/assert'; +import {AbiDecoder} from './utils/abi_decoder'; import {artifacts} from './artifacts'; import {ExchangeWrapper} from './contract_wrappers/exchange_wrapper'; import {TokenRegistryWrapper} from './contract_wrappers/token_registry_wrapper'; @@ -70,6 +71,7 @@ export class ZeroEx { */ public proxy: TokenTransferProxyWrapper; private _web3Wrapper: Web3Wrapper; + private _abiDecoder: AbiDecoder; /** * Verifies that the elliptic curve signature `signature` was generated * by signing `data` with the private key corresponding to the `signerAddress` address. @@ -183,7 +185,9 @@ export class ZeroEx { // We re-assign the send method so that Web3@1.0 providers work with 0x.js (provider as any).sendAsync = (provider as any).send; } - this._registerArtifactsWithinABIDecoder(); + const artifactJSONs = _.values(artifacts); + const abiArrays = _.map(artifactJSONs, artifact => artifact.abi); + this._abiDecoder = new AbiDecoder(abiArrays); const gasPrice = _.isUndefined(config) ? undefined : config.gasPrice; const defaults = { gasPrice, @@ -281,16 +285,13 @@ export class ZeroEx { if (!_.isNull(transactionReceipt)) { clearInterval(intervalId); const logsWithDecodedArgs = _.map(transactionReceipt.logs, (log: Web3.LogEntry) => { - const decodedLog = abiDecoder.decodeLogs([log])[0]; - const decodedArgs = decodedLog.events; - const args: DecodedLogArgs = {}; - _.forEach(decodedArgs, arg => { - args[arg.name] = arg.value; - }); + const decodedLog = this._abiDecoder.decodeLog(log); + if (_.isUndefined(decodedLog)) { + throw new Error('Unknown log'); + } const logWithDecodedArgs: LogWithDecodedArgs = { ...log, - args, - event: decodedLog.name, + ...decodedLog, }; return logWithDecodedArgs; }); @@ -304,9 +305,4 @@ export class ZeroEx { }); return txReceiptPromise; } - private _registerArtifactsWithinABIDecoder(): void { - for (const artifact of _.values(artifacts)) { - abiDecoder.addABI(artifact.abi); - } - } } diff --git a/src/types.ts b/src/types.ts index 9d3f77127..4f9c6714d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -402,11 +402,13 @@ export interface DecodedLogArgs { [argName: string]: ContractEventArg; } -export interface LogWithDecodedArgs extends Web3.LogEntry { +export interface DecodedArgs { args: DecodedLogArgs; event: string; } +export interface LogWithDecodedArgs extends Web3.LogEntry, DecodedArgs {} + export interface TransactionReceiptWithDecodedLogs extends Web3.TransactionReceipt { logs: LogWithDecodedArgs[]; } diff --git a/src/utils/abi_decoder.ts b/src/utils/abi_decoder.ts new file mode 100644 index 000000000..0600f86f2 --- /dev/null +++ b/src/utils/abi_decoder.ts @@ -0,0 +1,66 @@ +import * as Web3 from 'web3'; +import * as _ from 'lodash'; +import {AbiType, DecodedLogArgs, DecodedArgs} from '../types'; +import * as SolidityCoder from 'web3/lib/solidity/coder.js'; + +export class AbiDecoder { + private savedABIs: Web3.AbiDefinition[] = []; + private methodIDs: {[signatureHash: string]: Web3.EventAbi} = {}; + constructor(abiArrays: Web3.AbiDefinition[][]) { + _.map(abiArrays, this.addABI.bind(this)); + } + public decodeLog(logItem: Web3.LogEntry): DecodedArgs|undefined { + const methodID = logItem.topics[0]; + const event = this.methodIDs[methodID]; + if (!_.isUndefined(event)) { + const logData = logItem.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 Web3().toBigNumber(value).toString(16)); + } else if (param.type === 'uint256' || param.type === 'uint8' || param.type === 'int' ) { + value = new Web3().toBigNumber(value).toString(10); + } + decodedParams[param.name] = value; + }); + + return { + event: event.name, + args: decodedParams, + }; + } + } + private addABI(abiArray: Web3.AbiDefinition[]): void { + _.map(abiArray, (abi: Web3.AbiDefinition) => { + if (abi.type === AbiType.Event) { + const signature = `${abi.name}(${_.map(abi.inputs, input => input.type).join(',')})`; + const signatureHash = new Web3().sha3(signature); + this.methodIDs[signatureHash] = abi; + } + }); + this.savedABIs = this.savedABIs.concat(abiArray); + } + private padZeros(address: string) { + let formatted = address; + if (formatted.indexOf('0x') !== -1) { + formatted = formatted.slice(2); + } + + formatted = _.padStart(formatted, 40, '0'); + return '0x' + formatted; + } +} |