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

View File

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

View File

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