diff options
Diffstat (limited to 'packages/contract-wrappers')
-rw-r--r-- | packages/contract-wrappers/CHANGELOG.json | 9 | ||||
-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/transaction_encoder.ts | 17 | ||||
-rw-r--r-- | packages/contract-wrappers/test/calldata_decoder_test.ts | 127 |
5 files changed, 171 insertions, 1 deletions
diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 73c8e6070..6e0d1ca4b 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "version": "7.1.0", + "changes": [ + { + "note": "Added calldata decoding to ContractWrappers", + "pr": 1569 + } + ] + }, + { "version": "7.0.2", "changes": [ { diff --git a/packages/contract-wrappers/src/contract_wrappers.ts b/packages/contract-wrappers/src/contract_wrappers.ts index 4e594593e..f43dc5d26 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 abi decoder instance currently used by contract-wrappers + * @return AbiDecoder 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 69bbe3c91..5fc400edf 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -38,6 +38,8 @@ export { DutchAuctionWrapper } from './contract_wrappers/dutch_auction_wrapper'; export { TransactionEncoder } from './utils/transaction_encoder'; +export { AbiDecoder, DecodedCalldata } from '@0x/utils'; + export { ContractWrappersError, ForwarderWrapperError, @@ -83,6 +85,11 @@ export { JSONRPCResponseError, AbiDefinition, LogWithDecodedArgs, + LogEntry, + DecodedLogEntry, + DecodedLogEntryEvent, + LogEntryEvent, + RawLog, FunctionAbi, EventAbi, EventParameter, diff --git a/packages/contract-wrappers/src/utils/transaction_encoder.ts b/packages/contract-wrappers/src/utils/transaction_encoder.ts index 307487a9b..0832ee73a 100644 --- a/packages/contract-wrappers/src/utils/transaction_encoder.ts +++ b/packages/contract-wrappers/src/utils/transaction_encoder.ts @@ -242,6 +242,23 @@ export class TransactionEncoder { return abiEncodedData; } /** + * Encodes a matchOrders transaction. + * @param leftOrder First order to match. + * @param rightOrder Second order to match. + * @return Hex encoded abi of the function call. + */ + public matchOrdersTx(leftOrder: SignedOrder, rightOrder: SignedOrder): string { + assert.doesConformToSchema('leftOrder', leftOrder, schemas.orderSchema); + assert.doesConformToSchema('rightOrder', rightOrder, schemas.orderSchema); + const abiEncodedData = this._getExchangeContract().matchOrders.getABIEncodedTransactionData( + leftOrder, + rightOrder, + leftOrder.signature, + rightOrder.signature, + ); + return abiEncodedData; + } + /** * Encodes a preSign transaction. * @param hash Hash to pre-sign * @param signerAddress Address that should have signed the given hash. 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..ba1539ef5 --- /dev/null +++ b/packages/contract-wrappers/test/calldata_decoder_test.ts @@ -0,0 +1,127 @@ +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; + const [privateKeyLeft, privateKeyRight] = constants.TESTRPC_PRIVATE_KEYS; + const exchangeAddress = addressUtils.generatePseudoRandomAddress(); + const feeRecipientAddress = addressUtils.generatePseudoRandomAddress(); + // Create orders to match. + // Values are arbitrary, with the exception of maker addresses (generated above). + 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() + .decodeCalldataOrThrow(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() + .decodeCalldataOrThrow(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().decodeCalldataOrThrow(badTxData); + }).to.throw("No functions registered for selector '0x01020304'"); + }); + }); +}); |