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
This commit is contained in:
Kim Persson 2021-05-06 14:54:54 +02:00 committed by GitHub
parent 7d15baad0f
commit 7d34e09a12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 89 additions and 14 deletions

View File

@ -2,9 +2,13 @@
{ {
"version": "6.11.0", "version": "6.11.0",
"changes": [ "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", "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", "note": "Tweak compiler settings for smaller sampler bytecode",

View File

@ -161,6 +161,7 @@ export {
NativeRfqOrderQuoteReportEntry, NativeRfqOrderQuoteReportEntry,
QuoteReport, QuoteReport,
QuoteReportEntry, QuoteReportEntry,
PriceComparisonsReport,
} from './utils/quote_report_generator'; } from './utils/quote_report_generator';
export { QuoteRequestor } from './utils/quote_requestor'; export { QuoteRequestor } from './utils/quote_requestor';
export { ERC20BridgeSamplerContract, BalanceCheckerContract, FakeTakerContract } from './wrappers'; export { ERC20BridgeSamplerContract, BalanceCheckerContract, FakeTakerContract } from './wrappers';

View File

@ -495,7 +495,14 @@ function createSwapQuote(
gasSchedule: FeeSchedule, gasSchedule: FeeSchedule,
slippage: number, slippage: number,
): SwapQuote { ): SwapQuote {
const { optimizedOrders, quoteReport, sourceFlags, takerAmountPerEth, makerAmountPerEth } = optimizerResult; const {
optimizedOrders,
quoteReport,
sourceFlags,
takerAmountPerEth,
makerAmountPerEth,
priceComparisonsReport,
} = optimizerResult;
const isTwoHop = sourceFlags === SOURCE_FLAGS[ERC20BridgeSource.MultiHop]; const isTwoHop = sourceFlags === SOURCE_FLAGS[ERC20BridgeSource.MultiHop];
// Calculate quote info // Calculate quote info
@ -519,6 +526,7 @@ function createSwapQuote(
makerAmountPerEth, makerAmountPerEth,
quoteReport, quoteReport,
isTwoHop, isTwoHop,
priceComparisonsReport,
}; };
if (operation === MarketOperation.Buy) { if (operation === MarketOperation.Buy) {

View File

@ -18,7 +18,7 @@ import {
OptimizedMarketOrder, OptimizedMarketOrder,
TokenAdjacencyGraph, TokenAdjacencyGraph,
} from './utils/market_operation_utils/types'; } 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). * 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; worstCaseQuoteInfo: SwapQuoteInfo;
sourceBreakdown: SwapQuoteOrdersBreakdown; sourceBreakdown: SwapQuoteOrdersBreakdown;
quoteReport?: QuoteReport; quoteReport?: QuoteReport;
priceComparisonsReport?: PriceComparisonsReport;
isTwoHop: boolean; isTwoHop: boolean;
makerTokenDecimals: number; makerTokenDecimals: number;
takerTokenDecimals: number; takerTokenDecimals: number;

View File

@ -1059,7 +1059,6 @@ export const BALANCER_V2_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name
export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId( export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId(
{ {
// Unconfirmed Mainnet contracts, please confirm
[ChainId.Mainnet]: { [ChainId.Mainnet]: {
quoter: '0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6', quoter: '0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6',
router: '0xe592427a0aece92de3edee1f18e0157c05861564', router: '0xe592427a0aece92de3edee1f18e0157c05861564',
@ -1335,5 +1334,6 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
exchangeProxyOverhead: () => ZERO_AMOUNT, exchangeProxyOverhead: () => ZERO_AMOUNT,
allowFallback: true, allowFallback: true,
shouldGenerateQuoteReport: true, shouldGenerateQuoteReport: true,
shouldIncludePriceComparisonsReport: false,
tokenAdjacencyGraph: { default: [] }, tokenAdjacencyGraph: { default: [] },
}; };

View File

@ -16,7 +16,14 @@ import {
getNativeAdjustedMakerFillAmount, getNativeAdjustedMakerFillAmount,
} from '../utils'; } 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 { getComparisonPrices } from './comparison_price';
import { import {
BUY_SOURCE_FILTER_BY_CHAIN_ID, BUY_SOURCE_FILTER_BY_CHAIN_ID,
@ -68,6 +75,27 @@ export class MarketOperationUtils {
return generateQuoteReport(side, quotes.nativeOrders, liquidityDelivered, comparisonPrice, quoteRequestor); 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( constructor(
private readonly _sampler: DexOrderSampler, private readonly _sampler: DexOrderSampler,
private readonly contractAddresses: AssetSwapperContractAddresses, private readonly contractAddresses: AssetSwapperContractAddresses,
@ -677,7 +705,16 @@ export class MarketOperationUtils {
wholeOrderPrice, 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<void> { private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise<void> {

View File

@ -9,7 +9,7 @@ import { BigNumber } from '@0x/utils';
import { NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts } from '../../types'; import { NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts } from '../../types';
import { QuoteRequestor } from '../../utils/quote_requestor'; import { QuoteRequestor } from '../../utils/quote_requestor';
import { QuoteReport } from '../quote_report_generator'; import { PriceComparisonsReport, QuoteReport } from '../quote_report_generator';
import { CollapsedPath } from './path'; import { CollapsedPath } from './path';
import { SourceFilters } from './source_filters'; import { SourceFilters } from './source_filters';
@ -401,6 +401,11 @@ export interface GetMarketOrdersOpts {
* Whether to generate a quote report * Whether to generate a quote report
*/ */
shouldGenerateQuoteReport: boolean; shouldGenerateQuoteReport: boolean;
/**
* Whether to include price comparison data in the quote
*/
shouldIncludePriceComparisonsReport: boolean;
/** /**
* Token addresses with a list of adjacent intermediary tokens to consider * Token addresses with a list of adjacent intermediary tokens to consider
* hopping to. E.g DAI->USDC via an adjacent token WETH * hopping to. E.g DAI->USDC via an adjacent token WETH
@ -435,6 +440,7 @@ export interface OptimizerResult {
export interface OptimizerResultWithReport extends OptimizerResult { export interface OptimizerResultWithReport extends OptimizerResult {
quoteReport?: QuoteReport; quoteReport?: QuoteReport;
priceComparisonsReport?: PriceComparisonsReport;
} }
export type MarketDepthSide = Array<Array<DexSample<FillData>>>; export type MarketDepthSide = Array<Array<DexSample<FillData>>>;

View File

@ -60,6 +60,12 @@ export interface QuoteReport {
sourcesDelivered: QuoteReportEntry[]; sourcesDelivered: QuoteReportEntry[];
} }
export interface PriceComparisonsReport {
dexSources: BridgeQuoteReportEntry[];
multiHopSources: MultiHopQuoteReportEntry[];
nativeSources: Array<NativeLimitOrderQuoteReportEntry | NativeRfqOrderQuoteReportEntry>;
}
/** /**
* Generates a report of sources considered while computing the optimized * Generates a report of sources considered while computing the optimized
* swap quote, and the sources ultimately included in the computed quote. * swap quote, and the sources ultimately included in the computed quote.
@ -72,7 +78,7 @@ export function generateQuoteReport(
quoteRequestor?: QuoteRequestor, quoteRequestor?: QuoteRequestor,
): QuoteReport { ): QuoteReport {
const nativeOrderSourcesConsidered = nativeOrders.map(order => 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)]; const sourcesConsidered = [...nativeOrderSourcesConsidered.filter(order => order.isRfqt)];
@ -87,7 +93,7 @@ export function generateQuoteReport(
// map sources delivered // map sources delivered
sourcesDelivered = liquidityDelivered.map(collapsedFill => { sourcesDelivered = liquidityDelivered.map(collapsedFill => {
if (_isNativeOrderFromCollapsedFill(collapsedFill)) { if (_isNativeOrderFromCollapsedFill(collapsedFill)) {
return _nativeOrderToReportEntry( return nativeOrderToReportEntry(
collapsedFill.type, collapsedFill.type,
collapsedFill.fillData, collapsedFill.fillData,
nativeOrderSignaturesToFillableAmounts[_nativeDataToId(collapsedFill.fillData)], nativeOrderSignaturesToFillableAmounts[_nativeDataToId(collapsedFill.fillData)],
@ -95,13 +101,13 @@ export function generateQuoteReport(
quoteRequestor, quoteRequestor,
); );
} else { } else {
return _dexSampleToReportSource(collapsedFill, marketOperation); return dexSampleToReportSource(collapsedFill, marketOperation);
} }
}); });
} else { } else {
sourcesDelivered = [ sourcesDelivered = [
// tslint:disable-next-line: no-unnecessary-type-assertion // tslint:disable-next-line: no-unnecessary-type-assertion
_multiHopSampleToReportSource(liquidityDelivered as DexSample<MultiHopFillData>, marketOperation), multiHopSampleToReportSource(liquidityDelivered as DexSample<MultiHopFillData>, marketOperation),
]; ];
} }
return { return {
@ -115,7 +121,11 @@ function _nativeDataToId(data: { signature: Signature }): string {
return `${v}${r}${s}`; 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; const liquiditySource = ds.source;
if (liquiditySource === ERC20BridgeSource.Native) { 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<MultiHopFillData>, ds: DexSample<MultiHopFillData>,
marketOperation: MarketOperation, marketOperation: MarketOperation,
): MultiHopQuoteReportEntry { ): MultiHopQuoteReportEntry {
@ -176,7 +190,11 @@ function _isNativeOrderFromCollapsedFill(cf: CollapsedFill): cf is NativeCollaps
return type === FillQuoteTransformerOrderType.Limit || type === FillQuoteTransformerOrderType.Rfq; 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, type: FillQuoteTransformerOrderType,
fillData: NativeLimitOrderFillData | NativeRfqOrderFillData, fillData: NativeLimitOrderFillData | NativeRfqOrderFillData,
fillableAmount: BigNumber, fillableAmount: BigNumber,