Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Commit

Permalink
@0x/asset-swapper: Only generate fallbacks for native orders in opt…
Browse files Browse the repository at this point in the history
…imal path.

`@0x/asset-swapper`: Exclude conflicting sources across both optimal and fallback paths.
  • Loading branch information
merklejerk committed Mar 10, 2020
1 parent 847a7f4 commit cc12ad8
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 32 deletions.
24 changes: 21 additions & 3 deletions packages/asset-swapper/src/utils/market_operation_utils/fills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,25 @@ export function collapsePath(side: MarketOperation, path: Fill[]): CollapsedFill
return collapsed;
}

export function getUnusedSourcePaths(usedPath: Fill[], allPaths: Fill[][]): Fill[][] {
const usedSources = usedPath.map(f => f.source);
return allPaths.filter(p => !usedSources.includes(p[0].source));
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;
}
fallbackPaths.push(path);
}
return fallbackPaths;
}
44 changes: 24 additions & 20 deletions packages/asset-swapper/src/utils/market_operation_utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { MarketOperation } from '../../types';
import { difference } from '../utils';

import { BUY_SOURCES, DEFAULT_GET_MARKET_ORDERS_OPTS, FEE_QUOTE_SOURCES, ONE_ETHER, SELL_SOURCES } from './constants';
import { createFillPaths, getUnusedSourcePaths } from './fills';
import { createFillPaths, getFallbackSourcePaths, getPathSize } from './fills';
import { createOrdersFromPath, createSignedOrdersWithFillableAmounts, getNativeOrderTokens } from './orders';
import { findOptimalPath } from './path_optimizer';
import { DexOrderSampler, getSampleAmounts } from './sampler';
Expand Down Expand Up @@ -67,14 +67,14 @@ export class MarketOperationUtils {
),
// Get ETH -> maker token price.
DexOrderSampler.ops.getMedianSellRate(
difference(FEE_QUOTE_SOURCES, _opts.excludedSources).concat(
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
),
makerToken,
this._wethAddress,
ONE_ETHER,
this._liquidityProviderRegistry,
),
difference(FEE_QUOTE_SOURCES, _opts.excludedSources).concat(
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
),
makerToken,
this._wethAddress,
ONE_ETHER,
this._liquidityProviderRegistry,
),
// Get sell quotes for taker -> maker.
DexOrderSampler.ops.getSellQuotes(
difference(SELL_SOURCES, _opts.excludedSources).concat(
Expand Down Expand Up @@ -138,14 +138,14 @@ export class MarketOperationUtils {
),
// Get ETH -> taker token price.
DexOrderSampler.ops.getMedianSellRate(
difference(FEE_QUOTE_SOURCES, _opts.excludedSources).concat(
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
),
takerToken,
this._wethAddress,
ONE_ETHER,
this._liquidityProviderRegistry,
),
difference(FEE_QUOTE_SOURCES, _opts.excludedSources).concat(
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
),
takerToken,
this._wethAddress,
ONE_ETHER,
this._liquidityProviderRegistry,
),
// Get buy quotes for taker -> maker.
DexOrderSampler.ops.getBuyQuotes(
difference(BUY_SOURCES, _opts.excludedSources).concat(
Expand Down Expand Up @@ -281,11 +281,15 @@ export class MarketOperationUtils {
if (!optimalPath) {
throw new Error(AggregationError.NoOptimalPath);
}
// Find a fallback path from sources not used in the first path.
// Generate a fallback path if native orders are in the optimal paath.
let fallbackPath: Fill[] = [];
if (opts.allowFallback) {
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);
fallbackPath =
findOptimalPath(side, getUnusedSourcePaths(optimalPath, paths), inputAmount, opts.runLimit) || [];
findOptimalPath(side, getFallbackSourcePaths(optimalPath, paths), nativeInputAmount, opts.runLimit) ||
[];
}
return createOrdersFromPath([...optimalPath, ...fallbackPath], {
side,
Expand Down
24 changes: 15 additions & 9 deletions packages/asset-swapper/test/market_operation_utils_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,9 +571,10 @@ describe('MarketOperationUtils tests', () => {

it('fallback orders use different sources', async () => {
const rates: RatesBySource = {};
rates[ERC20BridgeSource.Eth2Dai] = [0.9, 0.8, 0.5, 0.5];
rates[ERC20BridgeSource.Native] = [0.9, 0.8, 0.5, 0.5];
rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01];
rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.01, 0.01];
rates[ERC20BridgeSource.Eth2Dai] = [0.4, 0.3, 0.01, 0.01];
// Won't be included because of conflicts.
rates[ERC20BridgeSource.Kyber] = [0.35, 0.2, 0.01, 0.01];
replaceSamplerOps({
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
Expand All @@ -584,8 +585,13 @@ describe('MarketOperationUtils tests', () => {
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true },
);
const orderSources = improvedOrders.map(o => o.fill.source);
const firstSources = [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap];
const secondSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Kyber, ERC20BridgeSource.Native];
const firstSources = [
ERC20BridgeSource.Native,
ERC20BridgeSource.Native,
ERC20BridgeSource.Native,
ERC20BridgeSource.Uniswap,
];
const secondSources = [ERC20BridgeSource.Eth2Dai];
expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort());
expect(orderSources.slice(firstSources.length).sort()).to.deep.eq(secondSources.sort());
});
Expand Down Expand Up @@ -881,9 +887,9 @@ describe('MarketOperationUtils tests', () => {

it('fallback orders use different sources', async () => {
const rates: RatesBySource = {};
rates[ERC20BridgeSource.Eth2Dai] = [0.9, 0.8, 0.5, 0.5];
rates[ERC20BridgeSource.Native] = [0.9, 0.8, 0.5, 0.5];
rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01];
rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.01, 0.01];
rates[ERC20BridgeSource.Eth2Dai] = [0.4, 0.3, 0.01, 0.01];
replaceSamplerOps({
getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates),
});
Expand All @@ -893,13 +899,13 @@ describe('MarketOperationUtils tests', () => {
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true },
);
const orderSources = improvedOrders.map(o => o.fill.source);
const firstSources = [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap];
const secondSources = [
ERC20BridgeSource.Native,
const firstSources = [
ERC20BridgeSource.Native,
ERC20BridgeSource.Native,
ERC20BridgeSource.Native,
ERC20BridgeSource.Uniswap,
];
const secondSources = [ERC20BridgeSource.Eth2Dai];
expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort());
expect(orderSources.slice(firstSources.length).sort()).to.deep.eq(secondSources.sort());
});
Expand Down

0 comments on commit cc12ad8

Please sign in to comment.