From 6406126ae356a6dd1102cf603d4ce00333c9fd62 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 6 Feb 2019 23:47:40 -0800 Subject: Merged tx decoder into AbiDecoder in utils and merged zeroex tx decoder into ContractWrappers. --- .../contract-wrappers/src/contract_wrappers.ts | 12 +- packages/contract-wrappers/src/index.ts | 7 - .../src/utils/zeroex_transaction_decoder.ts | 88 ---------- .../test/calldata_decoder_test.ts | 123 +++++++++++++ .../test/zeroex_transaction_decoder_test.ts | 190 --------------------- 5 files changed, 134 insertions(+), 286 deletions(-) delete mode 100644 packages/contract-wrappers/src/utils/zeroex_transaction_decoder.ts create mode 100644 packages/contract-wrappers/test/calldata_decoder_test.ts delete mode 100644 packages/contract-wrappers/test/zeroex_transaction_decoder_test.ts (limited to 'packages/contract-wrappers') 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, @@ -72,12 +71,6 @@ export { AssetProxyId, } from '@0x/types'; -export { - DeployedContractInfo, - TransactionData, - TransactionProperties -} from '@0x/utils'; - export { BlockParamLiteral, BlockParam, 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/calldata_decoder_test.ts b/packages/contract-wrappers/test/calldata_decoder_test.ts new file mode 100644 index 000000000..d44e13a89 --- /dev/null +++ b/packages/contract-wrappers/test/calldata_decoder_test.ts @@ -0,0 +1,123 @@ +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 { addressUtils, BigNumber } from '@0x/utils'; +import * as chai from 'chai'; +import * as _ from 'lodash'; +import 'mocha'; + +import { ContractAddresses, ContractWrappers } from '../src'; + +import { chaiSetup } from './utils/chai_setup'; +import { migrateOnceAsync } from './utils/migrate'; +import { provider, web3Wrapper } from './utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; + +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +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)'; + let signedOrderLeft: SignedOrder; + let signedOrderRight: SignedOrder; + let orderLeft = {}; + let orderRight = {}; + let matchOrdersTxData: string; + let contractAddresses: ContractAddresses; + let contractWrappers: ContractWrappers; + + before(async () => { + // Create accounts + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + const [makerAddressLeft, makerAddressRight] = accounts.slice(0, 2); + const exchangeAddress = addressUtils.generatePseudoRandomAddress(); + const feeRecipientAddress = addressUtils.generatePseudoRandomAddress(); + const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)]; + const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)]; + // Create orders to match + orderLeft = { + makerAddress: makerAddressLeft, + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + makerAssetAmount: new BigNumber(10), + takerAddress: '0x0000000000000000000000000000000000000000', + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + takerAssetAmount: new BigNumber(1), + feeRecipientAddress, + makerFee: new BigNumber(0), + takerFee: new BigNumber(0), + senderAddress: '0x0000000000000000000000000000000000000000', + expirationTimeSeconds: new BigNumber(1549498915), + salt: new BigNumber(217), + }; + orderRight = { + makerAddress: makerAddressRight, + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + makerAssetAmount: new BigNumber(1), + takerAddress: '0x0000000000000000000000000000000000000000', + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + takerAssetAmount: new BigNumber(8), + feeRecipientAddress, + makerFee: new BigNumber(0), + takerFee: new BigNumber(0), + senderAddress: '0x0000000000000000000000000000000000000000', + expirationTimeSeconds: new BigNumber(1549498915), + salt: new BigNumber(50010), + }; + const orderFactoryLeft = new OrderFactory(privateKeyLeft, orderLeft); + signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ exchangeAddress }); + const orderFactoryRight = new OrderFactory(privateKeyRight, orderRight); + signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ exchangeAddress }); + // Encode match orders transaction + contractAddresses = await migrateOnceAsync(); + await blockchainLifecycle.startAsync(); + const config = { + networkId: constants.TESTRPC_NETWORK_ID, + contractAddresses, + blockPollingIntervalMs: 10, + }; + contractWrappers = new ContractWrappers(provider, config); + const transactionEncoder = await contractWrappers.exchange.transactionEncoderAsync(); + matchOrdersTxData = transactionEncoder.matchOrdersTx(signedOrderLeft, signedOrderRight); + }); + + describe('decode', () => { + it('should successfully decode DutchAuction.matchOrders calldata', async () => { + const contractName = 'DutchAuction'; + const decodedTxData = contractWrappers.getAbiDecoder().tryDecodeCalldata(matchOrdersTxData, contractName); + const expectedFunctionName = 'matchOrders'; + const expectedFunctionArguments = { + buyOrder: orderLeft, + sellOrder: orderRight, + buySignature: signedOrderLeft.signature, + sellSignature: 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 successfully decode Exchange.matchOrders calldata (and distinguish from DutchAuction.matchOrders)', async () => { + const contractName = 'Exchange'; + const decodedTxData = contractWrappers.getAbiDecoder().tryDecodeCalldata(matchOrdersTxData, contractName); + 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 calldata', async () => { + const badTxData = '0x01020304'; + expect(() => { + contractWrappers.getAbiDecoder().tryDecodeCalldata(badTxData); + }).to.throw("No functions registered for selector '0x01020304'"); + }); + }); +}); diff --git a/packages/contract-wrappers/test/zeroex_transaction_decoder_test.ts b/packages/contract-wrappers/test/zeroex_transaction_decoder_test.ts deleted file mode 100644 index 83c810c3a..000000000 --- a/packages/contract-wrappers/test/zeroex_transaction_decoder_test.ts +++ /dev/null @@ -1,190 +0,0 @@ -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 * 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'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; - -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe.only('ZeroExTransactionDecoder', () => { - 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)'; - let signedOrderLeft: SignedOrder; - let signedOrderRight: SignedOrder; - let orderLeft = {}; - let orderRight = {}; - let matchOrdersTxData: string; - let contractAddresses: ContractAddresses; - - before(async () => { - // Create accounts - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const [makerAddressLeft, makerAddressRight] = accounts.slice(0, 2); - const exchangeAddress = addressUtils.generatePseudoRandomAddress(); - const feeRecipientAddress = addressUtils.generatePseudoRandomAddress(); - const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)]; - const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)]; - // Create orders to match - orderLeft = { - makerAddress: makerAddressLeft, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: new BigNumber(10), - takerAddress: '0x0000000000000000000000000000000000000000', - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - takerAssetAmount: new BigNumber(1), - feeRecipientAddress, - makerFee: new BigNumber(0), - takerFee: new BigNumber(0), - senderAddress: '0x0000000000000000000000000000000000000000', - expirationTimeSeconds: new BigNumber(1549498915), - salt: new BigNumber(217), - }; - orderRight = { - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: new BigNumber(1), - takerAddress: '0x0000000000000000000000000000000000000000', - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - takerAssetAmount: new BigNumber(8), - feeRecipientAddress, - makerFee: new BigNumber(0), - takerFee: new BigNumber(0), - senderAddress: '0x0000000000000000000000000000000000000000', - expirationTimeSeconds: new BigNumber(1549498915), - salt: new BigNumber(50010), - }; - const orderFactoryLeft = new OrderFactory(privateKeyLeft, orderLeft); - signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ exchangeAddress }); - const orderFactoryRight = new OrderFactory(privateKeyRight, orderRight); - signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ exchangeAddress }); - // Encode match orders transaction - contractAddresses = await migrateOnceAsync(); - await blockchainLifecycle.startAsync(); - const config = { - networkId: constants.TESTRPC_NETWORK_ID, - contractAddresses, - blockPollingIntervalMs: 10, - }; - const 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 () => { - const contractName = 'DutchAuction'; - const decodedTxData = ZeroExTransactionDecoder.decode(matchOrdersTxData, { contractName }); - const expectedFunctionName = 'matchOrders'; - const expectedFunctionArguments = { - buyOrder: orderLeft, - sellOrder: orderRight, - buySignature: signedOrderLeft.signature, - sellSignature: 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 successfully decode Exchange.matchOrders txData (and distinguish from DutchAuction.matchOrders)', async () => { - const contractName = 'Exchange'; - const decodedTxData = ZeroExTransactionDecoder.decode(matchOrdersTxData, { contractName }); - 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 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; - const badTxData = '0x01020304'; - expect(() => { - ZeroExTransactionDecoder.decode(badTxData, { contractAddress }); - }).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); - }); - }); -}); -- cgit