feat: asset-swapper use ethToInputRate when ethToOutputRate is unavailable (#2660)

This commit is contained in:
Jacob Evans 2020-08-10 20:41:17 +10:00 committed by GitHub
parent 71cde281b9
commit d0e9081852
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 45 additions and 7 deletions

View File

@ -41,6 +41,10 @@
{ {
"note": "Fix depth buy scale", "note": "Fix depth buy scale",
"pr": 2659 "pr": 2659
},
{
"note": "Adjust fill by ethToInputRate when ethToOutputRate is 0",
"pr": 2660
} }
] ]
}, },

View File

@ -17,6 +17,7 @@ export function createFillPaths(opts: {
dexQuotes?: DexSample[][]; dexQuotes?: DexSample[][];
targetInput?: BigNumber; targetInput?: BigNumber;
ethToOutputRate?: BigNumber; ethToOutputRate?: BigNumber;
ethToInputRate?: BigNumber;
excludedSources?: ERC20BridgeSource[]; excludedSources?: ERC20BridgeSource[];
feeSchedule?: FeeSchedule; feeSchedule?: FeeSchedule;
}): Fill[][] { }): Fill[][] {
@ -26,8 +27,9 @@ export function createFillPaths(opts: {
const orders = opts.orders || []; const orders = opts.orders || [];
const dexQuotes = opts.dexQuotes || []; const dexQuotes = opts.dexQuotes || [];
const ethToOutputRate = opts.ethToOutputRate || ZERO_AMOUNT; const ethToOutputRate = opts.ethToOutputRate || ZERO_AMOUNT;
const ethToInputRate = opts.ethToInputRate || ZERO_AMOUNT;
// Create native fill paths. // Create native fill paths.
const nativePath = nativeOrdersToPath(side, orders, opts.targetInput, ethToOutputRate, feeSchedule); const nativePath = nativeOrdersToPath(side, orders, opts.targetInput, ethToOutputRate, ethToInputRate, feeSchedule);
// Create DEX fill paths. // Create DEX fill paths.
const dexPaths = dexQuotesToPaths(side, dexQuotes, ethToOutputRate, feeSchedule); const dexPaths = dexQuotesToPaths(side, dexQuotes, ethToOutputRate, feeSchedule);
return filterPaths([...dexPaths, nativePath].map(p => clipPathToInput(p, opts.targetInput)), excludedSources); return filterPaths([...dexPaths, nativePath].map(p => clipPathToInput(p, opts.targetInput)), excludedSources);
@ -54,6 +56,7 @@ function nativeOrdersToPath(
orders: SignedOrderWithFillableAmounts[], orders: SignedOrderWithFillableAmounts[],
targetInput: BigNumber = POSITIVE_INF, targetInput: BigNumber = POSITIVE_INF,
ethToOutputRate: BigNumber, ethToOutputRate: BigNumber,
ethToInputRate: BigNumber,
fees: FeeSchedule, fees: FeeSchedule,
): Fill[] { ): Fill[] {
const sourcePathId = hexUtils.random(); const sourcePathId = hexUtils.random();
@ -64,9 +67,10 @@ function nativeOrdersToPath(
const takerAmount = fillableAmountsUtils.getTakerAssetAmountSwappedAfterOrderFees(order); const takerAmount = fillableAmountsUtils.getTakerAssetAmountSwappedAfterOrderFees(order);
const input = side === MarketOperation.Sell ? takerAmount : makerAmount; const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
const output = side === MarketOperation.Sell ? makerAmount : takerAmount; const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
const penalty = ethToOutputRate.times( const fee = fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!();
fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!(), const outputPenalty = !ethToOutputRate.isZero()
); ? ethToOutputRate.times(fee)
: ethToInputRate.times(fee).times(output.dividedToIntegerBy(input));
// targetInput can be less than the order size // targetInput can be less than the order size
// whilst the penalty is constant, it affects the adjusted output // whilst the penalty is constant, it affects the adjusted output
// only up until the target has been exhausted. // only up until the target has been exhausted.
@ -76,7 +80,7 @@ function nativeOrdersToPath(
// scale the clipped output inline with the input // scale the clipped output inline with the input
const clippedOutput = clippedInput.dividedBy(input).times(output); const clippedOutput = clippedInput.dividedBy(input).times(output);
const adjustedOutput = const adjustedOutput =
side === MarketOperation.Sell ? clippedOutput.minus(penalty) : clippedOutput.plus(penalty); side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty);
const adjustedRate = const adjustedRate =
side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput); side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput);
// Skip orders with rates that are <= 0. // Skip orders with rates that are <= 0.

View File

@ -1,4 +1,5 @@
import { ContractAddresses } from '@0x/contract-addresses'; import { ContractAddresses } from '@0x/contract-addresses';
import { ZERO_AMOUNT } from '@0x/order-utils';
import { RFQTIndicativeQuote } from '@0x/quote-server'; import { RFQTIndicativeQuote } from '@0x/quote-server';
import { SignedOrder } from '@0x/types'; import { SignedOrder } from '@0x/types';
import { BigNumber, NULL_ADDRESS } from '@0x/utils'; import { BigNumber, NULL_ADDRESS } from '@0x/utils';
@ -114,6 +115,17 @@ export class MarketOperationUtils {
this._liquidityProviderRegistry, this._liquidityProviderRegistry,
this._multiBridge, this._multiBridge,
), ),
// Get ETH -> taker token price.
await DexOrderSampler.ops.getMedianSellRateAsync(
difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources),
takerToken,
this._wethAddress,
ONE_ETHER,
this._wethAddress,
this._sampler.balancerPoolsCache,
this._liquidityProviderRegistry,
this._multiBridge,
),
// Get sell quotes for taker -> maker. // Get sell quotes for taker -> maker.
await DexOrderSampler.ops.getSellQuotesAsync( await DexOrderSampler.ops.getSellQuotesAsync(
difference( difference(
@ -152,7 +164,7 @@ export class MarketOperationUtils {
.then(async r => this._sampler.executeAsync(r)); .then(async r => this._sampler.executeAsync(r));
const [ const [
[orderFillableAmounts, liquidityProviderAddress, ethToMakerAssetRate, dexQuotes], [orderFillableAmounts, liquidityProviderAddress, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes],
rfqtIndicativeQuotes, rfqtIndicativeQuotes,
[balancerQuotes], [balancerQuotes],
] = await Promise.all([samplerPromise, rfqtPromise, balancerPromise]); ] = await Promise.all([samplerPromise, rfqtPromise, balancerPromise]);
@ -174,6 +186,7 @@ export class MarketOperationUtils {
nativeOrders, nativeOrders,
orderFillableAmounts, orderFillableAmounts,
ethToOutputRate: ethToMakerAssetRate, ethToOutputRate: ethToMakerAssetRate,
ethToInputRate: ethToTakerAssetRate,
rfqtIndicativeQuotes, rfqtIndicativeQuotes,
}; };
} }
@ -207,6 +220,17 @@ export class MarketOperationUtils {
makerToken, makerToken,
takerToken, takerToken,
), ),
// Get ETH -> maker token price.
await DexOrderSampler.ops.getMedianSellRateAsync(
difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources),
makerToken,
this._wethAddress,
ONE_ETHER,
this._wethAddress,
this._sampler.balancerPoolsCache,
this._liquidityProviderRegistry,
this._multiBridge,
),
// Get ETH -> taker token price. // Get ETH -> taker token price.
await DexOrderSampler.ops.getMedianSellRateAsync( await DexOrderSampler.ops.getMedianSellRateAsync(
difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources), difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources),
@ -255,7 +279,7 @@ export class MarketOperationUtils {
_opts, _opts,
); );
const [ const [
[orderFillableAmounts, liquidityProviderAddress, ethToTakerAssetRate, dexQuotes], [orderFillableAmounts, liquidityProviderAddress, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes],
rfqtIndicativeQuotes, rfqtIndicativeQuotes,
[balancerQuotes], [balancerQuotes],
] = await Promise.all([samplerPromise, rfqtPromise, balancerPromise]); ] = await Promise.all([samplerPromise, rfqtPromise, balancerPromise]);
@ -276,6 +300,7 @@ export class MarketOperationUtils {
nativeOrders, nativeOrders,
orderFillableAmounts, orderFillableAmounts,
ethToOutputRate: ethToTakerAssetRate, ethToOutputRate: ethToTakerAssetRate,
ethToInputRate: ethToMakerAssetRate,
rfqtIndicativeQuotes, rfqtIndicativeQuotes,
}; };
} }
@ -388,6 +413,7 @@ export class MarketOperationUtils {
const batchOrderFillableAmounts = executeResults.splice(0, batchNativeOrders.length) as BigNumber[][]; const batchOrderFillableAmounts = executeResults.splice(0, batchNativeOrders.length) as BigNumber[][];
const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[]; const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[];
const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][]; const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][];
const ethToInputRate = ZERO_AMOUNT;
return Promise.all( return Promise.all(
batchNativeOrders.map(async (nativeOrders, i) => { batchNativeOrders.map(async (nativeOrders, i) => {
@ -408,6 +434,7 @@ export class MarketOperationUtils {
dexQuotes, dexQuotes,
inputAmount: makerAmount, inputAmount: makerAmount,
ethToOutputRate: ethToTakerAssetRate, ethToOutputRate: ethToTakerAssetRate,
ethToInputRate,
rfqtIndicativeQuotes: [], rfqtIndicativeQuotes: [],
inputToken: makerToken, inputToken: makerToken,
outputToken: takerToken, outputToken: takerToken,
@ -454,6 +481,7 @@ export class MarketOperationUtils {
rfqtIndicativeQuotes, rfqtIndicativeQuotes,
dexQuotes, dexQuotes,
ethToOutputRate, ethToOutputRate,
ethToInputRate,
} = marketSideLiquidity; } = marketSideLiquidity;
const maxFallbackSlippage = opts.maxFallbackSlippage || 0; const maxFallbackSlippage = opts.maxFallbackSlippage || 0;
// Convert native orders and dex quotes into fill paths. // Convert native orders and dex quotes into fill paths.
@ -467,6 +495,7 @@ export class MarketOperationUtils {
dexQuotes, dexQuotes,
targetInput: inputAmount, targetInput: inputAmount,
ethToOutputRate, ethToOutputRate,
ethToInputRate,
excludedSources: opts.excludedSources, excludedSources: opts.excludedSources,
feeSchedule: opts.feeSchedule, feeSchedule: opts.feeSchedule,
}); });

View File

@ -291,5 +291,6 @@ export interface MarketSideLiquidity {
nativeOrders: SignedOrder[]; nativeOrders: SignedOrder[];
orderFillableAmounts: BigNumber[]; orderFillableAmounts: BigNumber[];
ethToOutputRate: BigNumber; ethToOutputRate: BigNumber;
ethToInputRate: BigNumber;
rfqtIndicativeQuotes: RFQTIndicativeQuote[]; rfqtIndicativeQuotes: RFQTIndicativeQuote[];
} }