diff options
Diffstat (limited to 'packages/contract-wrappers')
5 files changed, 129 insertions, 9 deletions
diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 8fa31052c..02e1acf91 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,10 +1,14 @@ [ { - "version": "1.1.0-rc.2", + "version": "1.0.1-rc.3", "changes": [ { "note": "Add ForwarderWrapper", "pr": 934 + }, + { + "note": "Optimize orders in ForwarderWrapper", + "pr": 936 } ] }, diff --git a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts index 90cfbe2af..13ef0fe01 100644 --- a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts @@ -10,6 +10,7 @@ import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema'; import { txOptsSchema } from '../schemas/tx_opts_schema'; import { TransactionOpts } from '../types'; import { assert } from '../utils/assert'; +import { calldataOptimizationUtils } from '../utils/calldata_optimization_utils'; import { constants } from '../utils/constants'; import { ContractWrapper } from './contract_wrapper'; @@ -75,14 +76,19 @@ export class ForwarderWrapper extends ContractWrapper { this.getZRXTokenAddress(), this.getEtherTokenAddress(), ); + // lowercase input addresses const normalizedTakerAddress = takerAddress.toLowerCase(); const normalizedFeeRecipientAddress = feeRecipientAddress.toLowerCase(); + // optimize orders + const optimizedMarketOrders = calldataOptimizationUtils.optimizeForwarderOrders(signedOrders); + const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(signedFeeOrders); + // send transaction const forwarderContractInstance = await this._getForwarderContractAsync(); const txHash = await forwarderContractInstance.marketSellOrdersWithEth.sendTransactionAsync( - signedOrders, - _.map(signedOrders, order => order.signature), - signedFeeOrders, - _.map(signedFeeOrders, order => order.signature), + optimizedMarketOrders, + _.map(optimizedMarketOrders, order => order.signature), + optimizedFeeOrders, + _.map(optimizedFeeOrders, order => order.signature), feePercentage, feeRecipientAddress, { @@ -138,15 +144,20 @@ export class ForwarderWrapper extends ContractWrapper { this.getZRXTokenAddress(), this.getEtherTokenAddress(), ); + // lowercase input addresses const normalizedTakerAddress = takerAddress.toLowerCase(); const normalizedFeeRecipientAddress = feeRecipientAddress.toLowerCase(); + // optimize orders + const optimizedMarketOrders = calldataOptimizationUtils.optimizeForwarderOrders(signedOrders); + const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(signedFeeOrders); + // send transaction const forwarderContractInstance = await this._getForwarderContractAsync(); const txHash = await forwarderContractInstance.marketBuyOrdersWithEth.sendTransactionAsync( - signedOrders, + optimizedMarketOrders, makerAssetFillAmount, - _.map(signedOrders, order => order.signature), - signedFeeOrders, - _.map(signedFeeOrders, order => order.signature), + _.map(optimizedMarketOrders, order => order.signature), + optimizedFeeOrders, + _.map(optimizedFeeOrders, order => order.signature), feePercentage, feeRecipientAddress, { diff --git a/packages/contract-wrappers/src/utils/calldata_optimization_utils.ts b/packages/contract-wrappers/src/utils/calldata_optimization_utils.ts new file mode 100644 index 000000000..3172cf531 --- /dev/null +++ b/packages/contract-wrappers/src/utils/calldata_optimization_utils.ts @@ -0,0 +1,44 @@ +import { SignedOrder } from '@0xproject/types'; +import * as _ from 'lodash'; + +import { constants } from './constants'; + +export const calldataOptimizationUtils = { + /** + * Takes an array of orders and outputs an array of equivalent orders where all takerAssetData are '0x' and + * all makerAssetData are '0x' except for that of the first order, which retains its original value + * @param orders An array of SignedOrder objects + * @returns optimized orders + */ + optimizeForwarderOrders(orders: SignedOrder[]): SignedOrder[] { + const optimizedOrders = _.map(orders, (order, index) => + transformOrder(order, { + makerAssetData: index === 0 ? order.makerAssetData : constants.NULL_BYTES, + takerAssetData: constants.NULL_BYTES, + }), + ); + return optimizedOrders; + }, + /** + * Takes an array of orders and outputs an array of equivalent orders where all takerAssetData are '0x' and + * all makerAssetData are '0x' + * @param orders An array of SignedOrder objects + * @returns optimized orders + */ + optimizeForwarderFeeOrders(orders: SignedOrder[]): SignedOrder[] { + const optimizedOrders = _.map(orders, (order, index) => + transformOrder(order, { + makerAssetData: constants.NULL_BYTES, + takerAssetData: constants.NULL_BYTES, + }), + ); + return optimizedOrders; + }, +}; + +const transformOrder = (order: SignedOrder, partialOrder: Partial<SignedOrder>) => { + return { + ...order, + ...partialOrder, + }; +}; diff --git a/packages/contract-wrappers/src/utils/constants.ts b/packages/contract-wrappers/src/utils/constants.ts index d436efefc..2df11538c 100644 --- a/packages/contract-wrappers/src/utils/constants.ts +++ b/packages/contract-wrappers/src/utils/constants.ts @@ -2,6 +2,7 @@ import { BigNumber } from '@0xproject/utils'; export const constants = { NULL_ADDRESS: '0x0000000000000000000000000000000000000000', + NULL_BYTES: '0x', TESTRPC_NETWORK_ID: 50, INVALID_JUMP_PATTERN: 'invalid JUMP at', REVERT: 'revert', diff --git a/packages/contract-wrappers/test/calldata_optimization_utils_test.ts b/packages/contract-wrappers/test/calldata_optimization_utils_test.ts new file mode 100644 index 000000000..a4cea772f --- /dev/null +++ b/packages/contract-wrappers/test/calldata_optimization_utils_test.ts @@ -0,0 +1,60 @@ +import { orderFactory } from '@0xproject/order-utils'; +import * as chai from 'chai'; +import * as _ from 'lodash'; +import 'mocha'; + +import { assert } from '../src/utils/assert'; +import { calldataOptimizationUtils } from '../src/utils/calldata_optimization_utils'; +import { constants } from '../src/utils/constants'; + +import { chaiSetup } from './utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +// utility for generating a set of order objects with mostly NULL values +// except for a specified makerAssetData and takerAssetData +const FAKE_ORDERS_COUNT = 5; +const generateFakeOrders = (makerAssetData: string, takerAssetData: string) => + _.map(_.range(FAKE_ORDERS_COUNT), index => { + const order = orderFactory.createOrder( + constants.NULL_ADDRESS, + constants.ZERO_AMOUNT, + makerAssetData, + constants.ZERO_AMOUNT, + takerAssetData, + constants.NULL_ADDRESS, + ); + return { + ...order, + signature: 'dummy signature', + }; + }); + +describe('calldataOptimizationUtils', () => { + const fakeMakerAssetData = 'fakeMakerAssetData'; + const fakeTakerAssetData = 'fakeTakerAssetData'; + const orders = generateFakeOrders(fakeMakerAssetData, fakeTakerAssetData); + describe('#optimizeForwarderOrders', () => { + it('should make makerAssetData `0x` unless first order', () => { + const optimizedOrders = calldataOptimizationUtils.optimizeForwarderOrders(orders); + expect(optimizedOrders[0].makerAssetData).to.equal(fakeMakerAssetData); + const ordersWithoutHead = _.slice(optimizedOrders, 1); + _.forEach(ordersWithoutHead, order => expect(order.makerAssetData).to.equal(constants.NULL_BYTES)); + }); + it('should make all takerAssetData `0x`', () => { + const optimizedOrders = calldataOptimizationUtils.optimizeForwarderOrders(orders); + _.forEach(optimizedOrders, order => expect(order.takerAssetData).to.equal(constants.NULL_BYTES)); + }); + }); + describe('#optimizeForwarderFeeOrders', () => { + it('should make all makerAssetData `0x`', () => { + const optimizedOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(orders); + _.forEach(optimizedOrders, order => expect(order.makerAssetData).to.equal(constants.NULL_BYTES)); + }); + it('should make all takerAssetData `0x`', () => { + const optimizedOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(orders); + _.forEach(optimizedOrders, order => expect(order.takerAssetData).to.equal(constants.NULL_BYTES)); + }); + }); +}); |