diff --git a/packages/asset-swapper/src/swap_quoter.ts b/packages/asset-swapper/src/swap_quoter.ts index ebca05c093..2c6dc82bf1 100644 --- a/packages/asset-swapper/src/swap_quoter.ts +++ b/packages/asset-swapper/src/swap_quoter.ts @@ -319,10 +319,8 @@ export class SwapQuoter { } // ** Prepare options for fetching market side liquidity ** - // Scale fees by gas price. - const cloneOpts = _.omit(opts, 'gasPrice') as GetMarketOrdersOpts; const calcOpts: GetMarketOrdersOpts = { - ...cloneOpts, + ...opts, gasPrice, exchangeProxyOverhead: opts.exchangeProxyOverhead, }; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/index.ts b/packages/asset-swapper/src/utils/market_operation_utils/index.ts index ce75501083..09af12f8a2 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -227,13 +227,7 @@ export class MarketOperationUtils { makerTokenDecimals: makerTokenDecimals, takerTokenDecimals: takerTokenDecimals, gasPrice: opts.gasPrice, - quotes: sampleLegs.map((tokenPath, i) => ({ - tokenPath, - inputToken: tokenPath[0], - outputToken: tokenPath[tokenPath.length - 1], - nativeOrders: [], - dexQuotes: samples[i], - })).filter(doesRawHopQuotesHaveLiquidity), + quotes: createRawHopQuotesFromSamples(MarketOperation.Sell, sampleLegs, samples), isRfqSupported, }; } @@ -335,6 +329,7 @@ export class MarketOperationUtils { twoHopPathDetails = twoHopPathDetails .sort((a, b) => -a.totalPrice.comparedTo(b.totalPrice)) .slice(0, 3); + console.log(twoHopPathDetails.map(p => ({ ...p, legs: p.legs.join(' -> ') }))); if (side === MarketOperation.Buy) { // Reverse legs and prices and invert prices for buys. @@ -471,13 +466,7 @@ export class MarketOperationUtils { makerTokenDecimals: makerTokenDecimals, takerTokenDecimals: takerTokenDecimals, gasPrice: opts.gasPrice, - quotes: sampleLegs.map((tokenPath, i) => ({ - tokenPath, - inputToken: tokenPath[tokenPath.length - 1], - outputToken: tokenPath[0], - nativeOrders: [], - dexQuotes: samples[i], - })).filter(doesRawHopQuotesHaveLiquidity), + quotes: createRawHopQuotesFromSamples(MarketOperation.Buy, sampleLegs, samples), isRfqSupported, }; } @@ -951,6 +940,7 @@ export class MarketOperationUtils { let hopInputAmount = inputAmount; const hops = []; for (const routeHop of route) { + console.log([routeHop.inputToken, routeHop.outputToken], routeHop.dexQuotes.map(q => q[0].source)); const hop = await this._createOptimizedHopAsync({ side, slippage, @@ -995,8 +985,9 @@ export class MarketOperationUtils { }; for (const route of hopRoutes) { const rate = getHopRouteOverallAdjustedTakerToMakerRate(route, rateOpts); + console.log(`considering route: <\n\t${route.map(r => `path: ${r.inputToken}->${r.outputToken}, ${r.orders.map(o => o.source)}, output: ${r.outputAmount}`).join(',\n\t')}\n>, overall rate: ${rate}, overall gas cost: ${route.reduce((a,v) => a + v.gasCost, 0)}`); if (!bestHopRouteTotalRate || rate.gt(bestHopRouteTotalRate)) { - console.log(`new best route: <\n\t${route.map(r => `path: ${r.inputToken}->${r.outputToken}, ${r.orders.map(o => o.source)}, output: ${r.outputAmount}`).join(',\n\t')}\n>, overall rate: ${rate}, overall gas cost: ${route.reduce((a,v) => a + v.gasCost, 0)}`); + console.log(`new best route: <${route.map(r => `path: ${r.inputToken}->${r.outputToken}`)}>`); bestHopRoute = route; bestHopRouteTotalRate = rate; } @@ -1109,7 +1100,6 @@ function injectRfqLiquidity( quotes.push({ inputToken, outputToken, - tokenPath: [takerToken, makerToken], dexQuotes: [], nativeOrders: fullOrders, }); @@ -1139,10 +1129,6 @@ function getTerminalTokensFromPaths(paths: Address[][]): Address[] { ]; } -function doesRawHopQuotesHaveLiquidity(hopQuotes: RawHopQuotes): boolean { - return hopQuotes.dexQuotes.length > 0 || hopQuotes.nativeOrders.length > 0; -} - function isSameTokenPath(a: Address[], b: Address[]): boolean { return a.length === b.length && a.every((v, idx) => v === b[idx]); } @@ -1151,3 +1137,39 @@ function isSameTokenPath(a: Address[], b: Address[]): boolean { const [pathTakerToken, pathMakerToken] = getTakerMakerTokenFromTokenPath(tokenPath); return pathTakerToken === takerToken && pathMakerToken === makerToken; } + + function createRawHopQuotesFromSamples(side: MarketOperation, tokenPaths: Address[][], samples: DexSample[][][]): RawHopQuotes[] { + if (tokenPaths.length !== samples.length) { + throw new Error(`Mismatched tokenPaths and samples arrays.`); + } + const filterEmptySamples = (s: DexSample[][]): DexSample[][] => { + return s.map(s => s.filter(ss => !ss.output.isZero())).filter(s => s.length); + }; + const hopQuotes = []; + for (let i = 0; i < tokenPaths.length; ++i) { + const tokenPath = tokenPaths[i]; + const pathSamples = samples[i]; + const [inputToken, outputToken] = [ + side === MarketOperation.Sell ? tokenPath[0] : tokenPath[tokenPath.length - 1], + side === MarketOperation.Sell ? tokenPath[tokenPath.length - 1] : tokenPath[0], + ]; + // See if there's already a hopQuote that has compatible input and output tokens + // and just merge with that one. + // We do this because we sample hidden hops separately but they can technically + // be combined together in the same route. + for (const existing of hopQuotes) { + if (existing.inputToken === inputToken && existing.outputToken === outputToken) { + existing.dexQuotes.push(...filterEmptySamples(pathSamples)); + continue; + } + } + hopQuotes.push({ + tokenPath, + inputToken, + outputToken, + nativeOrders: [], + dexQuotes: filterEmptySamples(pathSamples), + }); + } + return hopQuotes.filter(h => h.dexQuotes.length > 0 || h.nativeOrders.length > 0); + } diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts index b6662bc65a..cb99145523 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts @@ -3,7 +3,7 @@ import { BigNumber } from '@0x/utils'; import { Address } from '../../types'; -import { DexSample, ERC20BridgeSource, TokenAdjacencyGraph } from './types'; +import { DexSample, ERC20BridgeSource } from './types'; import { SamplerServiceRpcClient } from './sampler_service_rpc_client'; const DEFAULT_LIQUIDITY_SAMPLES = 16; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/types.ts b/packages/asset-swapper/src/utils/market_operation_utils/types.ts index 28a47259f3..6dfec92f07 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -518,7 +518,6 @@ export interface MarketSideLiquidity { } export interface RawHopQuotes { - tokenPath: Address[]; inputToken: Address; outputToken: Address; nativeOrders: NativeOrderWithFillableAmounts[];