@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[][] { export function getFallbackSourcePaths(optimalPath: Fill[], allPaths: Fill[][]): Fill[][] {
let optimalPathFlags = 0;
const optimalSources: ERC20BridgeSource[] = []; const optimalSources: ERC20BridgeSource[] = [];
for (const fill of optimalPath) { for (const fill of optimalPath) {
optimalPathFlags |= fill.flags;
if (!optimalSources.includes(fill.source)) { if (!optimalSources.includes(fill.source)) {
optimalSources.push(fill.source); optimalSources.push(fill.source);
} }
} }
const conflictFlags = FillFlags.Kyber | FillFlags.ConflictsWithKyber;
const fallbackPaths: Fill[][] = []; const fallbackPaths: Fill[][] = [];
for (const path of allPaths) { for (const path of allPaths) {
if (((optimalPathFlags | path[0].flags) & conflictFlags) === conflictFlags) {
continue;
}
if (optimalSources.includes(path[0].source)) { if (optimalSources.includes(path[0].source)) {
continue; 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); fallbackPaths.push(path);
} }
return fallbackPaths; return fallbackPaths;

View File

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

View File

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