From b7adc5a889c21dfd7090c162e90aeea73be5a1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20P=C3=A9rez?= Date: Tue, 9 Nov 2021 13:05:01 -0600 Subject: [PATCH] feat: Extended Quote Report * Extended Quote report for indicative quote * feat: Only save 'full' quotes on quote report * Unify extended quote report --- packages/asset-swapper/CHANGELOG.json | 9 + packages/asset-swapper/src/index.ts | 8 +- packages/asset-swapper/src/swap_quoter.ts | 2 + packages/asset-swapper/src/types.ts | 3 +- .../src/utils/market_operation_utils/index.ts | 34 ++- .../src/utils/market_operation_utils/types.ts | 8 +- .../src/utils/quote_report_generator.ts | 222 +++++++++++++++++- .../src/utils/quote_requestor.ts | 24 +- .../test/market_operation_utils_test.ts | 6 +- .../test/quote_report_generator_test.ts | 8 +- .../test/quote_requestor_test.ts | 76 +++--- yarn.lock | 1 + 12 files changed, 341 insertions(+), 60 deletions(-) diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index f55c3417b0..9bd6c7b552 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "version": "16.32.0", + "changes": [ + { + "note": "Extended Quote Report", + "pr": 361 + } + ], + "timestamp": 1636480845 + }, "timestamp": 1635903615, "version": "16.31.0", "changes": [ diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts index 0bd9ccac9d..82272123e6 100644 --- a/packages/asset-swapper/src/index.ts +++ b/packages/asset-swapper/src/index.ts @@ -162,14 +162,20 @@ export { export { ProtocolFeeUtils } from './utils/protocol_fee_utils'; export { BridgeQuoteReportEntry, + jsonifyFillData, MultiHopQuoteReportEntry, NativeLimitOrderQuoteReportEntry, NativeRfqOrderQuoteReportEntry, QuoteReport, QuoteReportEntry, + ExtendedQuoteReport, + ExtendedQuoteReportSources, + ExtendedQuoteReportEntry, + ExtendedQuoteReportIndexedEntry, + ExtendedQuoteReportIndexedEntryOutbound, PriceComparisonsReport, } from './utils/quote_report_generator'; -export { QuoteRequestor } from './utils/quote_requestor'; +export { QuoteRequestor, V4RFQIndicativeQuoteMM } from './utils/quote_requestor'; export { ERC20BridgeSamplerContract, BalanceCheckerContract, FakeTakerContract } from './wrappers'; import { ERC20BridgeSource } from './utils/market_operation_utils/types'; export type Native = ERC20BridgeSource.Native; diff --git a/packages/asset-swapper/src/swap_quoter.ts b/packages/asset-swapper/src/swap_quoter.ts index 54006ba846..73b4e44311 100644 --- a/packages/asset-swapper/src/swap_quoter.ts +++ b/packages/asset-swapper/src/swap_quoter.ts @@ -505,6 +505,7 @@ function createSwapQuote( const { optimizedOrders, quoteReport, + extendedQuoteReportSources, sourceFlags, takerAmountPerEth, makerAmountPerEth, @@ -532,6 +533,7 @@ function createSwapQuote( takerAmountPerEth, makerAmountPerEth, quoteReport, + extendedQuoteReportSources, isTwoHop, priceComparisonsReport, }; diff --git a/packages/asset-swapper/src/types.ts b/packages/asset-swapper/src/types.ts index d23213013a..b0dc693e74 100644 --- a/packages/asset-swapper/src/types.ts +++ b/packages/asset-swapper/src/types.ts @@ -19,7 +19,7 @@ import { OptimizedMarketOrder, TokenAdjacencyGraph, } from './utils/market_operation_utils/types'; -import { PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator'; +import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator'; import { MetricsProxy } from './utils/quote_requestor'; /** @@ -171,6 +171,7 @@ export interface SwapQuoteBase { worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown; quoteReport?: QuoteReport; + extendedQuoteReportSources?: ExtendedQuoteReportSources; priceComparisonsReport?: PriceComparisonsReport; isTwoHop: boolean; makerTokenDecimals: number; 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 d3d3b58025..21a02fba73 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -18,12 +18,15 @@ import { import { dexSampleToReportSource, + ExtendedQuoteReportSources, + generateExtendedQuoteReportSources, generateQuoteReport, multiHopSampleToReportSource, nativeOrderToReportEntry, PriceComparisonsReport, QuoteReport, } from './../quote_report_generator'; + import { getComparisonPrices } from './comparison_price'; import { BUY_SOURCE_FILTER_BY_CHAIN_ID, @@ -78,6 +81,25 @@ export class MarketOperationUtils { return generateQuoteReport(side, quotes.nativeOrders, liquidityDelivered, comparisonPrice, quoteRequestor); } + private static _computeExtendedQuoteReportSources( + quoteRequestor: QuoteRequestor | undefined, + marketSideLiquidity: MarketSideLiquidity, + amount: BigNumber, + optimizerResult: OptimizerResult, + comparisonPrice?: BigNumber | undefined, + ): ExtendedQuoteReportSources { + const { side, quotes } = marketSideLiquidity; + const { liquidityDelivered } = optimizerResult; + return generateExtendedQuoteReportSources( + side, + quotes, + liquidityDelivered, + amount, + comparisonPrice, + quoteRequestor, + ); + } + private static _computePriceComparisonsReport( quoteRequestor: QuoteRequestor | undefined, marketSideLiquidity: MarketSideLiquidity, @@ -702,6 +724,16 @@ export class MarketOperationUtils { ); } + // Always compute the Extended Quote Report + let extendedQuoteReportSources: ExtendedQuoteReportSources | undefined; + extendedQuoteReportSources = MarketOperationUtils._computeExtendedQuoteReportSources( + _opts.rfqt ? _opts.rfqt.quoteRequestor : undefined, + marketSideLiquidity, + amount, + optimizerResult, + wholeOrderPrice, + ); + let priceComparisonsReport: PriceComparisonsReport | undefined; if (_opts.shouldIncludePriceComparisonsReport) { priceComparisonsReport = MarketOperationUtils._computePriceComparisonsReport( @@ -710,7 +742,7 @@ export class MarketOperationUtils { wholeOrderPrice, ); } - return { ...optimizerResult, quoteReport, priceComparisonsReport }; + return { ...optimizerResult, quoteReport, extendedQuoteReportSources, 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 84b5a323a7..adb2a275f6 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -3,13 +3,12 @@ import { FillQuoteTransformerOrderType, FillQuoteTransformerRfqOrderInfo, } from '@0x/protocol-utils'; -import { V4RFQIndicativeQuote } from '@0x/quote-server'; import { MarketOperation } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts } from '../../types'; -import { QuoteRequestor } from '../../utils/quote_requestor'; -import { PriceComparisonsReport, QuoteReport } from '../quote_report_generator'; +import { QuoteRequestor, V4RFQIndicativeQuoteMM } from '../../utils/quote_requestor'; +import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from '../quote_report_generator'; import { CollapsedPath } from './path'; import { SourceFilters } from './source_filters'; @@ -491,6 +490,7 @@ export interface OptimizerResult { export interface OptimizerResultWithReport extends OptimizerResult { quoteReport?: QuoteReport; + extendedQuoteReportSources?: ExtendedQuoteReportSources; priceComparisonsReport?: PriceComparisonsReport; } @@ -519,7 +519,7 @@ export interface MarketSideLiquidity { export interface RawQuotes { nativeOrders: NativeOrderWithFillableAmounts[]; - rfqtIndicativeQuotes: V4RFQIndicativeQuote[]; + rfqtIndicativeQuotes: V4RFQIndicativeQuoteMM[]; twoHopQuotes: Array>; dexQuotes: Array>>; } diff --git a/packages/asset-swapper/src/utils/quote_report_generator.ts b/packages/asset-swapper/src/utils/quote_report_generator.ts index e7f2863e32..5ce10dc718 100644 --- a/packages/asset-swapper/src/utils/quote_report_generator.ts +++ b/packages/asset-swapper/src/utils/quote_report_generator.ts @@ -14,8 +14,9 @@ import { NativeFillData, NativeLimitOrderFillData, NativeRfqOrderFillData, + RawQuotes, } from './market_operation_utils/types'; -import { QuoteRequestor } from './quote_requestor'; +import { QuoteRequestor, V4RFQIndicativeQuoteMM } from './quote_requestor'; export interface QuoteReportEntryBase { liquiditySource: ERC20BridgeSource; @@ -36,30 +37,77 @@ export interface NativeLimitOrderQuoteReportEntry extends QuoteReportEntryBase { liquiditySource: ERC20BridgeSource.Native; fillData: NativeFillData; fillableTakerAmount: BigNumber; - isRfqt: false; + isRFQ: false; } export interface NativeRfqOrderQuoteReportEntry extends QuoteReportEntryBase { liquiditySource: ERC20BridgeSource.Native; fillData: NativeFillData; fillableTakerAmount: BigNumber; - isRfqt: true; + isRFQ: true; nativeOrder: RfqOrderFields; makerUri: string; comparisonPrice?: number; } +export interface IndicativeRfqOrderQuoteReportEntry extends QuoteReportEntryBase { + liquiditySource: ERC20BridgeSource.Native; + fillableTakerAmount: BigNumber; + isRFQ: true; + makerUri?: string; + comparisonPrice?: number; +} + export type QuoteReportEntry = | BridgeQuoteReportEntry | MultiHopQuoteReportEntry | NativeLimitOrderQuoteReportEntry | NativeRfqOrderQuoteReportEntry; +export type ExtendedQuoteReportEntry = + | BridgeQuoteReportEntry + | MultiHopQuoteReportEntry + | NativeLimitOrderQuoteReportEntry + | NativeRfqOrderQuoteReportEntry + | IndicativeRfqOrderQuoteReportEntry; + +export type ExtendedQuoteReportIndexedEntry = ExtendedQuoteReportEntry & { + quoteEntryIndex: number; + isDelivered: boolean; +}; + +export type ExtendedQuoteReportIndexedEntryOutbound = Omit & { + fillData?: string; +}; + export interface QuoteReport { sourcesConsidered: QuoteReportEntry[]; sourcesDelivered: QuoteReportEntry[]; } +export interface ExtendedQuoteReportSources { + sourcesConsidered: ExtendedQuoteReportIndexedEntry[]; + sourcesDelivered: ExtendedQuoteReportIndexedEntry[] | undefined; +} + +export interface ExtendedQuoteReport { + quoteId?: string; + taker?: string; + timestamp: number; + firmQuoteReport: boolean; + submissionBy: 'taker' | 'metaTxn' | 'rfqm'; + buyAmount?: string; + sellAmount?: string; + buyTokenAddress: string; + sellTokenAddress: string; + integratorId?: string; + slippageBips?: number; + zeroExTransactionHash?: string; + decodedUniqueId?: string; + sourcesConsidered: ExtendedQuoteReportIndexedEntryOutbound[]; + sourcesDelivered: ExtendedQuoteReportIndexedEntryOutbound[] | undefined; +} + export interface PriceComparisonsReport { dexSources: BridgeQuoteReportEntry[]; multiHopSources: MultiHopQuoteReportEntry[]; @@ -80,7 +128,7 @@ export function generateQuoteReport( const nativeOrderSourcesConsidered = nativeOrders.map(order => nativeOrderToReportEntry(order.type, order as any, order.fillableTakerAmount, comparisonPrice, quoteRequestor), ); - const sourcesConsidered = [...nativeOrderSourcesConsidered.filter(order => order.isRfqt)]; + const sourcesConsidered = [...nativeOrderSourcesConsidered.filter(order => order.isRFQ)]; let sourcesDelivered; if (Array.isArray(liquidityDelivered)) { @@ -116,6 +164,105 @@ export function generateQuoteReport( }; } +/** + * Generates a report of sources considered while computing the optimized + * swap quote, the sources ultimately included in the computed quote. This + * extende version incudes all considered quotes, not only native liquidity. + */ +export function generateExtendedQuoteReportSources( + marketOperation: MarketOperation, + quotes: RawQuotes, + liquidityDelivered: ReadonlyArray | DexSample, + amount: BigNumber, + comparisonPrice?: BigNumber | undefined, + quoteRequestor?: QuoteRequestor, +): ExtendedQuoteReportSources { + const sourcesConsidered: ExtendedQuoteReportEntry[] = []; + + // NativeOrders + sourcesConsidered.push( + ...quotes.nativeOrders.map(order => + nativeOrderToReportEntry( + order.type, + order as any, + order.fillableTakerAmount, + comparisonPrice, + quoteRequestor, + ), + ), + ); + + // IndicativeQuotes + sourcesConsidered.push( + ...quotes.rfqtIndicativeQuotes.map(order => indicativeQuoteToReportEntry(order, comparisonPrice)), + ); + + // MultiHop + sourcesConsidered.push(...quotes.twoHopQuotes.map(quote => multiHopSampleToReportSource(quote, marketOperation))); + + // Dex Quotes + sourcesConsidered.push( + ..._.flatten( + quotes.dexQuotes.map(dex => + dex + .filter(quote => isDexSampleForTotalAmount(quote, marketOperation, amount)) + .map(quote => dexSampleToReportSource(quote, marketOperation)), + ), + ), + ); + const sourcesConsideredIndexed = sourcesConsidered.map( + (quote, index): ExtendedQuoteReportIndexedEntry => { + return { + ...quote, + quoteEntryIndex: index, + isDelivered: false, + }; + }, + ); + let sourcesDelivered; + if (Array.isArray(liquidityDelivered)) { + // create easy way to look up fillable amounts + const nativeOrderSignaturesToFillableAmounts = _.fromPairs( + quotes.nativeOrders.map(o => { + return [_nativeDataToId(o), o.fillableTakerAmount]; + }), + ); + // map sources delivered + sourcesDelivered = liquidityDelivered.map(collapsedFill => { + if (_isNativeOrderFromCollapsedFill(collapsedFill)) { + return nativeOrderToReportEntry( + collapsedFill.type, + collapsedFill.fillData, + nativeOrderSignaturesToFillableAmounts[_nativeDataToId(collapsedFill.fillData)], + comparisonPrice, + quoteRequestor, + ); + } else { + return dexSampleToReportSource(collapsedFill, marketOperation); + } + }); + } else { + sourcesDelivered = [ + // tslint:disable-next-line: no-unnecessary-type-assertion + multiHopSampleToReportSource(liquidityDelivered as DexSample, marketOperation), + ]; + } + const sourcesDeliveredIndexed = sourcesDelivered.map( + (quote, index): ExtendedQuoteReportIndexedEntry => { + return { + ...quote, + quoteEntryIndex: index, + isDelivered: false, + }; + }, + ); + + return { + sourcesConsidered: sourcesConsideredIndexed, + sourcesDelivered: sourcesDeliveredIndexed, + }; +} + function _nativeDataToId(data: { signature: Signature }): string { const { v, r, s } = data.signature; return `${v}${r}${s}`; @@ -153,6 +300,22 @@ export function dexSampleToReportSource(ds: DexSample, marketOperation: MarketOp } } +/** + * Checks if a DEX sample is the one that represents the whole amount requested by taker + * NOTE: this is used for the QuoteReport to filter samples + */ +function isDexSampleForTotalAmount(ds: DexSample, marketOperation: MarketOperation, amount: BigNumber): boolean { + // input and output map to different values + // based on the market operation + if (marketOperation === MarketOperation.Buy) { + return ds.input === amount; + } else if (marketOperation === MarketOperation.Sell) { + return ds.output === amount; + } else { + throw new Error(`Unexpected marketOperation ${marketOperation}`); + } +} + /** * Generates a report sample for a MultiHop source * NOTE: this is used for the QuoteReport and quote price comparison data @@ -208,17 +371,17 @@ export function nativeOrderToReportEntry( }; // if we find this is an rfqt order, label it as such and associate makerUri - const isRfqt = type === FillQuoteTransformerOrderType.Rfq; + const isRFQ = type === FillQuoteTransformerOrderType.Rfq; const rfqtMakerUri = - isRfqt && quoteRequestor ? quoteRequestor.getMakerUriForSignature(fillData.signature) : undefined; + isRFQ && quoteRequestor ? quoteRequestor.getMakerUriForSignature(fillData.signature) : undefined; - if (isRfqt) { + if (isRFQ) { const nativeOrder = fillData.order as RfqOrderFields; // tslint:disable-next-line: no-object-literal-type-assertion return { liquiditySource: ERC20BridgeSource.Native, ...nativeOrderBase, - isRfqt: true, + isRFQ: true, makerUri: rfqtMakerUri || '', ...(comparisonPrice ? { comparisonPrice: comparisonPrice.toNumber() } : {}), nativeOrder, @@ -229,8 +392,49 @@ export function nativeOrderToReportEntry( return { liquiditySource: ERC20BridgeSource.Native, ...nativeOrderBase, - isRfqt: false, + isRFQ: false, fillData, }; } } + +/** + * Generates a report entry for an indicative RFQ Quote + * NOTE: this is used for the QuoteReport and quote price comparison data + */ +export function indicativeQuoteToReportEntry( + order: V4RFQIndicativeQuoteMM, + comparisonPrice?: BigNumber | undefined, +): IndicativeRfqOrderQuoteReportEntry { + const nativeOrderBase = { + makerAmount: order.makerAmount, + takerAmount: order.takerAmount, + fillableTakerAmount: order.takerAmount, + }; + + // tslint:disable-next-line: no-object-literal-type-assertion + return { + liquiditySource: ERC20BridgeSource.Native, + ...nativeOrderBase, + isRFQ: true, + makerUri: order.makerUri, + fillData: {}, + ...(comparisonPrice ? { comparisonPrice: comparisonPrice.toNumber() } : {}), + }; +} + +/** + * For the extended quote report, we output the filldata as JSON + */ +export function jsonifyFillData(source: ExtendedQuoteReportIndexedEntry): ExtendedQuoteReportIndexedEntryOutbound { + return { + ...source, + fillData: JSON.stringify(source.fillData, (key: string, value: any) => { + if (key === '_samplerContract') { + return {}; + } else { + return value; + } + }), + }; +} diff --git a/packages/asset-swapper/src/utils/quote_requestor.ts b/packages/asset-swapper/src/utils/quote_requestor.ts index d203670ad9..332b1d9afb 100644 --- a/packages/asset-swapper/src/utils/quote_requestor.ts +++ b/packages/asset-swapper/src/utils/quote_requestor.ts @@ -39,6 +39,10 @@ interface RfqQuote { makerUri: string; } +export interface V4RFQIndicativeQuoteMM extends V4RFQIndicativeQuote { + makerUri: string; +} + export interface MetricsProxy { /** * Increments a counter that is tracking valid Firm Quotes that are dropped due to low expiration. @@ -343,7 +347,7 @@ export class QuoteRequestor { marketOperation: MarketOperation, comparisonPrice: BigNumber | undefined, options: RfqmRequestOptions, - ): Promise { + ): Promise { const _opts: RfqRequestOpts = { ...constants.DEFAULT_RFQT_REQUEST_OPTS, ...options, @@ -367,7 +371,7 @@ export class QuoteRequestor { marketOperation: MarketOperation, comparisonPrice: BigNumber | undefined, options: RfqRequestOpts, - ): Promise { + ): Promise { const _opts: RfqRequestOpts = { ...constants.DEFAULT_RFQT_REQUEST_OPTS, ...options }; // Originally a takerAddress was required for indicative quotes, but // now we've eliminated that requirement. @0x/quote-server, however, @@ -398,8 +402,8 @@ export class QuoteRequestor { return this._orderSignatureToMakerUri[nativeDataToId({ signature })]; } - private _isValidRfqtIndicativeQuoteResponse(response: V4RFQIndicativeQuote): boolean { - const requiredKeys: Array = [ + private _isValidRfqtIndicativeQuoteResponse(response: V4RFQIndicativeQuoteMM): boolean { + const requiredKeys: Array = [ 'makerAmount', 'takerAmount', 'makerToken', @@ -545,7 +549,10 @@ export class QuoteRequestor { }, }); rfqMakerBlacklist.logTimeoutOrLackThereof(typedMakerUrl.url, latencyMs >= timeoutMs); - return { response: response.data, makerUri: typedMakerUrl.url }; + return { + response: { ...response.data, makerUri: typedMakerUrl.url }, + makerUri: typedMakerUrl.url, + }; } else { if (this._altRfqCreds === undefined) { throw new Error(`don't have credentials for alt MM`); @@ -694,7 +701,6 @@ export class QuoteRequestor { } else { const secondsRemaining = msRemainingUntilExpiration.div(ONE_SECOND_MS); this._metrics?.measureExpirationForValidOrder(isLastLook, order.maker, secondsRemaining); - const takerAmount = new BigNumber(order.takerAmount); const fillRatio = takerAmount.div(assetFillAmount); if (fillRatio.lt(1) && fillRatio.gte(FILL_RATIO_WARNING_LEVEL)) { @@ -744,9 +750,9 @@ export class QuoteRequestor { comparisonPrice: BigNumber | undefined, options: RfqRequestOpts, assetOfferings: RfqMakerAssetOfferings, - ): Promise { + ): Promise { // fetch quotes - const rawQuotes = await this._getQuotesAsync( + const rawQuotes = await this._getQuotesAsync( makerToken, takerToken, assetFillAmount, @@ -758,7 +764,7 @@ export class QuoteRequestor { ); // validate - const validationFunction = (o: V4RFQIndicativeQuote) => this._isValidRfqtIndicativeQuoteResponse(o); + const validationFunction = (o: V4RFQIndicativeQuoteMM) => this._isValidRfqtIndicativeQuoteResponse(o); const validQuotes = rawQuotes.filter(result => { const order = result.response; if (!validationFunction(order)) { diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index 1261244d0d..8f534d235a 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -159,7 +159,11 @@ describe('MarketOperationUtils tests', () => { } else { requestor .setup(r => r.requestRfqtIndicativeQuotesAsync(...args)) - .returns(async () => results.map(r => r.order)) + .returns(async () => + results.map(r => { + return { ...r.order, makerUri: 'https://foo.bar/' }; + }), + ) .verifiable(verifiable); } return requestor; diff --git a/packages/asset-swapper/test/quote_report_generator_test.ts b/packages/asset-swapper/test/quote_report_generator_test.ts index dcd67dab5f..ba482ce4c8 100644 --- a/packages/asset-swapper/test/quote_report_generator_test.ts +++ b/packages/asset-swapper/test/quote_report_generator_test.ts @@ -155,7 +155,7 @@ describe('generateQuoteReport', async () => { makerAmount: rfqtOrder1.order.makerAmount, takerAmount: rfqtOrder1.order.takerAmount, fillableTakerAmount: rfqtOrder1.fillableTakerAmount, - isRfqt: true, + isRFQ: true, makerUri: 'https://rfqt1.provider.club', nativeOrder: rfqtOrder1.order, fillData: { @@ -167,7 +167,7 @@ describe('generateQuoteReport', async () => { makerAmount: rfqtOrder2.order.makerAmount, takerAmount: rfqtOrder2.order.takerAmount, fillableTakerAmount: rfqtOrder2.fillableTakerAmount, - isRfqt: true, + isRFQ: true, makerUri: 'https://rfqt2.provider.club', nativeOrder: rfqtOrder2.order, fillData: { @@ -179,7 +179,7 @@ describe('generateQuoteReport', async () => { makerAmount: orderbookOrder2.order.makerAmount, takerAmount: orderbookOrder2.order.takerAmount, fillableTakerAmount: orderbookOrder2.fillableTakerAmount, - isRfqt: false, + isRFQ: false, fillData: { order: orderbookOrder2.order, } as NativeLimitOrderFillData, @@ -263,7 +263,7 @@ describe('generateQuoteReport', async () => { makerAmount: orderbookOrder1.order.makerAmount, takerAmount: orderbookOrder1.order.takerAmount, fillableTakerAmount: orderbookOrder1.fillableTakerAmount, - isRfqt: false, + isRFQ: false, fillData: { order: orderbookOrder1.order, } as NativeLimitOrderFillData, diff --git a/packages/asset-swapper/test/quote_requestor_test.ts b/packages/asset-swapper/test/quote_requestor_test.ts index 41abba200e..a285da8ae7 100644 --- a/packages/asset-swapper/test/quote_requestor_test.ts +++ b/packages/asset-swapper/test/quote_requestor_test.ts @@ -494,15 +494,18 @@ describe('QuoteRequestor', async () => { expiry: makeThreeMinuteExpiry(), }; + const goodMMUri1 = 'https://1337.0.0.1'; + const goodMMUri2 = 'https://37.0.0.1'; + mockedRequests.push({ ...mockedDefaults, - endpoint: 'https://1337.0.0.1', + endpoint: goodMMUri1, responseData: successfulQuote1, }); // [GOOD] Another Successful response mockedRequests.push({ ...mockedDefaults, - endpoint: 'https://37.0.0.1', + endpoint: goodMMUri2, responseData: successfulQuote1, }); @@ -532,6 +535,16 @@ describe('QuoteRequestor', async () => { responseData: { ...successfulQuote1, takerToken: otherToken1 }, }); + const assetOfferings: { [k: string]: [[string, string]] } = { + 'https://420.0.0.1': [[makerToken, takerToken]], + 'https://421.0.0.1': [[makerToken, takerToken]], + 'https://422.0.0.1': [[makerToken, takerToken]], + 'https://423.0.0.1': [[makerToken, takerToken]], + 'https://424.0.0.1': [[makerToken, takerToken]], + }; + assetOfferings[goodMMUri1] = [[makerToken, takerToken]]; + assetOfferings[goodMMUri2] = [[makerToken, takerToken]]; + return testHelpers.withMockedRfqQuotes( mockedRequests, [], @@ -539,15 +552,7 @@ describe('QuoteRequestor', async () => { async () => { const qr = new QuoteRequestor( {}, // No RFQ-T asset offerings - { - 'https://1337.0.0.1': [[makerToken, takerToken]], - 'https://37.0.0.1': [[makerToken, takerToken]], - 'https://420.0.0.1': [[makerToken, takerToken]], - 'https://421.0.0.1': [[makerToken, takerToken]], - 'https://422.0.0.1': [[makerToken, takerToken]], - 'https://423.0.0.1': [[makerToken, takerToken]], - 'https://424.0.0.1': [[makerToken, takerToken]], - }, + assetOfferings, quoteRequestorHttpClient, ); const resp = await qr.requestRfqmIndicativeQuotesAsync( @@ -572,7 +577,12 @@ describe('QuoteRequestor', async () => { }, }, ); - expect(resp.sort()).to.eql([successfulQuote1, successfulQuote1].sort()); + expect(resp.sort()).to.eql( + [ + { ...successfulQuote1, makerUri: goodMMUri1 }, + { ...successfulQuote1, makerUri: goodMMUri2 }, + ].sort(), + ); }, quoteRequestorHttpClient, ); @@ -622,9 +632,12 @@ describe('QuoteRequestor', async () => { expiry: makeThreeMinuteExpiry(), }; + const goodMMUri1 = 'https://1337.0.0.1'; + const goodMMUri2 = 'https://37.0.0.1'; + mockedRequests.push({ ...mockedDefaults, - endpoint: 'https://1337.0.0.1', + endpoint: goodMMUri1, responseData: successfulQuote1, }); // Test out a bad response code, ensure it doesnt cause throw @@ -655,28 +668,26 @@ describe('QuoteRequestor', async () => { // Another Successful response mockedRequests.push({ ...mockedDefaults, - endpoint: 'https://37.0.0.1', + endpoint: goodMMUri2, responseData: successfulQuote1, }); + const assetOfferings: { [k: string]: [[string, string]] } = { + 'https://420.0.0.1': [[makerToken, takerToken]], + 'https://421.0.0.1': [[makerToken, takerToken]], + 'https://422.0.0.1': [[makerToken, takerToken]], + 'https://423.0.0.1': [[makerToken, takerToken]], + 'https://424.0.0.1': [[makerToken, takerToken]], + }; + assetOfferings[goodMMUri1] = [[makerToken, takerToken]]; + assetOfferings[goodMMUri2] = [[makerToken, takerToken]]; + return testHelpers.withMockedRfqQuotes( mockedRequests, [], RfqQuoteEndpoint.Indicative, async () => { - const qr = new QuoteRequestor( - { - 'https://1337.0.0.1': [[makerToken, takerToken]], - 'https://420.0.0.1': [[makerToken, takerToken]], - 'https://421.0.0.1': [[makerToken, takerToken]], - 'https://422.0.0.1': [[makerToken, takerToken]], - 'https://423.0.0.1': [[makerToken, takerToken]], - 'https://424.0.0.1': [[makerToken, takerToken]], - 'https://37.0.0.1': [[makerToken, takerToken]], - }, - {}, - quoteRequestorHttpClient, - ); + const qr = new QuoteRequestor(assetOfferings, {}, quoteRequestorHttpClient); const resp = await qr.requestRfqtIndicativeQuotesAsync( makerToken, takerToken, @@ -693,7 +704,12 @@ describe('QuoteRequestor', async () => { intentOnFilling: true, }, ); - expect(resp.sort()).to.eql([successfulQuote1, successfulQuote1].sort()); + expect(resp.sort()).to.eql( + [ + { ...successfulQuote1, makerUri: goodMMUri1 }, + { ...successfulQuote1, makerUri: goodMMUri2 }, + ].sort(), + ); }, quoteRequestorHttpClient, ); @@ -784,7 +800,7 @@ describe('QuoteRequestor', async () => { makerEndpointMaxResponseTimeMs: maxTimeoutMs, }, ); - expect(resp.sort()).to.eql([successfulQuote1].sort()); // notice only one result, despite two requests made + expect(resp.sort()).to.eql([{ ...successfulQuote1, makerUri: 'https://1337.0.0.1' }].sort()); // notice only one result, despite two requests made }, quoteRequestorHttpClient, ); @@ -847,7 +863,7 @@ describe('QuoteRequestor', async () => { intentOnFilling: true, }, ); - expect(resp.sort()).to.eql([successfulQuote1].sort()); + expect(resp.sort()).to.eql([{ ...successfulQuote1, makerUri: 'https://1337.0.0.1' }].sort()); }, quoteRequestorHttpClient, ); diff --git a/yarn.lock b/yarn.lock index bbd1eb8fec..ef003db6ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6402,6 +6402,7 @@ fake-merkle-patricia-tree@^1.0.1: fast-abi@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/fast-abi/-/fast-abi-0.0.2.tgz#da5f796fd7c7b0c966d916ee21daae3eca61c07c" + integrity sha512-k/2s63SkFf6jU2LyF6oQC5/N+L90q6VD1wkp2NXo+DSHoTeOJD2Q6Egpcs+bTPODik0CHxjb7lORgsG+QCRq/Q== dependencies: "@mapbox/node-pre-gyp" "^1.0.4" neon-cli "^0.8.0"