added fill logic for otcOrders in AS, as well as MultiplexBatchFill for otc
This commit is contained in:
		@@ -50,6 +50,7 @@ import {
 | 
			
		||||
} from '../utils/market_operation_utils/types';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    multiplexOtcOrder,
 | 
			
		||||
    multiplexPlpEncoder,
 | 
			
		||||
    multiplexRfqEncoder,
 | 
			
		||||
    MultiplexSubcall,
 | 
			
		||||
@@ -341,18 +342,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
 | 
			
		||||
            !requiresTransformERC20(optsWithDefaults)
 | 
			
		||||
        ) {
 | 
			
		||||
            const rfqOrdersData = quote.orders.map(o => o.fillData as NativeRfqOrderFillData);
 | 
			
		||||
            const fillAmountPerOrder = (() => {
 | 
			
		||||
                // Don't think order taker amounts are clipped to actual sell amount
 | 
			
		||||
                // (the last one might be too large) so figure them out manually.
 | 
			
		||||
                let remaining = sellAmount;
 | 
			
		||||
                const fillAmounts = [];
 | 
			
		||||
                for (const o of quote.orders) {
 | 
			
		||||
                    const fillAmount = BigNumber.min(o.takerAmount, remaining);
 | 
			
		||||
                    fillAmounts.push(fillAmount);
 | 
			
		||||
                    remaining = remaining.minus(fillAmount);
 | 
			
		||||
                }
 | 
			
		||||
                return fillAmounts;
 | 
			
		||||
            })();
 | 
			
		||||
            const fillAmountPerOrder =  generateFillAmounts(sellAmount, quote);
 | 
			
		||||
            const callData =
 | 
			
		||||
                quote.orders.length === 1
 | 
			
		||||
                    ? this._exchangeProxy
 | 
			
		||||
@@ -376,53 +366,44 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //OTC orders
 | 
			
		||||
 | 
			
		||||
        if(
 | 
			
		||||
            [ChainId.Mainnet, ChainId.Polygon].includes(this.chainId) &&
 | 
			
		||||
            !isToETH &&
 | 
			
		||||
            !isFromETH &&
 | 
			
		||||
            quote.orders.every(o => o.type === FillQuoteTransformerOrderType.Otc) &&
 | 
			
		||||
            !requiresTransformERC20(optsWithDefaults)
 | 
			
		||||
        ){
 | 
			
		||||
            const otcOrdersData = quote.orders.map(o => o.fillData as NativeOtcOrderFillData);
 | 
			
		||||
            const fillAmountPerOrder = (() => {
 | 
			
		||||
                // Don't think order taker amounts are clipped to actual sell amount
 | 
			
		||||
                // (the last one might be too large) so figure them out manually.
 | 
			
		||||
                let remaining = sellAmount;
 | 
			
		||||
                const fillAmounts = [];
 | 
			
		||||
                for (const o of quote.orders) {
 | 
			
		||||
                    const fillAmount = BigNumber.min(o.takerAmount, remaining);
 | 
			
		||||
                    fillAmounts.push(fillAmount);
 | 
			
		||||
                    remaining = remaining.minus(fillAmount);
 | 
			
		||||
                }
 | 
			
		||||
                return fillAmounts;
 | 
			
		||||
            })();
 | 
			
		||||
 | 
			
		||||
            const fillAmountPerOrder = generateFillAmounts(sellAmount, quote);
 | 
			
		||||
            // grab the amount to fill on each OtcOrder (if more than 1)
 | 
			
		||||
 | 
			
		||||
            let callData;
 | 
			
		||||
 | 
			
		||||
            if(isFromETH){
 | 
			
		||||
                callData = this._exchangeProxy.fillOtcOrderWithEth(
 | 
			
		||||
                    otcOrdersData[0].order, otcOrdersData[0].signature
 | 
			
		||||
                ).getABIEncodedTransactionData();
 | 
			
		||||
            
 | 
			
		||||
            if(quote.orders.length === 1){
 | 
			
		||||
                if(isFromETH){
 | 
			
		||||
                    callData = this._exchangeProxy.fillOtcOrderWithEth(
 | 
			
		||||
                        otcOrdersData[0].order, otcOrdersData[0].signature
 | 
			
		||||
                    ).getABIEncodedTransactionData();
 | 
			
		||||
                }
 | 
			
		||||
                if(isToETH){
 | 
			
		||||
                    callData = this._exchangeProxy.fillOtcOrderForEth(
 | 
			
		||||
                        otcOrdersData[0].order, otcOrdersData[0].signature, fillAmountPerOrder[0]
 | 
			
		||||
                    ).getABIEncodedTransactionData();
 | 
			
		||||
                }
 | 
			
		||||
                else{
 | 
			
		||||
                    callData = this._exchangeProxy.fillOtcOrder(
 | 
			
		||||
                        otcOrdersData[0].order, otcOrdersData[0].signature, fillAmountPerOrder[0]
 | 
			
		||||
                    ).getABIEncodedTransactionData();
 | 
			
		||||
                }
 | 
			
		||||
                return {
 | 
			
		||||
                    calldataHexString: callData,
 | 
			
		||||
                    ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
 | 
			
		||||
                    toAddress: this._exchangeProxy.address,
 | 
			
		||||
                    allowanceTarget: this._exchangeProxy.address,
 | 
			
		||||
                    gasOverhead: ZERO_AMOUNT,
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
            if(isToETH){
 | 
			
		||||
                callData = this._exchangeProxy.fillOtcOrderForEth(
 | 
			
		||||
                    otcOrdersData[0].order, otcOrdersData[0].signature, fillAmountPerOrder[0]
 | 
			
		||||
                ).getABIEncodedTransactionData();
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                callData = this._exchangeProxy.fillOtcOrder(
 | 
			
		||||
                    otcOrdersData[0].order, otcOrdersData[0].signature, fillAmountPerOrder[0]
 | 
			
		||||
                ).getABIEncodedTransactionData();
 | 
			
		||||
            }
 | 
			
		||||
            return {
 | 
			
		||||
                calldataHexString: callData,
 | 
			
		||||
                ethAmount: ZERO_AMOUNT,
 | 
			
		||||
                toAddress: this._exchangeProxy.address,
 | 
			
		||||
                allowanceTarget: this._exchangeProxy.address,
 | 
			
		||||
                gasOverhead: ZERO_AMOUNT,
 | 
			
		||||
            };
 | 
			
		||||
            
 | 
			
		||||
            
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
 | 
			
		||||
@@ -618,19 +599,30 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
 | 
			
		||||
        for_loop: for (const [i, order] of quote.orders.entries()) {
 | 
			
		||||
            switch_statement: switch (order.source) {
 | 
			
		||||
                case ERC20BridgeSource.Native:
 | 
			
		||||
                    if (order.type !== FillQuoteTransformerOrderType.Rfq) {
 | 
			
		||||
                    if (order.type !== (FillQuoteTransformerOrderType.Rfq || FillQuoteTransformerOrderType.Otc)) {
 | 
			
		||||
                        // Should never happen because we check `isMultiplexBatchFillCompatible`
 | 
			
		||||
                        // before calling this function.
 | 
			
		||||
                        throw new Error('Multiplex batch fill only supported for RFQ native orders');
 | 
			
		||||
                        throw new Error('Multiplex batch fill only supported for RFQ native orders and OTC Orders');
 | 
			
		||||
                    }
 | 
			
		||||
                    if (order.type !== FillQuoteTransformerOrderType.Rfq){
 | 
			
		||||
                        subcalls.push({
 | 
			
		||||
                            id: MultiplexSubcall.Rfq,
 | 
			
		||||
                            sellAmount: order.takerAmount,
 | 
			
		||||
                            data: multiplexRfqEncoder.encode({
 | 
			
		||||
                                order: order.fillData.order,
 | 
			
		||||
                                signature: order.fillData.signature,
 | 
			
		||||
                            }),
 | 
			
		||||
                        });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        subcalls.push({
 | 
			
		||||
                            id: MultiplexSubcall.Otc,
 | 
			
		||||
                            sellAmount: order.takerAmount,
 | 
			
		||||
                            data: multiplexOtcOrder.encode({
 | 
			
		||||
                                order: order.fillData.order,
 | 
			
		||||
                                signature: order.fillData.signature,
 | 
			
		||||
                            }),
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                    subcalls.push({
 | 
			
		||||
                        id: MultiplexSubcall.Rfq,
 | 
			
		||||
                        sellAmount: order.takerAmount,
 | 
			
		||||
                        data: multiplexRfqEncoder.encode({
 | 
			
		||||
                            order: order.fillData.order,
 | 
			
		||||
                            signature: order.fillData.signature,
 | 
			
		||||
                        }),
 | 
			
		||||
                    });
 | 
			
		||||
                    break switch_statement;
 | 
			
		||||
                case ERC20BridgeSource.UniswapV2:
 | 
			
		||||
                case ERC20BridgeSource.SushiSwap:
 | 
			
		||||
@@ -781,6 +773,17 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateFillAmounts(sellAmount: BigNumber, quote: MarketBuySwapQuote | MarketSellSwapQuote): BigNumber[]{
 | 
			
		||||
        let remaining = sellAmount;
 | 
			
		||||
        const fillAmounts = [];
 | 
			
		||||
        for (const o of quote.orders) {
 | 
			
		||||
            const fillAmount = BigNumber.min(o.takerAmount, remaining);
 | 
			
		||||
            fillAmounts.push(fillAmount);
 | 
			
		||||
            remaining = remaining.minus(fillAmount);
 | 
			
		||||
        }
 | 
			
		||||
        return fillAmounts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function slipNonNativeOrders(quote: MarketSellSwapQuote | MarketBuySwapQuote): OptimizedMarketOrder[] {
 | 
			
		||||
    const slippage = getMaxQuoteSlippageRate(quote);
 | 
			
		||||
    if (slippage === 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { RfqOrder, SIGNATURE_ABI } from '@0x/protocol-utils';
 | 
			
		||||
import { OtcOrder, RfqOrder, SIGNATURE_ABI } from '@0x/protocol-utils';
 | 
			
		||||
import { AbiEncoder } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
export enum MultiplexSubcall {
 | 
			
		||||
@@ -26,6 +26,10 @@ export const multiplexRfqEncoder = AbiEncoder.create([
 | 
			
		||||
    { name: 'order', type: 'tuple', components: RfqOrder.STRUCT_ABI },
 | 
			
		||||
    { name: 'signature', type: 'tuple', components: SIGNATURE_ABI },
 | 
			
		||||
]);
 | 
			
		||||
export const multiplexOtcOrder = AbiEncoder.create([
 | 
			
		||||
    { name: 'order', type: 'tuple', components: OtcOrder.STRUCT_ABI },
 | 
			
		||||
    { name: 'signature', type: 'tuple', components: SIGNATURE_ABI },
 | 
			
		||||
]);
 | 
			
		||||
export const multiplexUniswapEncoder = AbiEncoder.create([
 | 
			
		||||
    { name: 'tokens', type: 'address[]' },
 | 
			
		||||
    { name: 'isSushi', type: 'bool' },
 | 
			
		||||
 
 | 
			
		||||
@@ -438,7 +438,8 @@ export interface OptimizedRfqOrder extends OptimizedMarketOrderBase<NativeRfqOrd
 | 
			
		||||
export type OptimizedMarketOrder =
 | 
			
		||||
    | OptimizedMarketBridgeOrder<FillData>
 | 
			
		||||
    | OptimizedMarketOrderBase<NativeLimitOrderFillData>
 | 
			
		||||
    | OptimizedMarketOrderBase<NativeRfqOrderFillData>;
 | 
			
		||||
    | OptimizedMarketOrderBase<NativeRfqOrderFillData>
 | 
			
		||||
    | OptimizedMarketOrderBase<NativeOtcOrderFillData>;
 | 
			
		||||
 | 
			
		||||
export interface GetMarketOrdersRfqOpts extends RfqRequestOpts {
 | 
			
		||||
    rfqClient?: IRfqClient;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user