Revive quote report (#184)

* Revives Quote Report

* prettier

* Remove unused parameters

* updated a few issues with tests

* Remove old code

* Fixed other unit tests
This commit is contained in:
Daniel Pyrathon 2021-03-30 09:57:03 -07:00 committed by GitHub
parent 24397c51a8
commit 525bc8197b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 29 additions and 123 deletions

View File

@ -661,6 +661,6 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
gasSchedule: DEFAULT_GAS_SCHEDULE, gasSchedule: DEFAULT_GAS_SCHEDULE,
exchangeProxyOverhead: () => ZERO_AMOUNT, exchangeProxyOverhead: () => ZERO_AMOUNT,
allowFallback: true, allowFallback: true,
shouldGenerateQuoteReport: false, shouldGenerateQuoteReport: true,
tokenAdjacencyGraph: { default: [] }, tokenAdjacencyGraph: { default: [] },
}; };

View File

@ -62,17 +62,8 @@ export class MarketOperationUtils {
comparisonPrice?: BigNumber | undefined, comparisonPrice?: BigNumber | undefined,
): QuoteReport { ): QuoteReport {
const { side, quotes } = marketSideLiquidity; const { side, quotes } = marketSideLiquidity;
const { dexQuotes, twoHopQuotes, nativeOrders } = quotes;
const { liquidityDelivered } = optimizerResult; const { liquidityDelivered } = optimizerResult;
return generateQuoteReport( return generateQuoteReport(side, quotes.nativeOrders, liquidityDelivered, comparisonPrice, quoteRequestor);
side,
_.flatten(dexQuotes),
twoHopQuotes,
nativeOrders,
liquidityDelivered,
comparisonPrice,
quoteRequestor,
);
} }
constructor( constructor(

View File

@ -1,4 +1,4 @@
import { FillQuoteTransformerOrderType, Signature } from '@0x/protocol-utils'; import { FillQuoteTransformerOrderType, RfqOrderFields, Signature } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import _ = require('lodash'); import _ = require('lodash');
@ -44,6 +44,7 @@ export interface NativeRfqOrderQuoteReportEntry extends QuoteReportEntryBase {
fillData: NativeFillData; fillData: NativeFillData;
fillableTakerAmount: BigNumber; fillableTakerAmount: BigNumber;
isRfqt: true; isRfqt: true;
nativeOrder: RfqOrderFields;
makerUri: string; makerUri: string;
comparisonPrice?: number; comparisonPrice?: number;
} }
@ -65,25 +66,15 @@ export interface QuoteReport {
*/ */
export function generateQuoteReport( export function generateQuoteReport(
marketOperation: MarketOperation, marketOperation: MarketOperation,
dexQuotes: DexSample[],
multiHopQuotes: Array<DexSample<MultiHopFillData>>,
nativeOrders: NativeOrderWithFillableAmounts[], nativeOrders: NativeOrderWithFillableAmounts[],
liquidityDelivered: ReadonlyArray<CollapsedFill> | DexSample<MultiHopFillData>, liquidityDelivered: ReadonlyArray<CollapsedFill> | DexSample<MultiHopFillData>,
comparisonPrice?: BigNumber | undefined, comparisonPrice?: BigNumber | undefined,
quoteRequestor?: QuoteRequestor, quoteRequestor?: QuoteRequestor,
): QuoteReport { ): QuoteReport {
const dexReportSourcesConsidered = dexQuotes.map(quote => _dexSampleToReportSource(quote, marketOperation));
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 multiHopSourcesConsidered = multiHopQuotes.map(quote => const sourcesConsidered = [...nativeOrderSourcesConsidered.filter(order => order.isRfqt)];
_multiHopSampleToReportSource(quote, marketOperation),
);
const sourcesConsidered = [
...dexReportSourcesConsidered,
...nativeOrderSourcesConsidered,
...multiHopSourcesConsidered,
];
let sourcesDelivered; let sourcesDelivered;
if (Array.isArray(liquidityDelivered)) { if (Array.isArray(liquidityDelivered)) {
@ -193,7 +184,6 @@ function _nativeOrderToReportEntry(
quoteRequestor?: QuoteRequestor, quoteRequestor?: QuoteRequestor,
): NativeRfqOrderQuoteReportEntry | NativeLimitOrderQuoteReportEntry { ): NativeRfqOrderQuoteReportEntry | NativeLimitOrderQuoteReportEntry {
const nativeOrderBase = { const nativeOrderBase = {
liquiditySource: ERC20BridgeSource.Native,
makerAmount: fillData.order.makerAmount, makerAmount: fillData.order.makerAmount,
takerAmount: fillData.order.takerAmount, takerAmount: fillData.order.takerAmount,
fillableTakerAmount: fillableAmount, fillableTakerAmount: fillableAmount,
@ -201,23 +191,28 @@ function _nativeOrderToReportEntry(
// if we find this is an rfqt order, label it as such and associate makerUri // if we find this is an rfqt order, label it as such and associate makerUri
const isRfqt = type === FillQuoteTransformerOrderType.Rfq; const isRfqt = type === FillQuoteTransformerOrderType.Rfq;
const rfqtMakerUri = isRfqt ? quoteRequestor!.getMakerUriForSignature(fillData.signature) : undefined; const rfqtMakerUri =
isRfqt && quoteRequestor ? quoteRequestor.getMakerUriForSignature(fillData.signature) : undefined;
if (isRfqt) { if (isRfqt) {
const nativeOrder = fillData.order as RfqOrderFields;
// tslint:disable-next-line: no-object-literal-type-assertion // tslint:disable-next-line: no-object-literal-type-assertion
return { return {
liquiditySource: ERC20BridgeSource.Native,
...nativeOrderBase, ...nativeOrderBase,
isRfqt: true, isRfqt: true,
makerUri: rfqtMakerUri || '', makerUri: rfqtMakerUri || '',
...(comparisonPrice ? { comparisonPrice: comparisonPrice.toNumber() } : {}), ...(comparisonPrice ? { comparisonPrice: comparisonPrice.toNumber() } : {}),
nativeOrder,
fillData, fillData,
} as NativeRfqOrderQuoteReportEntry; };
} else { } else {
// tslint:disable-next-line: no-object-literal-type-assertion // tslint:disable-next-line: no-object-literal-type-assertion
return { return {
liquiditySource: ERC20BridgeSource.Native,
...nativeOrderBase, ...nativeOrderBase,
isRfqt: false, isRfqt: false,
fillData, fillData,
} as NativeLimitOrderQuoteReportEntry; };
} }
} }

View File

@ -127,6 +127,9 @@ describe('MarketOperationUtils tests', () => {
TypeMoq.It.isAny(), TypeMoq.It.isAny(),
]; ];
const requestor = TypeMoq.Mock.ofType(QuoteRequestor, TypeMoq.MockBehavior.Loose, true); const requestor = TypeMoq.Mock.ofType(QuoteRequestor, TypeMoq.MockBehavior.Loose, true);
requestor
.setup(mqr => mqr.getMakerUriForSignature(TypeMoq.It.isValue(SIGNATURE)))
.returns(() => 'https://foo.bar');
if (type === 'firm') { if (type === 'firm') {
requestor requestor
.setup(r => r.requestRfqtFirmQuotesAsync(...args)) .setup(r => r.requestRfqtFirmQuotesAsync(...args))
@ -696,6 +699,9 @@ describe('MarketOperationUtils tests', () => {
const feeSchedule = { const feeSchedule = {
[ERC20BridgeSource.Native]: _.constant(new BigNumber(1)), [ERC20BridgeSource.Native]: _.constant(new BigNumber(1)),
}; };
mockedQuoteRequestor
.setup(mqr => mqr.getMakerUriForSignature(TypeMoq.It.isValue(SIGNATURE)))
.returns(() => 'https://foo.bar');
mockedQuoteRequestor mockedQuoteRequestor
.setup(mqr => .setup(mqr =>
mqr.requestRfqtFirmQuotesAsync( mqr.requestRfqtFirmQuotesAsync(
@ -799,6 +805,7 @@ describe('MarketOperationUtils tests', () => {
intentOnFilling: true, intentOnFilling: true,
quoteRequestor: { quoteRequestor: {
requestRfqtFirmQuotesAsync: mockedQuoteRequestor.object.requestRfqtFirmQuotesAsync, requestRfqtFirmQuotesAsync: mockedQuoteRequestor.object.requestRfqtFirmQuotesAsync,
getMakerUriForSignature: mockedQuoteRequestor.object.getMakerUriForSignature,
} as any, } as any,
}, },
}, },

View File

@ -58,32 +58,18 @@ describe('generateQuoteReport', async () => {
it('should generate report properly for sell', () => { it('should generate report properly for sell', () => {
const marketOperation: MarketOperation = MarketOperation.Sell; const marketOperation: MarketOperation = MarketOperation.Sell;
const kyberSample1: DexSample = {
source: ERC20BridgeSource.Kyber,
input: new BigNumber(10000),
output: new BigNumber(10001),
fillData: {},
};
const kyberSample2: DexSample = { const kyberSample2: DexSample = {
source: ERC20BridgeSource.Kyber, source: ERC20BridgeSource.Kyber,
input: new BigNumber(10003), input: new BigNumber(10003),
output: new BigNumber(10004), output: new BigNumber(10004),
fillData: {}, fillData: {},
}; };
const uniswapSample1: DexSample = {
source: ERC20BridgeSource.UniswapV2,
input: new BigNumber(10003),
output: new BigNumber(10004),
fillData: {},
};
const uniswapSample2: DexSample = { const uniswapSample2: DexSample = {
source: ERC20BridgeSource.UniswapV2, source: ERC20BridgeSource.UniswapV2,
input: new BigNumber(10005), input: new BigNumber(10005),
output: new BigNumber(10006), output: new BigNumber(10006),
fillData: {}, fillData: {},
}; };
const dexQuotes: DexSample[] = [kyberSample1, kyberSample2, uniswapSample1, uniswapSample2];
const orderbookOrder1: NativeOrderWithFillableAmounts = { const orderbookOrder1: NativeOrderWithFillableAmounts = {
order: new LimitOrder({ takerAmount: new BigNumber(1000) }), order: new LimitOrder({ takerAmount: new BigNumber(1000) }),
type: FillQuoteTransformerOrderType.Limit, type: FillQuoteTransformerOrderType.Limit,
@ -158,8 +144,6 @@ describe('generateQuoteReport', async () => {
const orderReport = generateQuoteReport( const orderReport = generateQuoteReport(
marketOperation, marketOperation,
dexQuotes,
[],
nativeOrders, nativeOrders,
pathGenerated, pathGenerated,
undefined, undefined,
@ -173,6 +157,7 @@ describe('generateQuoteReport', async () => {
fillableTakerAmount: rfqtOrder1.fillableTakerAmount, fillableTakerAmount: rfqtOrder1.fillableTakerAmount,
isRfqt: true, isRfqt: true,
makerUri: 'https://rfqt1.provider.club', makerUri: 'https://rfqt1.provider.club',
nativeOrder: rfqtOrder1.order,
fillData: { fillData: {
order: rfqtOrder1.order, order: rfqtOrder1.order,
} as NativeRfqOrderFillData, } as NativeRfqOrderFillData,
@ -184,20 +169,11 @@ describe('generateQuoteReport', async () => {
fillableTakerAmount: rfqtOrder2.fillableTakerAmount, fillableTakerAmount: rfqtOrder2.fillableTakerAmount,
isRfqt: true, isRfqt: true,
makerUri: 'https://rfqt2.provider.club', makerUri: 'https://rfqt2.provider.club',
nativeOrder: rfqtOrder2.order,
fillData: { fillData: {
order: rfqtOrder2.order, order: rfqtOrder2.order,
} as NativeRfqOrderFillData, } as NativeRfqOrderFillData,
}; };
const orderbookOrder1Source: NativeLimitOrderQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.Native,
makerAmount: orderbookOrder1.order.makerAmount,
takerAmount: orderbookOrder1.order.takerAmount,
fillableTakerAmount: orderbookOrder1.fillableTakerAmount,
isRfqt: false,
fillData: {
order: orderbookOrder1.order,
} as NativeLimitOrderFillData,
};
const orderbookOrder2Source: NativeLimitOrderQuoteReportEntry = { const orderbookOrder2Source: NativeLimitOrderQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.Native, liquiditySource: ERC20BridgeSource.Native,
makerAmount: orderbookOrder2.order.makerAmount, makerAmount: orderbookOrder2.order.makerAmount,
@ -208,24 +184,12 @@ describe('generateQuoteReport', async () => {
order: orderbookOrder2.order, order: orderbookOrder2.order,
} as NativeLimitOrderFillData, } as NativeLimitOrderFillData,
}; };
const uniswap1Source: BridgeQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.UniswapV2,
makerAmount: uniswapSample1.output,
takerAmount: uniswapSample1.input,
fillData: {},
};
const uniswap2Source: BridgeQuoteReportEntry = { const uniswap2Source: BridgeQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.UniswapV2, liquiditySource: ERC20BridgeSource.UniswapV2,
makerAmount: uniswapSample2.output, makerAmount: uniswapSample2.output,
takerAmount: uniswapSample2.input, takerAmount: uniswapSample2.input,
fillData: {}, fillData: {},
}; };
const kyber1Source: BridgeQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.Kyber,
makerAmount: kyberSample1.output,
takerAmount: kyberSample1.input,
fillData: {},
};
const kyber2Source: BridgeQuoteReportEntry = { const kyber2Source: BridgeQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.Kyber, liquiditySource: ERC20BridgeSource.Kyber,
makerAmount: kyberSample2.output, makerAmount: kyberSample2.output,
@ -233,16 +197,7 @@ describe('generateQuoteReport', async () => {
fillData: {}, fillData: {},
}; };
const expectedSourcesConsidered: QuoteReportEntry[] = [ const expectedSourcesConsidered: QuoteReportEntry[] = [rfqtOrder1Source, rfqtOrder2Source];
kyber1Source,
kyber2Source,
uniswap1Source,
uniswap2Source,
orderbookOrder1Source,
rfqtOrder1Source,
rfqtOrder2Source,
orderbookOrder2Source,
];
const expectedSourcesDelivered: QuoteReportEntry[] = [ const expectedSourcesDelivered: QuoteReportEntry[] = [
rfqtOrder2Source, rfqtOrder2Source,
orderbookOrder2Source, orderbookOrder2Source,
@ -267,7 +222,6 @@ describe('generateQuoteReport', async () => {
output: new BigNumber(10004), output: new BigNumber(10004),
fillData: {}, fillData: {},
}; };
const dexQuotes: DexSample[] = [kyberSample1, uniswapSample1];
const orderbookOrder1: NativeOrderWithFillableAmounts = { const orderbookOrder1: NativeOrderWithFillableAmounts = {
order: new LimitOrder({ takerAmount: new BigNumber(1101) }), order: new LimitOrder({ takerAmount: new BigNumber(1101) }),
type: FillQuoteTransformerOrderType.Limit, type: FillQuoteTransformerOrderType.Limit,
@ -302,7 +256,7 @@ describe('generateQuoteReport', async () => {
}; };
const pathGenerated: CollapsedFill[] = [orderbookOrder1Fill, uniswap1Fill, kyber1Fill]; const pathGenerated: CollapsedFill[] = [orderbookOrder1Fill, uniswap1Fill, kyber1Fill];
const orderReport = generateQuoteReport(marketOperation, dexQuotes, [], nativeOrders, pathGenerated); const orderReport = generateQuoteReport(marketOperation, nativeOrders, pathGenerated);
const orderbookOrder1Source: NativeLimitOrderQuoteReportEntry = { const orderbookOrder1Source: NativeLimitOrderQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.Native, liquiditySource: ERC20BridgeSource.Native,
@ -314,16 +268,6 @@ describe('generateQuoteReport', async () => {
order: orderbookOrder1.order, order: orderbookOrder1.order,
} as NativeLimitOrderFillData, } as NativeLimitOrderFillData,
}; };
const orderbookOrder2Source: NativeLimitOrderQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.Native,
makerAmount: orderbookOrder2.order.makerAmount,
takerAmount: orderbookOrder2.order.takerAmount,
fillableTakerAmount: orderbookOrder2.fillableTakerAmount,
isRfqt: false,
fillData: {
order: orderbookOrder2.order,
} as NativeLimitOrderFillData,
};
const uniswap1Source: BridgeQuoteReportEntry = { const uniswap1Source: BridgeQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.UniswapV2, liquiditySource: ERC20BridgeSource.UniswapV2,
makerAmount: uniswapSample1.input, makerAmount: uniswapSample1.input,
@ -337,24 +281,14 @@ describe('generateQuoteReport', async () => {
fillData: {}, fillData: {},
}; };
const expectedSourcesConsidered: QuoteReportEntry[] = [ // No order is considered here because only Native RFQ orders are considered.
kyber1Source, const expectedSourcesConsidered: QuoteReportEntry[] = [];
uniswap1Source,
orderbookOrder1Source,
orderbookOrder2Source,
];
const expectedSourcesDelivered: QuoteReportEntry[] = [orderbookOrder1Source, uniswap1Source, kyber1Source]; const expectedSourcesDelivered: QuoteReportEntry[] = [orderbookOrder1Source, uniswap1Source, kyber1Source];
expectEqualQuoteReportEntries(orderReport.sourcesConsidered, expectedSourcesConsidered, `sourcesConsidered`); expectEqualQuoteReportEntries(orderReport.sourcesConsidered, expectedSourcesConsidered, `sourcesConsidered`);
expectEqualQuoteReportEntries(orderReport.sourcesDelivered, expectedSourcesDelivered, `sourcesDelivered`); expectEqualQuoteReportEntries(orderReport.sourcesDelivered, expectedSourcesDelivered, `sourcesDelivered`);
}); });
it('should correctly generate report for a two-hop quote', () => { it('should correctly generate report for a two-hop quote', () => {
const marketOperation: MarketOperation = MarketOperation.Sell; const marketOperation: MarketOperation = MarketOperation.Sell;
const kyberSample1: DexSample = {
source: ERC20BridgeSource.Kyber,
input: new BigNumber(10000),
output: new BigNumber(10001),
fillData: {},
};
const orderbookOrder1: NativeOrderWithFillableAmounts = { const orderbookOrder1: NativeOrderWithFillableAmounts = {
order: new LimitOrder({ takerAmount: new BigNumber(1101) }), order: new LimitOrder({ takerAmount: new BigNumber(1101) }),
type: FillQuoteTransformerOrderType.Limit, type: FillQuoteTransformerOrderType.Limit,
@ -387,29 +321,7 @@ describe('generateQuoteReport', async () => {
fillData: twoHopFillData, fillData: twoHopFillData,
}; };
const orderReport = generateQuoteReport( const orderReport = generateQuoteReport(marketOperation, [orderbookOrder1], twoHopSample);
marketOperation,
[kyberSample1],
[twoHopSample],
[orderbookOrder1],
twoHopSample,
);
const orderbookOrder1Source: NativeLimitOrderQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.Native,
makerAmount: orderbookOrder1.order.makerAmount,
takerAmount: orderbookOrder1.order.takerAmount,
fillableTakerAmount: orderbookOrder1.fillableTakerAmount,
isRfqt: false,
fillData: {
order: orderbookOrder1.order,
} as NativeLimitOrderFillData,
};
const kyber1Source: BridgeQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.Kyber,
makerAmount: kyberSample1.output,
takerAmount: kyberSample1.input,
fillData: {},
};
const twoHopSource: MultiHopQuoteReportEntry = { const twoHopSource: MultiHopQuoteReportEntry = {
liquiditySource: ERC20BridgeSource.MultiHop, liquiditySource: ERC20BridgeSource.MultiHop,
makerAmount: twoHopSample.output, makerAmount: twoHopSample.output,
@ -418,7 +330,8 @@ describe('generateQuoteReport', async () => {
fillData: twoHopFillData, fillData: twoHopFillData,
}; };
const expectedSourcesConsidered: QuoteReportEntry[] = [kyber1Source, orderbookOrder1Source, twoHopSource]; // No entry is present in considered because No RFQ orders were reported.
const expectedSourcesConsidered: QuoteReportEntry[] = [];
expectEqualQuoteReportEntries(orderReport.sourcesConsidered, expectedSourcesConsidered, `sourcesConsidered`); expectEqualQuoteReportEntries(orderReport.sourcesConsidered, expectedSourcesConsidered, `sourcesConsidered`);
expect(orderReport.sourcesDelivered.length).to.eql(1); expect(orderReport.sourcesDelivered.length).to.eql(1);
expect(orderReport.sourcesDelivered[0]).to.deep.equal(twoHopSource); expect(orderReport.sourcesDelivered[0]).to.deep.equal(twoHopSource);