@0x/asset-swapper: Clean up source breakdown code a bit.

`@0x/asset-swapper`: Allow Kyber conflicts in fallback path.
This commit is contained in:
Lawrence Forman
2020-03-10 00:05:14 -04:00
parent 05bf55dca8
commit 0e7a473116
3 changed files with 29 additions and 38 deletions

View File

@@ -260,23 +260,23 @@ export function collapsePath(side: MarketOperation, path: Fill[]): CollapsedFill
}
export function getFallbackSourcePaths(optimalPath: Fill[], allPaths: Fill[][]): Fill[][] {
let optimalPathFlags = 0;
const optimalSources: ERC20BridgeSource[] = [];
for (const fill of optimalPath) {
optimalPathFlags |= fill.flags;
if (!optimalSources.includes(fill.source)) {
optimalSources.push(fill.source);
}
}
const conflictFlags = FillFlags.Kyber | FillFlags.ConflictsWithKyber;
const fallbackPaths: Fill[][] = [];
for (const path of allPaths) {
if (((optimalPathFlags | path[0].flags) & conflictFlags) === conflictFlags) {
continue;
}
if (optimalSources.includes(path[0].source)) {
continue;
}
// HACK(dorothy-zbornak): We *should* be filtering out paths that
// conflict with the optimal path (i.e., Kyber conflicts), but in
// practice we often end up not being able to find a fallback path
// because we've lost 2 major liquiduty sources. The end result is
// we end up with many more reverts than what would be actually caused
// by conflicts.
fallbackPaths.push(path);
}
return fallbackPaths;

View File

@@ -285,10 +285,10 @@ export class MarketOperationUtils {
let fallbackPath: Fill[] = [];
const nativeSubPath = optimalPath.filter(f => f.source === ERC20BridgeSource.Native);
if (opts.allowFallback && nativeSubPath.length !== 0) {
// The fallback path is only as large as the native path.
const [nativeInputAmount] = getPathSize(nativeSubPath, inputAmount);
// The fallback path is, at most, as large as the native path.
const fallbackInputAmount = BigNumber.min(inputAmount, getPathSize(nativeSubPath, inputAmount)[0]);
fallbackPath =
findOptimalPath(side, getFallbackSourcePaths(optimalPath, paths), nativeInputAmount, opts.runLimit) ||
findOptimalPath(side, getFallbackSourcePaths(optimalPath, paths), fallbackInputAmount, opts.runLimit) ||
[];
}
return createOrdersFromPath([...optimalPath, ...fallbackPath], {

View File

@@ -198,7 +198,7 @@ export class SwapQuoteCalculator {
true,
);
const breakdown = this._getSwapQuoteOrdersBreakdown(resultOrders, operation);
const breakdown = getSwapQuoteOrdersBreakdown(resultOrders, operation);
const quoteBase: SwapQuoteBase = {
takerAssetData,
@@ -427,36 +427,27 @@ export class SwapQuoteCalculator {
gas: 0,
};
}
}
// tslint:disable-next-line: prefer-function-over-method
private _getSwapQuoteOrdersBreakdown(
orders: OptimizedMarketOrder[],
operation: MarketOperation,
): SwapQuoteOrdersBreakdown {
// HACK: to shut up linter
const breakdown: SwapQuoteOrdersBreakdown = {};
// total asset amount (accounting for slippage protection)
const totalAssetAmount = BigNumber.sum(
...[
constants.ZERO_AMOUNT,
...orders.map(o => (operation === MarketOperation.Buy ? o.makerAssetAmount : o.takerAssetAmount)),
],
);
return orders.reduce((acc: SwapQuoteOrdersBreakdown, order: OptimizedMarketOrder): SwapQuoteOrdersBreakdown => {
const assetAmount = operation === MarketOperation.Buy ? order.makerAssetAmount : order.takerAssetAmount;
const { source } = order.fill;
return {
...acc,
...{
[source]: !!acc[source]
? acc[source].plus(assetAmount.dividedBy(totalAssetAmount))
: assetAmount.dividedBy(totalAssetAmount),
},
};
}, breakdown);
function getSwapQuoteOrdersBreakdown(
orders: OptimizedMarketOrder[],
operation: MarketOperation,
): SwapQuoteOrdersBreakdown {
const orderAmounts =
operation === MarketOperation.Buy
? orders.map(o => o.fill.totalMakerAssetAmount)
: orders.map(o => o.fill.totalTakerAssetAmount);
const amountsBySource: SwapQuoteOrdersBreakdown = {};
orders.forEach((o, i) => {
const source = o.fill.source;
amountsBySource[source] = orderAmounts[i].plus(amountsBySource[source] || 0);
});
const totalAmount = BigNumber.sum(0, ...orderAmounts);
const breakdown: SwapQuoteOrdersBreakdown = {};
for (const [source, amount] of Object.entries(amountsBySource)) {
breakdown[source] = amount.div(totalAmount);
}
return breakdown;
}
function getTakerAssetAmountBreakDown(