aboutsummaryrefslogtreecommitdiffstats
path: root/packages/utils/src
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 /packages/utils/src
parent6bde77bb57e1d0526154cc6b2b3bb0ff70c1d2b0 (diff)
downloaddexon-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.
Diffstat (limited to 'packages/utils/src')
-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
4 files changed, 105 insertions, 144 deletions
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[];
-}