From a016747c36e0bc2c182e3d2b565ca63fb95e2b1b Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 2 Aug 2018 10:55:09 -0700 Subject: Add findFeeOrdersThatCoverFeesForTargetOrders to ForwarderWrapper --- .../src/contract_wrappers/forwarder_wrapper.ts | 65 ++++++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts index 5e879b3a8..c0961b3a3 100644 --- a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts @@ -27,7 +27,7 @@ export class ForwarderWrapper extends ContractWrapper { /** * Takes an array of orders and returns a subset of those orders that has enough makerAssetAmount (taking into account on-chain balances, * allowances, and partial fills) in order to fill the input makerAssetFillAmount plus slippageBufferAmount. Iterates from first order to last. - * Sort the input by rate in order to get the subset of orders that will cost the least ETH. + * Sort the input by ascending rate in order to get the subset of orders that will cost the least ETH. * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders should specify the same makerAsset. * All orders should specify WETH as the takerAsset. * @param orderStates An array of objects corresponding to the signedOrders parameter that each contain on-chain state @@ -46,20 +46,20 @@ export class ForwarderWrapper extends ContractWrapper { assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); assert.isBigNumber('makerAssetFillAmount', makerAssetFillAmount); assert.isBigNumber('slippageBufferAmount', slippageBufferAmount); - // calculate total amount of makerAsset needed to fill + // calculate total amount of makerAsset needed to be filled const totalFillAmount = makerAssetFillAmount.plus(slippageBufferAmount); // iterate through the signedOrders input from left to right until we have enough makerAsset to fill totalFillAmount const result = _.reduce( signedOrders, ({ resultOrders, remainingFillAmount }, order, index) => { if (remainingFillAmount.lessThanOrEqualTo(constants.ZERO_AMOUNT)) { - return { resultOrders, remainingFillAmount }; + return { resultOrders, remainingFillAmount: constants.ZERO_AMOUNT }; } else { const orderState = orderStates[index]; - const orderRemainingFillableMakerAssetAmount = orderState.remainingFillableMakerAssetAmount; + const makerAssetAmountAvailable = ForwarderWrapper._getMakerAssetAmountAvailable(orderState); return { resultOrders: _.concat(resultOrders, order), - remainingFillAmount: remainingFillAmount.minus(orderState.remainingFillableMakerAssetAmount), + remainingFillAmount: remainingFillAmount.minus(makerAssetAmountAvailable), }; } }, @@ -67,6 +67,61 @@ export class ForwarderWrapper extends ContractWrapper { ); return result; } + /** + * Takes an array of orders and an array of feeOrders. Returns a subset of the feeOrders that has enough ZRX (taking into account + * on-chain balances, allowances, and partial fills) in order to fill the takerFees required by signedOrders plus a + * slippageBufferAmount. Iterates from first feeOrder to last. Sort the feeOrders by ascending rate in order to get the subset of + * feeOrders that will cost the least ETH. + * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @param orderStates An array of objects corresponding to the signedOrders parameter that each contain on-chain state + * relevant to that order. + * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @param feeOrderStates An array of objects corresponding to the signedOrders parameter that each contain on-chain state + * relevant to that order. + * @param makerAssetFillAmount The amount of makerAsset desired to be filled. + * @param slippageBufferAmount An additional amount makerAsset to be covered by the result in case of trade collisions or partial fills. + * @return Resulting orders and remaining fill amount that could not be covered by the input. + */ + public static findFeeOrdersThatCoverFeesForTargetOrders( + signedOrders: SignedOrder[], + orderStates: OrderRelevantState[], + signedFeeOrders: SignedOrder[], + feeOrderStates: OrderRelevantState[], + slippageBufferAmount: BigNumber = constants.ZERO_AMOUNT, + ): { resultOrders: SignedOrder[]; remainingFillAmount: BigNumber } { + // type assertions + assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); + assert.isBigNumber('slippageBufferAmount', slippageBufferAmount); + // calculate total amount of ZRX needed to fill signedOrders + const totalFeeAmount = _.reduce( + signedOrders, + (accFees, order, index) => { + const orderState = orderStates[index]; + const makerAssetAmountAvailable = ForwarderWrapper._getMakerAssetAmountAvailable(orderState); + const feeToFillMakerAssetAmountAvailable = makerAssetAmountAvailable + .div(order.makerAssetAmount) + .mul(order.takerFee); + return feeToFillMakerAssetAmountAvailable; + }, + constants.ZERO_AMOUNT, + ); + return ForwarderWrapper.findOrdersThatCoverMakerAssetFillAmount( + signedFeeOrders, + feeOrderStates, + totalFeeAmount, + slippageBufferAmount, + ); + } + private static _getMakerAssetAmountAvailable(orderState: OrderRelevantState): BigNumber { + return BigNumber.min( + orderState.makerBalance, + orderState.remainingFillableMakerAssetAmount, + orderState.makerProxyAllowance, + ); + } constructor( web3Wrapper: Web3Wrapper, networkId: number, -- cgit