From 100840b74359e00bda90146f87ddeafd62db06e3 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 29 Jan 2019 17:18:33 -0800 Subject: Utility function to decode arbitrary 0x calldata --- packages/utils/package.json | 2 +- packages/utils/src/calldata_decoder.ts | 66 ++++++++++++++++++++++++++++ packages/utils/src/index.ts | 1 + packages/utils/test/calldata_decoder_test.ts | 21 +++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 packages/utils/src/calldata_decoder.ts create mode 100644 packages/utils/test/calldata_decoder_test.ts (limited to 'packages/utils') diff --git a/packages/utils/package.json b/packages/utils/package.json index 86fecbc7c..895560961 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -14,7 +14,7 @@ "lint": "tslint --format stylish --project .", "test": "yarn run_mocha", "test:circleci": "yarn test:coverage", - "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", + "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*_test.js' 'lib/test/*_test.js' --bail --exit", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info" }, diff --git a/packages/utils/src/calldata_decoder.ts b/packages/utils/src/calldata_decoder.ts new file mode 100644 index 000000000..780332514 --- /dev/null +++ b/packages/utils/src/calldata_decoder.ts @@ -0,0 +1,66 @@ +// Decodes any 0x transaction + +import * as ContractArtifacts from '@0x/contract-artifacts'; +import { SimpleContractArtifact } from '@0x/types'; +import { ContractAbi, MethodAbi } from 'ethereum-types'; +import * as _ from 'lodash'; +import { AbiEncoder } from '.'; + +export interface DecodedCalldata { + functionName: string; + functionSignature: string; + functionArguments: any; +} + +interface AbiEncoderBySelector { + [index: string]: AbiEncoder.Method; +} + +export class CalldataDecoder { + private readonly _abiEncoderBySelector: AbiEncoderBySelector = {}; + private static _instance: CalldataDecoder; + + public static getInstance(): CalldataDecoder { + if (!CalldataDecoder._instance) { + CalldataDecoder._instance = new CalldataDecoder(); + } + return CalldataDecoder._instance; + } + + public constructor() { + _.each(ContractArtifacts, (contractArtifactAsJson: any) => { + const conractArtifact = contractArtifactAsJson as SimpleContractArtifact; + const contractAbi: ContractAbi = conractArtifact.compilerOutput.abi; + const functionAbis = _.filter(contractAbi, (abiEntry) => {return abiEntry.type === 'function'}) as MethodAbi[]; + _.each(functionAbis, (functionAbi) => { + const abiEncoder = new AbiEncoder.Method(functionAbi); + const functionSelector = abiEncoder.getSelector(); + if (_.has(this._abiEncoderBySelector, functionSelector)) { + return; + } + this._abiEncoderBySelector[functionSelector] = abiEncoder; + }); + }); + } + + public static decode(calldata: string, rules?: AbiEncoder.DecodingRules): DecodedCalldata { + if (!calldata.startsWith('0x') || calldata.length < 10) { + throw new Error(`Malformed calldata. Must include hex prefix '0x' and 4-byte function selector. Got '${calldata}'`); + } + const functionSelector = calldata.substr(0, 10); + const instance = CalldataDecoder.getInstance(); + const abiEncoder = instance._abiEncoderBySelector[functionSelector]; + if (_.isUndefined(abiEncoder)) { + throw new Error(`Could not find matching abi encoder for selector '${functionSelector}'`); + } + const functionName = abiEncoder.getDataItem().name; + const functionSignature = abiEncoder.getSignatureType(); + const functionArguments = abiEncoder.decode(calldata, rules); + const decodedCalldata = { + functionName, + functionSignature, + functionArguments + } + return decodedCalldata; + } +} \ No newline at end of file diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 082aff6bb..3e5058edc 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -11,3 +11,4 @@ export { errorUtils } from './error_utils'; export { fetchAsync } from './fetch_async'; export { signTypedDataUtils } from './sign_typed_data_utils'; export import AbiEncoder = require('./abi_encoder'); +export { CalldataDecoder } from './calldata_decoder'; diff --git a/packages/utils/test/calldata_decoder_test.ts b/packages/utils/test/calldata_decoder_test.ts new file mode 100644 index 000000000..a3ff02a59 --- /dev/null +++ b/packages/utils/test/calldata_decoder_test.ts @@ -0,0 +1,21 @@ +import * as chai from 'chai'; +import 'mocha'; + +import { CalldataDecoder } from '../src'; +import { chaiSetup } from './utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe.only('CalldataDecoder', () => { + describe.only('decodeCalldata', () => { + it.only('should successfull decode fillOrder calldata', async () => { + //const cancelCalldata = '0xd46b02c3000000000000000000000000000000000000000000000000000000000000002000000000000000000000000056178a0d5f301baf6cf3e1cd53d9863437345bf90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a258b39954cef5cb142fd567a46cddb31a6701240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000071d75ab9b9204fffc40000000000000000000000000000000000000000000000011c6e19c53d35b66200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c50f2ed000000000000000000000000000000000000000000000000000001689c2bc812000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000'; + //const marketBuycalldata = '0xe5fa431b0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000012309ce5400000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000008c26348f63f9e008f0dd09a0ce1ed7caf6c1366b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e150a33ffa97a8d22f59c77ae5487b089ef62e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000001323e717ba3800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006ea9bd19a0c4b5533ac98f58db0558a96e15ec5f71d64b6070cea4b5df10b7fb35424035000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000006cb262679c522c4f0834041a6248e8feb35f0337000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000421c750cedbf0eef0914c09b296f08462c363527f454bcf2dfaaf2f772e290d0ee5b0417d8b95837cbe501494195edc2a5a48c664d2ef74a340e40213c05db8767fa03000000000000000000000000000000000000000000000000000000000000'; + const calldata = '0x3c28d861000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000500000000000000000000000000da912ecc847b3d98ca882e396e693e485deed5180000000000000000000000000681e844593a051e2882ec897ecd5444efe19ff20000000000000000000000008124071f810d533ff63de61d0c98db99eeb99d640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008bb6a7394e2f000000000000000000000000000000000000000000000000868cab59cce788000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c51035008197e43b4d84439ec534b62670eaaaf4a46f50ff37ff62f6d1c1fbe8b036d3c000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000503f9794d6a6bb0df8fbb19a2b3e2aeab35339ad000000000000000000000000000000000000000000000000000000000000000000000000000000003997d0f55d1daa549e95c240bc6353636f4cf9740000000000000000000000000681e844593a051e2882ec897ecd5444efe19ff20000000000000000000000008124071f810d533ff63de61d0c98db99eeb99d6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000871bcc4c32c9d66800000000000000000000000000000000000000000000000000008a70a4d2d2100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c510350c20e53540c9b2c9207ad9a04e472e2224af211f08efc2f0eec15d7e1cfbf2109000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000421c8f294b2728c269a9d01a1b58fe7cae2ef7895bd2de48cc3101eb47464d96594340924793fc8325db26a3abd5602605806a82ca77e810494c5ecab58b03449de80300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000421c372d6daa8e6ce2c696e51b6e1e33f10fd2b41b403cd88c311a617c3656ea02fe454e51cddf4682751bea9a02ce725cf364d1107f27be427d5157adbdcca2609b03000000000000000000000000000000000000000000000000000000000000'; + const decodedCalldata = CalldataDecoder.decode(calldata); + console.log(JSON.stringify(decodedCalldata, null, 4)); + expect(5).to.be.equal(5); + }); + }); +}); -- cgit v1.2.3