diff options
author | Greg Hysen <greg.hysen@gmail.com> | 2019-02-07 15:47:40 +0800 |
---|---|---|
committer | Greg Hysen <greg.hysen@gmail.com> | 2019-02-09 08:25:30 +0800 |
commit | 6406126ae356a6dd1102cf603d4ce00333c9fd62 (patch) | |
tree | 390299a3cd99152ed17c3657a723978ab73ceaf2 | |
parent | 6bde77bb57e1d0526154cc6b2b3bb0ff70c1d2b0 (diff) | |
download | dexon-0x-contracts-6406126ae356a6dd1102cf603d4ce00333c9fd62.tar.gz dexon-0x-contracts-6406126ae356a6dd1102cf603d4ce00333c9fd62.tar.zst dexon-0x-contracts-6406126ae356a6dd1102cf603d4ce00333c9fd62.zip |
Merged tx decoder into AbiDecoder in utils and merged zeroex tx decoder into ContractWrappers.
-rw-r--r-- | packages/contract-wrappers/src/contract_wrappers.ts | 12 | ||||
-rw-r--r-- | packages/contract-wrappers/src/index.ts | 7 | ||||
-rw-r--r-- | packages/contract-wrappers/src/utils/zeroex_transaction_decoder.ts | 88 | ||||
-rw-r--r-- | packages/contract-wrappers/test/calldata_decoder_test.ts (renamed from packages/contract-wrappers/test/zeroex_transaction_decoder_test.ts) | 87 | ||||
-rw-r--r-- | packages/utils/CHANGELOG.json | 9 | ||||
-rw-r--r-- | packages/utils/src/abi_decoder.ts | 121 | ||||
-rw-r--r-- | packages/utils/src/index.ts | 1 | ||||
-rw-r--r-- | packages/utils/src/transaction_decoder.ts | 112 | ||||
-rw-r--r-- | packages/utils/src/types.ts | 15 | ||||
-rw-r--r-- | packages/utils/test/abi_decoder_test.ts (renamed from packages/utils/test/transaction_decoder_test.ts) | 32 |
10 files changed, 145 insertions, 339 deletions
diff --git a/packages/contract-wrappers/src/contract_wrappers.ts b/packages/contract-wrappers/src/contract_wrappers.ts index 4e594593e..63de61a47 100644 --- a/packages/contract-wrappers/src/contract_wrappers.ts +++ b/packages/contract-wrappers/src/contract_wrappers.ts @@ -1,4 +1,5 @@ import { + DutchAuction, ERC20Proxy, ERC20Token, ERC721Proxy, @@ -8,6 +9,7 @@ import { OrderValidator, WETH9, } from '@0x/contract-artifacts'; +import { AbiDecoder } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; @@ -87,6 +89,7 @@ export class ContractWrappers { }; this._web3Wrapper = new Web3Wrapper(provider, txDefaults); const artifactsArray = [ + DutchAuction, ERC20Proxy, ERC20Token, ERC721Proxy, @@ -97,7 +100,7 @@ export class ContractWrappers { WETH9, ]; _.forEach(artifactsArray, artifact => { - this._web3Wrapper.abiDecoder.addABI(artifact.compilerOutput.abi); + this._web3Wrapper.abiDecoder.addABI(artifact.compilerOutput.abi, artifact.contractName); }); const blockPollingIntervalMs = _.isUndefined(config.blockPollingIntervalMs) ? constants.DEFAULT_BLOCK_POLLING_INTERVAL @@ -168,4 +171,11 @@ export class ContractWrappers { public getProvider(): Provider { return this._web3Wrapper.getProvider(); } + /** + * Get the provider instance currently used by contract-wrappers + * @return Web3 provider instance + */ + public getAbiDecoder(): AbiDecoder { + return this._web3Wrapper.abiDecoder; + } } diff --git a/packages/contract-wrappers/src/index.ts b/packages/contract-wrappers/src/index.ts index fd4b9d357..69bbe3c91 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -37,7 +37,6 @@ export { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapp export { DutchAuctionWrapper } from './contract_wrappers/dutch_auction_wrapper'; export { TransactionEncoder } from './utils/transaction_encoder'; -export { ZeroExTransactionDecoder } from './utils/zeroex_transaction_decoder'; export { ContractWrappersError, @@ -73,12 +72,6 @@ export { } from '@0x/types'; export { - DeployedContractInfo, - TransactionData, - TransactionProperties -} from '@0x/utils'; - -export { BlockParamLiteral, BlockParam, ContractEventArg, diff --git a/packages/contract-wrappers/src/utils/zeroex_transaction_decoder.ts b/packages/contract-wrappers/src/utils/zeroex_transaction_decoder.ts deleted file mode 100644 index 17f06497c..000000000 --- a/packages/contract-wrappers/src/utils/zeroex_transaction_decoder.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { getContractAddressesForNetworkOrThrow, NetworkId } from '@0x/contract-addresses'; -import * as ContractArtifacts from '@0x/contract-artifacts'; -import { SimpleContractArtifact } from '@0x/types'; -import { AbiDefinition, ContractAbi } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { - DeployedContractInfo, - DeployedContractInfoByName, - TransactionData, - TransactionDecoder, - TransactionProperties, -} from '@0x/utils'; - -export class ZeroExTransactionDecoder extends TransactionDecoder { - private static _instance: ZeroExTransactionDecoder; - /** - * Adds a set of ABI definitions, after which transaction data targeting these ABI's can be decoded. - * Additional properties can be included to disambiguate similar ABI's. For example, if two functions - * have the same signature but different parameter names, then their ABI definitions can be disambiguated - * by specifying a contract name. - * @param abiDefinitions ABI definitions for a given contract. - * @param contractName Name of contract that encapsulates the ABI definitions (optional). - * @param deploymentInfos A collection of network/address pairs where this contract is deployed (optional). - */ - public static addABI( - abiDefinitions: AbiDefinition[], - contractName: string, - deploymentInfos?: DeployedContractInfo[], - ): void { - const instance = ZeroExTransactionDecoder._getInstance(); - instance.addABI(abiDefinitions, contractName, deploymentInfos); - } - /** - * Decodes transaction data for a known ABI. - * @param txData hex-encoded transaction data. - * @param txProperties Properties about the transaction used to disambiguate similar ABI's (optional). - * @return Decoded transaction data. Includes: function name and signature, along with the decoded arguments. - */ - public static decode(calldata: string, txProperties?: TransactionProperties): TransactionData { - const instance = ZeroExTransactionDecoder._getInstance(); - const decodedCalldata = instance.decode(calldata, txProperties); - return decodedCalldata; - } - /** - * Gets instance for singleton. - * @return singleton instance. - */ - private static _getInstance(): ZeroExTransactionDecoder { - if (!ZeroExTransactionDecoder._instance) { - ZeroExTransactionDecoder._instance = new ZeroExTransactionDecoder(); - } - return ZeroExTransactionDecoder._instance; - } - /** - * Adds all known contract ABI's defined by the @0x/Artifacts package, along with known 0x - * contract addresses. - */ - private constructor() { - super(); - // Load addresses by contract name - const deployedContractInfoByName: DeployedContractInfoByName = {}; - _.each(NetworkId, (networkId: any) => { - if (typeof networkId !== 'number') { - return; - } - const contractAddressesForNetwork = getContractAddressesForNetworkOrThrow(networkId); - _.each(contractAddressesForNetwork, (contractAddress: string, contractName: string) => { - const contractNameLowercase = _.toLower(contractName); - if (_.isUndefined(deployedContractInfoByName[contractNameLowercase])) { - deployedContractInfoByName[contractNameLowercase] = []; - } - deployedContractInfoByName[contractNameLowercase].push({ - contractAddress, - networkId, - }); - }); - }); - // Load contract artifacts - _.each(ContractArtifacts, (contractArtifactAsJson: any) => { - const conractArtifact = contractArtifactAsJson as SimpleContractArtifact; - const contractName = conractArtifact.contractName; - const contractNameLowercase = _.toLower(contractName); - const contractAbi: ContractAbi = conractArtifact.compilerOutput.abi; - this.addABI(contractAbi, contractName, deployedContractInfoByName[contractNameLowercase]); - }); - } -} diff --git a/packages/contract-wrappers/test/zeroex_transaction_decoder_test.ts b/packages/contract-wrappers/test/calldata_decoder_test.ts index 83c810c3a..d44e13a89 100644 --- a/packages/contract-wrappers/test/zeroex_transaction_decoder_test.ts +++ b/packages/contract-wrappers/test/calldata_decoder_test.ts @@ -2,14 +2,12 @@ import { constants, OrderFactory } from '@0x/contracts-test-utils'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils } from '@0x/order-utils'; import { SignedOrder } from '@0x/types'; -import { AbiEncoder, addressUtils, BigNumber } from '@0x/utils'; +import { addressUtils, BigNumber } from '@0x/utils'; import * as chai from 'chai'; -import { MethodAbi } from 'ethereum-types'; import * as _ from 'lodash'; import 'mocha'; import { ContractAddresses, ContractWrappers } from '../src'; -import { ZeroExTransactionDecoder } from '../src/utils/zeroex_transaction_decoder'; import { chaiSetup } from './utils/chai_setup'; import { migrateOnceAsync } from './utils/migrate'; @@ -20,7 +18,7 @@ const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -describe.only('ZeroExTransactionDecoder', () => { +describe('ABI Decoding Calldata', () => { const defaultERC20MakerAssetAddress = addressUtils.generatePseudoRandomAddress(); const matchOrdersSignature = 'matchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),bytes,bytes)'; @@ -30,6 +28,7 @@ describe.only('ZeroExTransactionDecoder', () => { let orderRight = {}; let matchOrdersTxData: string; let contractAddresses: ContractAddresses; + let contractWrappers: ContractWrappers; before(async () => { // Create accounts @@ -80,15 +79,15 @@ describe.only('ZeroExTransactionDecoder', () => { contractAddresses, blockPollingIntervalMs: 10, }; - const contractWrappers = new ContractWrappers(provider, config); + contractWrappers = new ContractWrappers(provider, config); const transactionEncoder = await contractWrappers.exchange.transactionEncoderAsync(); matchOrdersTxData = transactionEncoder.matchOrdersTx(signedOrderLeft, signedOrderRight); }); describe('decode', () => { - it('should successfully decode DutchAuction.matchOrders txData', async () => { + it('should successfully decode DutchAuction.matchOrders calldata', async () => { const contractName = 'DutchAuction'; - const decodedTxData = ZeroExTransactionDecoder.decode(matchOrdersTxData, { contractName }); + const decodedTxData = contractWrappers.getAbiDecoder().tryDecodeCalldata(matchOrdersTxData, contractName); const expectedFunctionName = 'matchOrders'; const expectedFunctionArguments = { buyOrder: orderLeft, @@ -100,9 +99,9 @@ describe.only('ZeroExTransactionDecoder', () => { expect(decodedTxData.functionSignature).to.be.equal(matchOrdersSignature); expect(decodedTxData.functionArguments).to.be.deep.equal(expectedFunctionArguments); }); - it('should successfully decode Exchange.matchOrders txData (and distinguish from DutchAuction.matchOrders)', async () => { + it('should successfully decode Exchange.matchOrders calldata (and distinguish from DutchAuction.matchOrders)', async () => { const contractName = 'Exchange'; - const decodedTxData = ZeroExTransactionDecoder.decode(matchOrdersTxData, { contractName }); + const decodedTxData = contractWrappers.getAbiDecoder().tryDecodeCalldata(matchOrdersTxData, contractName); const expectedFunctionName = 'matchOrders'; const expectedFunctionArguments = { leftOrder: orderLeft, @@ -114,77 +113,11 @@ describe.only('ZeroExTransactionDecoder', () => { expect(decodedTxData.functionSignature).to.be.equal(matchOrdersSignature); expect(decodedTxData.functionArguments).to.be.deep.equal(expectedFunctionArguments); }); - it('should successfully decode Exchange.matchOrders, using exchange address to identify the exchange contract', async () => { - const contractAddress = contractAddresses.exchange; - const decodedTxData = ZeroExTransactionDecoder.decode(matchOrdersTxData, { contractAddress }); - const expectedFunctionName = 'matchOrders'; - const expectedFunctionArguments = { - leftOrder: orderLeft, - rightOrder: orderRight, - leftSignature: signedOrderLeft.signature, - rightSignature: signedOrderRight.signature, - }; - expect(decodedTxData.functionName).to.be.equal(expectedFunctionName); - expect(decodedTxData.functionSignature).to.be.equal(matchOrdersSignature); - expect(decodedTxData.functionArguments).to.be.deep.equal(expectedFunctionArguments); - }); - it('should throw if cannot decode txData', async () => { - const contractAddress = contractAddresses.exchange; + it('should throw if cannot decode calldata', async () => { const badTxData = '0x01020304'; expect(() => { - ZeroExTransactionDecoder.decode(badTxData, { contractAddress }); + contractWrappers.getAbiDecoder().tryDecodeCalldata(badTxData); }).to.throw("No functions registered for selector '0x01020304'"); }); }); - - describe('addABI', () => { - it('should successfully add a new ABI', async () => { - // Add new ABI - const abi: MethodAbi = { - name: 'foobar', - type: 'function', - inputs: [ - { - name: 'addr', - type: 'address', - }, - ], - outputs: [ - { - name: 'butter', - type: 'string', - }, - ], - constant: false, - payable: false, - stateMutability: 'pure', - }; - const contractName = 'newContract'; - const contractAddress = addressUtils.generatePseudoRandomAddress(); - const networkId = 1; - const contractInfo = [ - { - contractAddress, - networkId, - }, - ]; - ZeroExTransactionDecoder.addABI([abi], contractName, contractInfo); - // Create some tx data - const foobarEncoder = new AbiEncoder.Method(abi); - const foobarSignature = foobarEncoder.getSignature(); - const foobarTxData = foobarEncoder.encode([contractAddress]); - // Decode tx data using contract name - const decodedTxData = ZeroExTransactionDecoder.decode(foobarTxData, { contractName }); - const expectedFunctionName = abi.name; - const expectedFunctionArguments = { - addr: contractAddress, - }; - expect(decodedTxData.functionName).to.be.equal(expectedFunctionName); - expect(decodedTxData.functionSignature).to.be.equal(foobarSignature); - expect(decodedTxData.functionArguments).to.be.deep.equal(expectedFunctionArguments); - // Decode tx data using contract address - const decodedTxDataDecodedWithAddress = ZeroExTransactionDecoder.decode(foobarTxData, { contractAddress }); - expect(decodedTxDataDecodedWithAddress).to.be.deep.equal(decodedTxData); - }); - }); }); diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index 9ce2a4c52..95f61a43c 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "version": "4.1.0", + "changes": [ + { + "note": "Added method decoding to AbiDecoder", + "pr": 1569 + } + ] + }, + { "version": "4.0.4", "changes": [ { diff --git a/packages/utils/src/abi_decoder.ts b/packages/utils/src/abi_decoder.ts index 28b6418d8..c817ad285 100644 --- a/packages/utils/src/abi_decoder.ts +++ b/packages/utils/src/abi_decoder.ts @@ -6,28 +6,49 @@ import { EventParameter, LogEntry, LogWithDecodedArgs, + MethodAbi, RawLog, SolidityTypes, } from 'ethereum-types'; import * as ethers from 'ethers'; import * as _ from 'lodash'; +import { AbiEncoder } from '.'; import { addressUtils } from './address_utils'; import { BigNumber } from './configured_bignumber'; +import { FunctionInfoBySelector, TransactionData } from './types'; /** * AbiDecoder allows you to decode event logs given a set of supplied contract ABI's. It takes the contract's event * signature from the ABI and attempts to decode the logs using it. */ export class AbiDecoder { - private readonly _methodIds: { [signatureHash: string]: { [numIndexedArgs: number]: EventAbi } } = {}; + private readonly _eventIds: { [signatureHash: string]: { [numIndexedArgs: number]: EventAbi } } = {}; + private readonly _functionInfoBySelector: FunctionInfoBySelector = {}; + /** + * Retrieves the function selector from tranasction data. + * @param calldata hex-encoded transaction data. + * @return hex-encoded function selector. + */ + private static _getFunctionSelector(calldata: string): string { + const functionSelectorLength = 10; + if (!calldata.startsWith('0x') || calldata.length < functionSelectorLength) { + throw new Error( + `Malformed transaction data. Must include a hex prefix '0x' and 4-byte function selector. Got '${calldata}'`, + ); + } + const functionSelector = calldata.substr(0, functionSelectorLength); + return functionSelector; + } /** * Instantiate an AbiDecoder * @param abiArrays An array of contract ABI's * @return AbiDecoder instance */ constructor(abiArrays: AbiDefinition[][]) { - _.forEach(abiArrays, this.addABI.bind(this)); + _.each(abiArrays, (abi) => { + this.addABI(abi); + }); } /** * Attempt to decode a log given the ABI's the AbiDecoder knows about. @@ -37,10 +58,10 @@ export class AbiDecoder { public tryToDecodeLogOrNoop<ArgsType extends DecodedLogArgs>(log: LogEntry): LogWithDecodedArgs<ArgsType> | RawLog { const methodId = log.topics[0]; const numIndexedArgs = log.topics.length - 1; - if (_.isUndefined(this._methodIds[methodId]) || _.isUndefined(this._methodIds[methodId][numIndexedArgs])) { + if (_.isUndefined(this._eventIds[methodId]) || _.isUndefined(this._eventIds[methodId][numIndexedArgs])) { return log; } - const event = this._methodIds[methodId][numIndexedArgs]; + const event = this._eventIds[methodId][numIndexedArgs]; const ethersInterface = new ethers.utils.Interface([event]); const decodedParams: DecodedLogArgs = {}; let topicsIndex = 1; @@ -89,25 +110,93 @@ export class AbiDecoder { } } /** - * Add additional ABI definitions to the AbiDecoder - * @param abiArray An array of ABI definitions to add to the AbiDecoder + * Decodes transaction data for a known ABI. + * @param calldata hex-encoded transaction data. + * @param contractName used to disambiguate similar ABI's (optional). + * @return Decoded transaction data. Includes: function name and signature, along with the decoded arguments. */ - public addABI(abiArray: AbiDefinition[]): void { + public tryDecodeCalldata(calldata: string, contractName?: string): TransactionData { + const functionSelector = AbiDecoder._getFunctionSelector(calldata); + const candidateFunctionInfos = this._functionInfoBySelector[functionSelector]; + if (_.isUndefined(candidateFunctionInfos)) { + throw new Error(`No functions registered for selector '${functionSelector}'`); + } + const functionInfo = _.find(candidateFunctionInfos, txDecoder => { + return ( + (_.isUndefined(contractName) || + _.toLower(txDecoder.contractName) === _.toLower(contractName))); + }); + if (_.isUndefined(functionInfo)) { + throw new Error(`No function registered with selector ${functionSelector} and contract name ${contractName}.`); + } else if (_.isUndefined(functionInfo.abiEncoder)) { + throw new Error( + `Function ABI Encoder is not defined, for function registered with selector ${functionSelector} and contract name ${contractName}.`, + ); + } + const functionName = functionInfo.abiEncoder.getDataItem().name; + const functionSignature = functionInfo.abiEncoder.getSignatureType(); + const functionArguments = functionInfo.abiEncoder.decode(calldata); + const decodedCalldata = { + functionName, + functionSignature, + functionArguments, + }; + return decodedCalldata; + } + /** + * Adds a set of ABI definitions, after which transaction data targeting these ABI's can be decoded. + * Additional properties can be included to disambiguate similar ABI's. For example, if two functions + * have the same signature but different parameter names, then their ABI definitions can be disambiguated + * by specifying a contract name. + * @param abiDefinitions ABI definitions for a given contract. + * @param contractName Name of contract that encapsulates the ABI definitions (optional). + */ + public addABI( + abiArray: AbiDefinition[], + contractName?: string + ): void { if (_.isUndefined(abiArray)) { return; } const ethersInterface = new ethers.utils.Interface(abiArray); _.map(abiArray, (abi: AbiDefinition) => { - if (abi.type === AbiType.Event) { - // tslint:disable-next-line:no-unnecessary-type-assertion - const eventAbi = abi as EventAbi; - const topic = ethersInterface.events[eventAbi.name].topic; - const numIndexedArgs = _.reduce(eventAbi.inputs, (sum, input) => (input.indexed ? sum + 1 : sum), 0); - this._methodIds[topic] = { - ...this._methodIds[topic], - [numIndexedArgs]: eventAbi, - }; + switch (abi.type) { + case AbiType.Event: + this._addEventABI(abi as EventAbi, ethersInterface); + break; + + case AbiType.Function: + this._addMethodABI(abi as MethodAbi, contractName); + break; + + default: + // ignore other types + break; } }); } + private _addEventABI(abi: EventAbi, ethersInterface: ethers.utils.Interface): void { + // tslint:disable-next-line:no-unnecessary-type-assertion + const eventAbi = abi as EventAbi; + const topic = ethersInterface.events[eventAbi.name].topic; + const numIndexedArgs = _.reduce(eventAbi.inputs, (sum, input) => (input.indexed ? sum + 1 : sum), 0); + this._eventIds[topic] = { + ...this._eventIds[topic], + [numIndexedArgs]: eventAbi, + }; + } + private _addMethodABI(methodAbi: MethodAbi, contractName?: string): void { + const abiEncoder = new AbiEncoder.Method(methodAbi); + const functionSelector = abiEncoder.getSelector(); + if (!(functionSelector in this._functionInfoBySelector)) { + this._functionInfoBySelector[functionSelector] = []; + } + // Recored a copy of this ABI for each deployment + const functionSignature = abiEncoder.getSignature(); + this._functionInfoBySelector[functionSelector].push({ + functionSignature, + abiEncoder, + contractName, + }); + } } diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 6f1c14c83..467129d2b 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -12,4 +12,3 @@ export { fetchAsync } from './fetch_async'; export { signTypedDataUtils } from './sign_typed_data_utils'; export import AbiEncoder = require('./abi_encoder'); export * from './types'; -export { TransactionDecoder } from './transaction_decoder'; diff --git a/packages/utils/src/transaction_decoder.ts b/packages/utils/src/transaction_decoder.ts deleted file mode 100644 index 9d567286e..000000000 --- a/packages/utils/src/transaction_decoder.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { AbiDefinition, MethodAbi } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { AbiEncoder } from '.'; -import { DeployedContractInfo, FunctionInfoBySelector, TransactionData, TransactionProperties } from './types'; - -export class TransactionDecoder { - private readonly _functionInfoBySelector: FunctionInfoBySelector = {}; - /** - * Retrieves the function selector from tranasction data. - * @param txData hex-encoded transaction data. - * @return hex-encoded function selector. - */ - private static _getFunctionSelector(txData: string): string { - const functionSelectorLength = 10; - if (!txData.startsWith('0x') || txData.length < functionSelectorLength) { - throw new Error( - `Malformed transaction data. Must include a hex prefix '0x' and 4-byte function selector. Got '${txData}'`, - ); - } - const functionSelector = txData.substr(0, functionSelectorLength); - return functionSelector; - } - /** - * Adds a set of ABI definitions, after which transaction data targeting these ABI's can be decoded. - * Additional properties can be included to disambiguate similar ABI's. For example, if two functions - * have the same signature but different parameter names, then their ABI definitions can be disambiguated - * by specifying a contract name. - * @param abiDefinitions ABI definitions for a given contract. - * @param contractName Name of contract that encapsulates the ABI definitions (optional). - * @param deploymentInfos A collection of network/address pairs where this contract is deployed (optional). - */ - public addABI( - abiDefinitions: AbiDefinition[], - contractName?: string, - deploymentInfos?: DeployedContractInfo[], - ): void { - // Disregard definitions that are not functions - // tslint:disable no-unnecessary-type-assertion - const functionAbis = _.filter(abiDefinitions, abiEntry => { - return abiEntry.type === 'function'; - }) as MethodAbi[]; - // tslint:enable no-unnecessary-type-assertion - // Record function ABI's - _.each(functionAbis, functionAbi => { - const abiEncoder = new AbiEncoder.Method(functionAbi); - const functionSelector = abiEncoder.getSelector(); - if (!(functionSelector in this._functionInfoBySelector)) { - this._functionInfoBySelector[functionSelector] = []; - } - // Recored a copy of this ABI for each deployment - const functionSignature = abiEncoder.getSignature(); - _.each(deploymentInfos, deploymentInfo => { - this._functionInfoBySelector[functionSelector].push({ - functionSignature, - abiEncoder, - contractName, - contractAddress: deploymentInfo.contractAddress, - networkId: deploymentInfo.networkId, - }); - }); - // There is no deployment info for this contract; record it without an address/network id - if (_.isEmpty(deploymentInfos)) { - this._functionInfoBySelector[functionSelector].push({ - functionSignature, - abiEncoder, - contractName, - }); - } - }); - } - /** - * Decodes transaction data for a known ABI. - * @param txData hex-encoded transaction data. - * @param txProperties Properties about the transaction used to disambiguate similar ABI's (optional). - * @return Decoded transaction data. Includes: function name and signature, along with the decoded arguments. - */ - public decode(txData: string, txProperties_?: TransactionProperties): TransactionData { - // Lookup - const functionSelector = TransactionDecoder._getFunctionSelector(txData); - const txProperties = _.isUndefined(txProperties_) ? {} : txProperties_; - const candidateFunctionInfos = this._functionInfoBySelector[functionSelector]; - if (_.isUndefined(candidateFunctionInfos)) { - throw new Error(`No functions registered for selector '${functionSelector}'`); - } - const functionInfo = _.find(candidateFunctionInfos, txDecoder => { - return ( - (_.isUndefined(txProperties.contractName) || - _.toLower(txDecoder.contractName) === _.toLower(txProperties.contractName)) && - (_.isUndefined(txProperties.contractAddress) || - txDecoder.contractAddress === txProperties.contractAddress) && - (_.isUndefined(txProperties.networkId) || txDecoder.networkId === txProperties.networkId) - ); - }); - if (_.isUndefined(functionInfo)) { - throw new Error(`No function registered with properties: ${JSON.stringify(txProperties)}.`); - } else if (_.isUndefined(functionInfo.abiEncoder)) { - throw new Error( - `Function ABI Encoder is not defined, for function with properties: ${JSON.stringify(txProperties)}.`, - ); - } - const functionName = functionInfo.abiEncoder.getDataItem().name; - const functionSignature = functionInfo.abiEncoder.getSignatureType(); - const functionArguments = functionInfo.abiEncoder.decode(txData); - const decodedCalldata = { - functionName, - functionSignature, - functionArguments, - }; - return decodedCalldata; - } -} diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index 2510a9ec2..cd7a13d53 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -17,18 +17,3 @@ export interface TransactionData { functionSignature: string; functionArguments: any; } - -export interface TransactionProperties { - contractName?: string; - contractAddress?: string; - networkId?: number; -} - -export interface DeployedContractInfo { - contractAddress: string; - networkId: number; -} - -export interface DeployedContractInfoByName { - [index: string]: DeployedContractInfo[]; -} diff --git a/packages/utils/test/transaction_decoder_test.ts b/packages/utils/test/abi_decoder_test.ts index 725ad5032..346bfdfd3 100644 --- a/packages/utils/test/transaction_decoder_test.ts +++ b/packages/utils/test/abi_decoder_test.ts @@ -2,22 +2,22 @@ import * as chai from 'chai'; import { MethodAbi } from 'ethereum-types'; import 'mocha'; -import { AbiEncoder, TransactionDecoder } from '../src'; +import { AbiEncoder, AbiDecoder } from '../src'; import { chaiSetup } from './utils/chai_setup'; chaiSetup.configure(); const expect = chai.expect; -describe('TransactionDecoder', () => { - it('should successfully add a new ABI and decode tx data for it', async () => { +describe('AbiDecoder', () => { + it('should successfully add a new ABI and decode calldata for it', async () => { // Add new ABI const abi: MethodAbi = { name: 'foobar', type: 'function', inputs: [ { - name: 'addr', + name: 'testAddress', type: 'address', }, ], @@ -32,31 +32,19 @@ describe('TransactionDecoder', () => { stateMutability: 'pure', }; const contractName = 'newContract'; - const contractAddress = '0x0001020304050607080900010203040506070809'; - const networkId = 1; - const contractInfo = [ - { - contractAddress, - networkId, - }, - ]; - const transactionDecoder = new TransactionDecoder(); - transactionDecoder.addABI([abi], contractName, contractInfo); + const testAddress = '0x0001020304050607080900010203040506070809'; + const abiDecoder = new AbiDecoder([]); + abiDecoder.addABI([abi], contractName); // Create some tx data const foobarEncoder = new AbiEncoder.Method(abi); const foobarSignature = foobarEncoder.getSignature(); - const foobarTxData = foobarEncoder.encode([contractAddress]); + const foobarTxData = foobarEncoder.encode([testAddress]); // Decode tx data using contract name - const decodedTxData = transactionDecoder.decode(foobarTxData, { contractName }); + const decodedTxData = abiDecoder.tryDecodeCalldata(foobarTxData, contractName); const expectedFunctionName = abi.name; - const expectedFunctionArguments = { - addr: contractAddress, - }; + const expectedFunctionArguments = {testAddress}; expect(decodedTxData.functionName).to.be.equal(expectedFunctionName); expect(decodedTxData.functionSignature).to.be.equal(foobarSignature); expect(decodedTxData.functionArguments).to.be.deep.equal(expectedFunctionArguments); - // Decode tx data using contract address - const decodedTxDataDecodedWithAddress = transactionDecoder.decode(foobarTxData, { contractAddress }); - expect(decodedTxDataDecodedWithAddress).to.be.deep.equal(decodedTxData); }); }); |