added fill logic for otcOrders in AS, as well as MultiplexBatchFill for otc

This commit is contained in:
Noah Khamliche
2022-07-14 15:43:07 -04:00
parent f466f8310f
commit 4f57736c82
3 changed files with 69 additions and 61 deletions

View File

@@ -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) {

View File

@@ -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' },

View File

@@ -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;