aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Hysen <greg.hysen@gmail.com>2019-02-07 15:47:40 +0800
committerGreg Hysen <greg.hysen@gmail.com>2019-02-09 08:25:30 +0800
commit6406126ae356a6dd1102cf603d4ce00333c9fd62 (patch)
tree390299a3cd99152ed17c3657a723978ab73ceaf2
parent6bde77bb57e1d0526154cc6b2b3bb0ff70c1d2b0 (diff)
downloaddexon-0x-contracts-6406126ae356a6dd1102cf603d4ce00333c9fd62.tar
dexon-0x-contracts-6406126ae356a6dd1102cf603d4ce00333c9fd62.tar.gz
dexon-0x-contracts-6406126ae356a6dd1102cf603d4ce00333c9fd62.tar.bz2
dexon-0x-contracts-6406126ae356a6dd1102cf603d4ce00333c9fd62.tar.lz
dexon-0x-contracts-6406126ae356a6dd1102cf603d4ce00333c9fd62.tar.xz
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.ts12
-rw-r--r--packages/contract-wrappers/src/index.ts7
-rw-r--r--packages/contract-wrappers/src/utils/zeroex_transaction_decoder.ts88
-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.json9
-rw-r--r--packages/utils/src/abi_decoder.ts121
-rw-r--r--packages/utils/src/index.ts1
-rw-r--r--packages/utils/src/transaction_decoder.ts112
-rw-r--r--packages/utils/src/types.ts15
-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);
});
});