From 7d34e09a129f23af8d504c8ab301f17c6bfb8565 Mon Sep 17 00:00:00 2001 From: Kim Persson Date: Thu, 6 May 2021 14:54:54 +0200 Subject: [PATCH] fix: add separate priceComparisonsReport to fix missing quoteReport data [TKR-91] (#219) * fix: add separate priceComparisonsReport to fix missing quoteReport data * chore: remove notice about unconfirmed Uniswap V3 addresses * refactor: move price comparisons computation logic into separate method * chore: add AS changelog entry --- packages/asset-swapper/CHANGELOG.json | 6 ++- packages/asset-swapper/src/index.ts | 1 + packages/asset-swapper/src/swap_quoter.ts | 10 ++++- packages/asset-swapper/src/types.ts | 3 +- .../utils/market_operation_utils/constants.ts | 2 +- .../src/utils/market_operation_utils/index.ts | 41 ++++++++++++++++++- .../src/utils/market_operation_utils/types.ts | 8 +++- .../src/utils/quote_report_generator.ts | 32 +++++++++++---- 8 files changed, 89 insertions(+), 14 deletions(-) diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index 8e26b90870..51fa4f07bd 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -2,9 +2,13 @@ { "version": "6.11.0", "changes": [ + { + "note": "Add price comparisons data separate from the quote report", + "pr": 219 + }, { "note": "Add caching for top Balancer V2 pools on startup and during regular intervals", - "pr": "228" + "pr": 228 }, { "note": "Tweak compiler settings for smaller sampler bytecode", diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts index 72415d9c97..1c137ab622 100644 --- a/packages/asset-swapper/src/index.ts +++ b/packages/asset-swapper/src/index.ts @@ -161,6 +161,7 @@ export { NativeRfqOrderQuoteReportEntry, QuoteReport, QuoteReportEntry, + PriceComparisonsReport, } from './utils/quote_report_generator'; export { QuoteRequestor } from './utils/quote_requestor'; export { ERC20BridgeSamplerContract, BalanceCheckerContract, FakeTakerContract } from './wrappers'; diff --git a/packages/asset-swapper/src/swap_quoter.ts b/packages/asset-swapper/src/swap_quoter.ts index 58e23ba2ed..414b170fe1 100644 --- a/packages/asset-swapper/src/swap_quoter.ts +++ b/packages/asset-swapper/src/swap_quoter.ts @@ -495,7 +495,14 @@ function createSwapQuote( gasSchedule: FeeSchedule, slippage: number, ): SwapQuote { - const { optimizedOrders, quoteReport, sourceFlags, takerAmountPerEth, makerAmountPerEth } = optimizerResult; + const { + optimizedOrders, + quoteReport, + sourceFlags, + takerAmountPerEth, + makerAmountPerEth, + priceComparisonsReport, + } = optimizerResult; const isTwoHop = sourceFlags === SOURCE_FLAGS[ERC20BridgeSource.MultiHop]; // Calculate quote info @@ -519,6 +526,7 @@ function createSwapQuote( makerAmountPerEth, quoteReport, isTwoHop, + priceComparisonsReport, }; if (operation === MarketOperation.Buy) { diff --git a/packages/asset-swapper/src/types.ts b/packages/asset-swapper/src/types.ts index ce817d3828..4549502d76 100644 --- a/packages/asset-swapper/src/types.ts +++ b/packages/asset-swapper/src/types.ts @@ -18,7 +18,7 @@ import { OptimizedMarketOrder, TokenAdjacencyGraph, } from './utils/market_operation_utils/types'; -import { QuoteReport } from './utils/quote_report_generator'; +import { PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator'; /** * expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m). @@ -169,6 +169,7 @@ export interface SwapQuoteBase { worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown; quoteReport?: QuoteReport; + priceComparisonsReport?: PriceComparisonsReport; isTwoHop: boolean; makerTokenDecimals: number; takerTokenDecimals: number; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts index 194b9af5c4..1403b18199 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -1059,7 +1059,6 @@ export const BALANCER_V2_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId( { - // Unconfirmed Mainnet contracts, please confirm [ChainId.Mainnet]: { quoter: '0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6', router: '0xe592427a0aece92de3edee1f18e0157c05861564', @@ -1335,5 +1334,6 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = { exchangeProxyOverhead: () => ZERO_AMOUNT, allowFallback: true, shouldGenerateQuoteReport: true, + shouldIncludePriceComparisonsReport: false, tokenAdjacencyGraph: { default: [] }, }; 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 3b296f6acb..9a4194662d 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -16,7 +16,14 @@ import { getNativeAdjustedMakerFillAmount, } from '../utils'; -import { generateQuoteReport, QuoteReport } from './../quote_report_generator'; +import { + dexSampleToReportSource, + generateQuoteReport, + multiHopSampleToReportSource, + nativeOrderToReportEntry, + PriceComparisonsReport, + QuoteReport, +} from './../quote_report_generator'; import { getComparisonPrices } from './comparison_price'; import { BUY_SOURCE_FILTER_BY_CHAIN_ID, @@ -68,6 +75,27 @@ export class MarketOperationUtils { return generateQuoteReport(side, quotes.nativeOrders, liquidityDelivered, comparisonPrice, quoteRequestor); } + private static _computePriceComparisonsReport( + quoteRequestor: QuoteRequestor | undefined, + marketSideLiquidity: MarketSideLiquidity, + comparisonPrice?: BigNumber | undefined, + ): PriceComparisonsReport { + const { side, quotes } = marketSideLiquidity; + const dexSources = _.flatten(quotes.dexQuotes).map(quote => dexSampleToReportSource(quote, side)); + const multiHopSources = quotes.twoHopQuotes.map(quote => multiHopSampleToReportSource(quote, side)); + const nativeSources = quotes.nativeOrders.map(order => + nativeOrderToReportEntry( + order.type, + order as any, + order.fillableTakerAmount, + comparisonPrice, + quoteRequestor, + ), + ); + + return { dexSources, multiHopSources, nativeSources }; + } + constructor( private readonly _sampler: DexOrderSampler, private readonly contractAddresses: AssetSwapperContractAddresses, @@ -677,7 +705,16 @@ export class MarketOperationUtils { wholeOrderPrice, ); } - return { ...optimizerResult, quoteReport }; + + let priceComparisonsReport: PriceComparisonsReport | undefined; + if (_opts.shouldIncludePriceComparisonsReport) { + priceComparisonsReport = MarketOperationUtils._computePriceComparisonsReport( + _opts.rfqt ? _opts.rfqt.quoteRequestor : undefined, + marketSideLiquidity, + wholeOrderPrice, + ); + } + return { ...optimizerResult, quoteReport, priceComparisonsReport }; } private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise { 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 f5a1668758..770673f6d5 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -9,7 +9,7 @@ import { BigNumber } from '@0x/utils'; import { NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts } from '../../types'; import { QuoteRequestor } from '../../utils/quote_requestor'; -import { QuoteReport } from '../quote_report_generator'; +import { PriceComparisonsReport, QuoteReport } from '../quote_report_generator'; import { CollapsedPath } from './path'; import { SourceFilters } from './source_filters'; @@ -401,6 +401,11 @@ export interface GetMarketOrdersOpts { * Whether to generate a quote report */ shouldGenerateQuoteReport: boolean; + + /** + * Whether to include price comparison data in the quote + */ + shouldIncludePriceComparisonsReport: boolean; /** * Token addresses with a list of adjacent intermediary tokens to consider * hopping to. E.g DAI->USDC via an adjacent token WETH @@ -435,6 +440,7 @@ export interface OptimizerResult { export interface OptimizerResultWithReport extends OptimizerResult { quoteReport?: QuoteReport; + priceComparisonsReport?: PriceComparisonsReport; } export type MarketDepthSide = Array>>; diff --git a/packages/asset-swapper/src/utils/quote_report_generator.ts b/packages/asset-swapper/src/utils/quote_report_generator.ts index cd756a922e..e7f2863e32 100644 --- a/packages/asset-swapper/src/utils/quote_report_generator.ts +++ b/packages/asset-swapper/src/utils/quote_report_generator.ts @@ -60,6 +60,12 @@ export interface QuoteReport { sourcesDelivered: QuoteReportEntry[]; } +export interface PriceComparisonsReport { + dexSources: BridgeQuoteReportEntry[]; + multiHopSources: MultiHopQuoteReportEntry[]; + nativeSources: Array; +} + /** * Generates a report of sources considered while computing the optimized * swap quote, and the sources ultimately included in the computed quote. @@ -72,7 +78,7 @@ export function generateQuoteReport( quoteRequestor?: QuoteRequestor, ): QuoteReport { const nativeOrderSourcesConsidered = nativeOrders.map(order => - _nativeOrderToReportEntry(order.type, order as any, order.fillableTakerAmount, comparisonPrice, quoteRequestor), + nativeOrderToReportEntry(order.type, order as any, order.fillableTakerAmount, comparisonPrice, quoteRequestor), ); const sourcesConsidered = [...nativeOrderSourcesConsidered.filter(order => order.isRfqt)]; @@ -87,7 +93,7 @@ export function generateQuoteReport( // map sources delivered sourcesDelivered = liquidityDelivered.map(collapsedFill => { if (_isNativeOrderFromCollapsedFill(collapsedFill)) { - return _nativeOrderToReportEntry( + return nativeOrderToReportEntry( collapsedFill.type, collapsedFill.fillData, nativeOrderSignaturesToFillableAmounts[_nativeDataToId(collapsedFill.fillData)], @@ -95,13 +101,13 @@ export function generateQuoteReport( quoteRequestor, ); } else { - return _dexSampleToReportSource(collapsedFill, marketOperation); + return dexSampleToReportSource(collapsedFill, marketOperation); } }); } else { sourcesDelivered = [ // tslint:disable-next-line: no-unnecessary-type-assertion - _multiHopSampleToReportSource(liquidityDelivered as DexSample, marketOperation), + multiHopSampleToReportSource(liquidityDelivered as DexSample, marketOperation), ]; } return { @@ -115,7 +121,11 @@ function _nativeDataToId(data: { signature: Signature }): string { return `${v}${r}${s}`; } -function _dexSampleToReportSource(ds: DexSample, marketOperation: MarketOperation): BridgeQuoteReportEntry { +/** + * Generates a report sample for a DEX source + * NOTE: this is used for the QuoteReport and quote price comparison data + */ +export function dexSampleToReportSource(ds: DexSample, marketOperation: MarketOperation): BridgeQuoteReportEntry { const liquiditySource = ds.source; if (liquiditySource === ERC20BridgeSource.Native) { @@ -143,7 +153,11 @@ function _dexSampleToReportSource(ds: DexSample, marketOperation: MarketOperatio } } -function _multiHopSampleToReportSource( +/** + * Generates a report sample for a MultiHop source + * NOTE: this is used for the QuoteReport and quote price comparison data + */ +export function multiHopSampleToReportSource( ds: DexSample, marketOperation: MarketOperation, ): MultiHopQuoteReportEntry { @@ -176,7 +190,11 @@ function _isNativeOrderFromCollapsedFill(cf: CollapsedFill): cf is NativeCollaps return type === FillQuoteTransformerOrderType.Limit || type === FillQuoteTransformerOrderType.Rfq; } -function _nativeOrderToReportEntry( +/** + * Generates a report entry for a native order + * NOTE: this is used for the QuoteReport and quote price comparison data + */ +export function nativeOrderToReportEntry( type: FillQuoteTransformerOrderType, fillData: NativeLimitOrderFillData | NativeRfqOrderFillData, fillableAmount: BigNumber,