From 64a0080616dd57ed9c1ce5b003fe28221f32b996 Mon Sep 17 00:00:00 2001 From: David Sun Date: Fri, 28 Jun 2019 13:32:51 -0700 Subject: [PATCH] add forwarder logic for market sell --- .../forwarder_swap_quote_consumer.ts | 147 +++++++++++++----- packages/asset-buyer/src/swap_quoter.ts | 31 ++-- packages/asset-buyer/src/types.ts | 14 ++ packages/asset-buyer/src/utils/assert.ts | 19 ++- .../src/utils/swap_quote_calculator.ts | 41 +++-- packages/asset-buyer/src/utils/utils.ts | 20 +++ packages/order-utils/src/market_utils.ts | 4 +- 7 files changed, 209 insertions(+), 67 deletions(-) diff --git a/packages/asset-buyer/src/quote_consumers/forwarder_swap_quote_consumer.ts b/packages/asset-buyer/src/quote_consumers/forwarder_swap_quote_consumer.ts index 713c0d4b9a..caad19fd31 100644 --- a/packages/asset-buyer/src/quote_consumers/forwarder_swap_quote_consumer.ts +++ b/packages/asset-buyer/src/quote_consumers/forwarder_swap_quote_consumer.ts @@ -8,8 +8,11 @@ import { constants } from '../constants'; import { CalldataInfo, ForwarderMarketBuySmartContractParams, + ForwarderMarketSellSmartContractParams, ForwarderSwapQuoteExecutionOpts, ForwarderSwapQuoteGetOutputOpts, + MarketBuySwapQuote, + MarketSellSwapQuote, SmartContractParamsInfo, SwapQuote, SwapQuoteConsumer, @@ -22,7 +25,8 @@ import { assetDataUtils } from '../utils/asset_data_utils'; import { swapQuoteConsumerUtils } from '../utils/swap_quote_consumer_utils'; import { utils } from '../utils/utils'; -export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer { +export class ForwarderSwapQuoteConsumer + implements SwapQuoteConsumer { public readonly provider: ZeroExProvider; public readonly networkId: number; @@ -51,17 +55,35 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer { assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow()); - const { params, to, ethAmount, methodAbi } = await this.getSmartContractParamsOrThrowAsync(quote, opts); + const consumableQuote = (quote as any) as (MarketBuySwapQuote | MarketSellSwapQuote); + const smartContractParamsInfo = await this.getSmartContractParamsOrThrowAsync(consumableQuote, opts); + const { to, methodAbi, ethAmount } = smartContractParamsInfo; + const abiEncoder = new AbiEncoder.Method(methodAbi); - const args = [ - params.orders, - params.makerAssetFillAmount, - params.signatures, - params.feeOrders, - params.feeSignatures, - params.feePercentage, - params.feeRecipient, - ]; + + let args: any[]; + if (utils.isSwapQuoteMarketBuy(consumableQuote)) { + const marketBuyParams = (smartContractParamsInfo.params as any) as ForwarderMarketBuySmartContractParams; + args = [ + marketBuyParams.orders, + marketBuyParams.makerAssetFillAmount, + marketBuyParams.signatures, + marketBuyParams.feeOrders, + marketBuyParams.feeSignatures, + marketBuyParams.feePercentage, + marketBuyParams.feeRecipient, + ]; + } else { + const marketSellParams = (smartContractParamsInfo.params as any) as ForwarderMarketSellSmartContractParams; + args = [ + marketSellParams.orders, + marketSellParams.signatures, + marketSellParams.feeOrders, + marketSellParams.feeSignatures, + marketSellParams.feePercentage, + marketSellParams.feeRecipient, + ]; + } const calldataHexString = abiEncoder.encode(args); return { calldataHexString, @@ -79,7 +101,9 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer, - ): Promise> { + ): Promise< + SmartContractParamsInfo + > { assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow()); const { ethAmount, feeRecipient, feePercentage: unFormattedFeePercentage } = _.merge( @@ -99,26 +123,51 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer o.signature); const feeSignatures = _.map(feeOrders, o => o.signature); const feePercentage = utils.numberPercentageToEtherTokenAmountPercentage(unFormattedFeePercentage); - const params: ForwarderMarketBuySmartContractParams = { - orders, - makerAssetFillAmount, - signatures, - feeOrders, - feeSignatures, - feePercentage, - feeRecipient, - }; + let params: ForwarderMarketBuySmartContractParams | ForwarderMarketSellSmartContractParams; + let methodName: string; + if (utils.isSwapQuoteMarketBuy(consumableQuoteWithAffiliateFee)) { + const { makerAssetFillAmount } = consumableQuoteWithAffiliateFee; + + params = { + orders, + makerAssetFillAmount, + signatures, + feeOrders, + feeSignatures, + feePercentage, + feeRecipient, + }; + + methodName = 'marketBuyOrdersWithEth'; + } else { + const { takerAssetFillAmount } = consumableQuoteWithAffiliateFee; + + params = { + orders, + takerAssetFillAmount, + signatures, + feeOrders, + feeSignatures, + feePercentage, + feeRecipient, + }; + methodName = 'marketSellOrdersWithEth'; + } const methodAbi = utils.getMethodAbiFromContractAbi( this._contractWrappers.forwarder.abi, - 'marketBuyOrdersWithEth', + methodName, ) as MethodAbi; return { @@ -163,25 +212,47 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer { - return orderCalculationUtils.getTakerFillAmount(orders[index], makerAssetAmount); - }); + const remainingFillableTakerAssetAmounts = remainingFillableMakerAssetAmounts.map( + (makerAssetAmount: BigNumber, index: number) => { + return orderCalculationUtils.getTakerFillAmount(orders[index], makerAssetAmount); + }, + ); const feeOrders = feeOrdersAndFillableAmounts.orders; const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts; const slippageBufferAmount = takerAssetFillAmount.multipliedBy(slippagePercentage).integerValue(); @@ -32,9 +41,12 @@ export const swapQuoteCalculator = { remainingFillableTakerAssetAmounts, slippageBufferAmount, }); - const ordersRemainingFillableMakerAssetAmounts = _.map(ordersRemainingFillableTakerAssetAmounts, (takerAssetAmount: BigNumber, index: number) => { - return orderCalculationUtils.getMakerFillAmount(resultOrders[index], takerAssetAmount); - }); + const ordersRemainingFillableMakerAssetAmounts = _.map( + ordersRemainingFillableTakerAssetAmounts, + (takerAssetAmount: BigNumber, index: number) => { + return orderCalculationUtils.getMakerFillAmount(resultOrders[index], takerAssetAmount); + }, + ); // if we do not have enough orders to cover the desired assetBuyAmount, throw if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { // We needed the amount they requested to buy, plus the amount for slippage @@ -260,7 +272,10 @@ function calculateMarketSellQuoteInfo( let makerTokenAmount = constants.ZERO_AMOUNT; let zrxTakerTokenAmount = constants.ZERO_AMOUNT; if (isMakerAssetZrxToken) { - makerTokenAmount = findZrxTokenAmountFromSellingTakerTokenAmount(ordersAndFillableAmounts, takerAssetSellAmount); + makerTokenAmount = findZrxTokenAmountFromSellingTakerTokenAmount( + ordersAndFillableAmounts, + takerAssetSellAmount, + ); } else { // find eth and zrx amounts needed to buy const takerTokenAndZrxAmountToBuyAsset = findMakerTokenAmountReceivedAndZrxAmountNeededToSellAsset( @@ -304,7 +319,10 @@ function findZrxTokenAmountFromSellingTakerTokenAmount( (acc, order, index) => { const { totalZrxTokenAmount, remainingTakerAssetFillAmount } = acc; const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; - const remainingFillableTakerAssetAmount = orderCalculationUtils.getTakerFillAmount(order, remainingFillableMakerAssetAmount); + const remainingFillableTakerAssetAmount = orderCalculationUtils.getTakerFillAmount( + order, + remainingFillableMakerAssetAmount, + ); const takerFillAmount = BigNumber.min(remainingTakerAssetFillAmount, remainingFillableTakerAssetAmount); const makerFillAmount = orderCalculationUtils.getMakerFillAmount(order, takerFillAmount); const feeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount); @@ -400,7 +418,10 @@ function findMakerTokenAmountReceivedAndZrxAmountNeededToSellAsset( (acc, order, index) => { const { totalMakerTokenAmount, totalZrxAmount, remainingTakerAssetFillAmount } = acc; const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; - const remainingFillableTakerAssetAmount = orderCalculationUtils.getTakerFillAmount(order, remainingFillableMakerAssetAmount); + const remainingFillableTakerAssetAmount = orderCalculationUtils.getTakerFillAmount( + order, + remainingFillableMakerAssetAmount, + ); const takerFillAmount = BigNumber.min(acc.remainingTakerAssetFillAmount, remainingFillableTakerAssetAmount); const makerFillAmount = orderCalculationUtils.getMakerFillAmount(order, takerFillAmount); const takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount); diff --git a/packages/asset-buyer/src/utils/utils.ts b/packages/asset-buyer/src/utils/utils.ts index b08bb0e152..a208a7b908 100644 --- a/packages/asset-buyer/src/utils/utils.ts +++ b/packages/asset-buyer/src/utils/utils.ts @@ -4,6 +4,14 @@ import { AbiDefinition, ContractAbi, MethodAbi } from 'ethereum-types'; import * as _ from 'lodash'; import { constants } from '../constants'; +import { + MarketBuySwapQuote, + MarketBuySwapQuoteInfo, + MarketSellSwapQuote, + MarketSellSwapQuoteInfo, + SwapQuote, + SwapQuoteInfo, +} from '../types'; // tslint:disable:no-unnecessary-type-assertion export const utils = { @@ -25,4 +33,16 @@ export const utils = { }, ) as MethodAbi | undefined; }, + isSwapQuoteMarketBuy(quote: SwapQuote): quote is MarketBuySwapQuote { + return (quote as MarketSellSwapQuote).takerAssetFillAmount !== undefined; + }, + isSwapQuoteMarketSell(quote: SwapQuote): quote is MarketSellSwapQuote { + return (quote as MarketBuySwapQuote).makerAssetFillAmount !== undefined; + }, + isSwapQuoteInfoMarketBuy(quote: SwapQuoteInfo): quote is MarketBuySwapQuoteInfo { + return (quote as MarketBuySwapQuoteInfo).takerTokenAmount !== undefined; + }, + isSwapQuoteInfoMarketSell(quote: SwapQuoteInfo): quote is MarketSellSwapQuoteInfo { + return (quote as MarketSellSwapQuoteInfo).makerTokenAmount !== undefined; + }, }; diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts index 2b71f524e7..4d02efe9ee 100644 --- a/packages/order-utils/src/market_utils.ts +++ b/packages/order-utils/src/market_utils.ts @@ -5,9 +5,7 @@ import * as _ from 'lodash'; import { assert } from './assert'; import { constants } from './constants'; -import { - orderCalculationUtils, -} from './order_calculation_utils'; +import { orderCalculationUtils } from './order_calculation_utils'; import { FeeOrdersAndRemainingFeeAmount, FindFeeOrdersThatCoverFeesForTargetOrdersOpts,