use compatible quotes across different hop legs
This commit is contained in:
		@@ -319,10 +319,8 @@ export class SwapQuoter {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //  ** Prepare options for fetching market side liquidity **
 | 
					        //  ** Prepare options for fetching market side liquidity **
 | 
				
			||||||
        // Scale fees by gas price.
 | 
					 | 
				
			||||||
        const cloneOpts = _.omit(opts, 'gasPrice') as GetMarketOrdersOpts;
 | 
					 | 
				
			||||||
        const calcOpts: GetMarketOrdersOpts = {
 | 
					        const calcOpts: GetMarketOrdersOpts = {
 | 
				
			||||||
            ...cloneOpts,
 | 
					            ...opts,
 | 
				
			||||||
            gasPrice,
 | 
					            gasPrice,
 | 
				
			||||||
            exchangeProxyOverhead: opts.exchangeProxyOverhead,
 | 
					            exchangeProxyOverhead: opts.exchangeProxyOverhead,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -227,13 +227,7 @@ export class MarketOperationUtils {
 | 
				
			|||||||
            makerTokenDecimals: makerTokenDecimals,
 | 
					            makerTokenDecimals: makerTokenDecimals,
 | 
				
			||||||
            takerTokenDecimals: takerTokenDecimals,
 | 
					            takerTokenDecimals: takerTokenDecimals,
 | 
				
			||||||
            gasPrice: opts.gasPrice,
 | 
					            gasPrice: opts.gasPrice,
 | 
				
			||||||
            quotes: sampleLegs.map((tokenPath, i) => ({
 | 
					            quotes: createRawHopQuotesFromSamples(MarketOperation.Sell, sampleLegs, samples),
 | 
				
			||||||
                tokenPath,
 | 
					 | 
				
			||||||
                inputToken: tokenPath[0],
 | 
					 | 
				
			||||||
                outputToken: tokenPath[tokenPath.length - 1],
 | 
					 | 
				
			||||||
                nativeOrders: [],
 | 
					 | 
				
			||||||
                dexQuotes: samples[i],
 | 
					 | 
				
			||||||
            })).filter(doesRawHopQuotesHaveLiquidity),
 | 
					 | 
				
			||||||
            isRfqSupported,
 | 
					            isRfqSupported,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -335,6 +329,7 @@ export class MarketOperationUtils {
 | 
				
			|||||||
        twoHopPathDetails = twoHopPathDetails
 | 
					        twoHopPathDetails = twoHopPathDetails
 | 
				
			||||||
            .sort((a, b) => -a.totalPrice.comparedTo(b.totalPrice))
 | 
					            .sort((a, b) => -a.totalPrice.comparedTo(b.totalPrice))
 | 
				
			||||||
            .slice(0, 3);
 | 
					            .slice(0, 3);
 | 
				
			||||||
 | 
					        console.log(twoHopPathDetails.map(p => ({ ...p, legs: p.legs.join(' -> ') })));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (side === MarketOperation.Buy) {
 | 
					        if (side === MarketOperation.Buy) {
 | 
				
			||||||
            // Reverse legs and prices and invert prices for buys.
 | 
					            // Reverse legs and prices and invert prices for buys.
 | 
				
			||||||
@@ -471,13 +466,7 @@ export class MarketOperationUtils {
 | 
				
			|||||||
            makerTokenDecimals: makerTokenDecimals,
 | 
					            makerTokenDecimals: makerTokenDecimals,
 | 
				
			||||||
            takerTokenDecimals: takerTokenDecimals,
 | 
					            takerTokenDecimals: takerTokenDecimals,
 | 
				
			||||||
            gasPrice: opts.gasPrice,
 | 
					            gasPrice: opts.gasPrice,
 | 
				
			||||||
            quotes: sampleLegs.map((tokenPath, i) => ({
 | 
					            quotes: createRawHopQuotesFromSamples(MarketOperation.Buy, sampleLegs, samples),
 | 
				
			||||||
                tokenPath,
 | 
					 | 
				
			||||||
                inputToken: tokenPath[tokenPath.length - 1],
 | 
					 | 
				
			||||||
                outputToken: tokenPath[0],
 | 
					 | 
				
			||||||
                nativeOrders: [],
 | 
					 | 
				
			||||||
                dexQuotes: samples[i],
 | 
					 | 
				
			||||||
            })).filter(doesRawHopQuotesHaveLiquidity),
 | 
					 | 
				
			||||||
            isRfqSupported,
 | 
					            isRfqSupported,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -951,6 +940,7 @@ export class MarketOperationUtils {
 | 
				
			|||||||
            let hopInputAmount = inputAmount;
 | 
					            let hopInputAmount = inputAmount;
 | 
				
			||||||
            const hops = [];
 | 
					            const hops = [];
 | 
				
			||||||
            for (const routeHop of route) {
 | 
					            for (const routeHop of route) {
 | 
				
			||||||
 | 
					                console.log([routeHop.inputToken, routeHop.outputToken], routeHop.dexQuotes.map(q => q[0].source));
 | 
				
			||||||
                const hop = await this._createOptimizedHopAsync({
 | 
					                const hop = await this._createOptimizedHopAsync({
 | 
				
			||||||
                    side,
 | 
					                    side,
 | 
				
			||||||
                    slippage,
 | 
					                    slippage,
 | 
				
			||||||
@@ -995,8 +985,9 @@ export class MarketOperationUtils {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
        for (const route of hopRoutes) {
 | 
					        for (const route of hopRoutes) {
 | 
				
			||||||
            const rate = getHopRouteOverallAdjustedTakerToMakerRate(route, rateOpts);
 | 
					            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)) {
 | 
					            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;
 | 
					                bestHopRoute = route;
 | 
				
			||||||
                bestHopRouteTotalRate = rate;
 | 
					                bestHopRouteTotalRate = rate;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -1109,7 +1100,6 @@ function injectRfqLiquidity(
 | 
				
			|||||||
        quotes.push({
 | 
					        quotes.push({
 | 
				
			||||||
            inputToken,
 | 
					            inputToken,
 | 
				
			||||||
            outputToken,
 | 
					            outputToken,
 | 
				
			||||||
            tokenPath: [takerToken, makerToken],
 | 
					 | 
				
			||||||
            dexQuotes: [],
 | 
					            dexQuotes: [],
 | 
				
			||||||
            nativeOrders: fullOrders,
 | 
					            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 {
 | 
					function isSameTokenPath(a: Address[], b: Address[]): boolean {
 | 
				
			||||||
    return a.length === b.length && a.every((v, idx) => v === b[idx]);
 | 
					    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);
 | 
					     const [pathTakerToken, pathMakerToken] = getTakerMakerTokenFromTokenPath(tokenPath);
 | 
				
			||||||
     return pathTakerToken === takerToken && pathMakerToken === makerToken;
 | 
					     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);
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ import { BigNumber } from '@0x/utils';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { Address } from '../../types';
 | 
					import { Address } from '../../types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DexSample, ERC20BridgeSource, TokenAdjacencyGraph } from './types';
 | 
					import { DexSample, ERC20BridgeSource } from './types';
 | 
				
			||||||
import { SamplerServiceRpcClient } from './sampler_service_rpc_client';
 | 
					import { SamplerServiceRpcClient } from './sampler_service_rpc_client';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DEFAULT_LIQUIDITY_SAMPLES = 16;
 | 
					const DEFAULT_LIQUIDITY_SAMPLES = 16;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -518,7 +518,6 @@ export interface MarketSideLiquidity {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface RawHopQuotes {
 | 
					export interface RawHopQuotes {
 | 
				
			||||||
    tokenPath: Address[];
 | 
					 | 
				
			||||||
    inputToken: Address;
 | 
					    inputToken: Address;
 | 
				
			||||||
    outputToken: Address;
 | 
					    outputToken: Address;
 | 
				
			||||||
    nativeOrders: NativeOrderWithFillableAmounts[];
 | 
					    nativeOrders: NativeOrderWithFillableAmounts[];
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user