Added initial unit tests and implementation
This commit is contained in:
		@@ -57,6 +57,7 @@ export async function getRfqtIndicativeQuotesAsync(
 | 
			
		||||
    takerAssetData: string,
 | 
			
		||||
    marketOperation: MarketOperation,
 | 
			
		||||
    assetFillAmount: BigNumber,
 | 
			
		||||
    comparisonPrice: BigNumber | undefined,
 | 
			
		||||
    opts: Partial<GetMarketOrdersOpts>,
 | 
			
		||||
): Promise<RFQTIndicativeQuote[]> {
 | 
			
		||||
    if (opts.rfqt && opts.rfqt.isIndicative === true && opts.rfqt.quoteRequestor) {
 | 
			
		||||
@@ -65,7 +66,7 @@ export async function getRfqtIndicativeQuotesAsync(
 | 
			
		||||
            takerAssetData,
 | 
			
		||||
            assetFillAmount,
 | 
			
		||||
            marketOperation,
 | 
			
		||||
            undefined,
 | 
			
		||||
            comparisonPrice,
 | 
			
		||||
            opts.rfqt,
 | 
			
		||||
        );
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -589,6 +590,17 @@ export class MarketOperationUtils {
 | 
			
		||||
        // If RFQ liquidity is enabled, make a request to check RFQ liquidity
 | 
			
		||||
        const { rfqt } = _opts;
 | 
			
		||||
        if (rfqt && rfqt.quoteRequestor && marketSideLiquidity.quoteSourceFilters.isAllowed(ERC20BridgeSource.Native)) {
 | 
			
		||||
 | 
			
		||||
            // Calculate a suggested price. For now, this is simply the overall price of the aggregation.
 | 
			
		||||
            let comparisonPrice: BigNumber | undefined;
 | 
			
		||||
            if (optimizerResult) {
 | 
			
		||||
                const totalMakerAmount = BigNumber.sum(...optimizerResult.optimizedOrders.map(order => order.makerAssetAmount));
 | 
			
		||||
                const totalTakerAmount = BigNumber.sum(...optimizerResult.optimizedOrders.map(order => order.takerAssetAmount));
 | 
			
		||||
                if (totalTakerAmount.gt(0)) {
 | 
			
		||||
                    comparisonPrice = totalMakerAmount.div(totalTakerAmount);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // If we are making an indicative quote, make the RFQT request and then re-run the sampler if new orders come back.
 | 
			
		||||
            if (rfqt.isIndicative) {
 | 
			
		||||
                const indicativeQuotes = await getRfqtIndicativeQuotesAsync(
 | 
			
		||||
@@ -596,6 +608,7 @@ export class MarketOperationUtils {
 | 
			
		||||
                    nativeOrders[0].takerAssetData,
 | 
			
		||||
                    side,
 | 
			
		||||
                    amount,
 | 
			
		||||
                    comparisonPrice,
 | 
			
		||||
                    _opts,
 | 
			
		||||
                );
 | 
			
		||||
                // Re-run optimizer with the new indicative quote
 | 
			
		||||
@@ -621,7 +634,7 @@ export class MarketOperationUtils {
 | 
			
		||||
                        nativeOrders[0].takerAssetData,
 | 
			
		||||
                        amount,
 | 
			
		||||
                        side,
 | 
			
		||||
                        undefined,
 | 
			
		||||
                        comparisonPrice,
 | 
			
		||||
                        rfqt,
 | 
			
		||||
                    );
 | 
			
		||||
                    if (firmQuotes.length > 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ import {
 | 
			
		||||
import { createFillPaths } from '../src/utils/market_operation_utils/fills';
 | 
			
		||||
import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler';
 | 
			
		||||
import { BATCH_SOURCE_FILTERS } from '../src/utils/market_operation_utils/sampler_operations';
 | 
			
		||||
import { SourceFilters } from '../src/utils/market_operation_utils/source_filters';
 | 
			
		||||
import {
 | 
			
		||||
    AggregationError,
 | 
			
		||||
    DexSample,
 | 
			
		||||
@@ -471,6 +472,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                TAKER_ASSET_DATA,
 | 
			
		||||
                MarketOperation.Sell,
 | 
			
		||||
                new BigNumber('100e18'),
 | 
			
		||||
                undefined,
 | 
			
		||||
                {
 | 
			
		||||
                    rfqt: { quoteRequestor: requestor.object, ...partialRfqt },
 | 
			
		||||
                },
 | 
			
		||||
@@ -699,6 +701,106 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                mockedMarketOpUtils.verifyAll();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('optimizer will send in a comparison price to RFQ providers', async () => {
 | 
			
		||||
 | 
			
		||||
                // Set up mocked quote requestor, will return an order that is better
 | 
			
		||||
                // than the best of the orders.
 | 
			
		||||
                const mockedQuoteRequestor = TypeMoq.Mock.ofType(
 | 
			
		||||
                    QuoteRequestor,
 | 
			
		||||
                    TypeMoq.MockBehavior.Loose,
 | 
			
		||||
                    false,
 | 
			
		||||
                    {},
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                let requestedComparisonPrice: BigNumber | undefined;
 | 
			
		||||
                mockedQuoteRequestor.setup(
 | 
			
		||||
                    mqr => mqr.requestRfqtFirmQuotesAsync(
 | 
			
		||||
                        TypeMoq.It.isAny(),
 | 
			
		||||
                        TypeMoq.It.isAny(),
 | 
			
		||||
                        TypeMoq.It.isAny(),
 | 
			
		||||
                        TypeMoq.It.isAny(),
 | 
			
		||||
                        TypeMoq.It.isAny(),
 | 
			
		||||
                        TypeMoq.It.isAny(),
 | 
			
		||||
                    )
 | 
			
		||||
                ).callback((
 | 
			
		||||
                    _makerAssetData: string,
 | 
			
		||||
                    _takerAssetData: string,
 | 
			
		||||
                    _assetFillAmount: BigNumber,
 | 
			
		||||
                    _marketOperation: MarketOperation,
 | 
			
		||||
                    comparisonPrice: BigNumber | undefined,
 | 
			
		||||
                    _options: RfqtRequestOpts,
 | 
			
		||||
                ) => {
 | 
			
		||||
                    requestedComparisonPrice = comparisonPrice;
 | 
			
		||||
                }).returns(async () => {
 | 
			
		||||
                    return [
 | 
			
		||||
                        {
 | 
			
		||||
                            signedOrder: createOrder({
 | 
			
		||||
                                makerAssetData: MAKER_ASSET_DATA,
 | 
			
		||||
                                takerAssetData: TAKER_ASSET_DATA,
 | 
			
		||||
                                makerAssetAmount: Web3Wrapper.toBaseUnitAmount(321, 18),
 | 
			
		||||
                                takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 18),
 | 
			
		||||
                            }),
 | 
			
		||||
                        },
 | 
			
		||||
                    ];
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Set up sampler, will only return 1 on-chain order
 | 
			
		||||
                const mockedMarketOpUtils = TypeMoq.Mock.ofType(
 | 
			
		||||
                    MarketOperationUtils,
 | 
			
		||||
                    TypeMoq.MockBehavior.Loose,
 | 
			
		||||
                    false,
 | 
			
		||||
                    MOCK_SAMPLER,
 | 
			
		||||
                    contractAddresses,
 | 
			
		||||
                    ORDER_DOMAIN,
 | 
			
		||||
                );
 | 
			
		||||
                mockedMarketOpUtils.callBase = true;
 | 
			
		||||
                mockedMarketOpUtils.setup(
 | 
			
		||||
                    mou => mou.getMarketSellLiquidityAsync(
 | 
			
		||||
                        TypeMoq.It.isAny(),
 | 
			
		||||
                        TypeMoq.It.isAny(),
 | 
			
		||||
                        TypeMoq.It.isAny(),
 | 
			
		||||
                    )
 | 
			
		||||
                ).returns(async () => {
 | 
			
		||||
                    return {
 | 
			
		||||
                        dexQuotes: [],
 | 
			
		||||
                        ethToInputRate: Web3Wrapper.toBaseUnitAmount(1, 18),
 | 
			
		||||
                        ethToOutputRate: Web3Wrapper.toBaseUnitAmount(1, 18),
 | 
			
		||||
                        inputAmount: Web3Wrapper.toBaseUnitAmount(1, 18),
 | 
			
		||||
                        inputToken: MAKER_TOKEN,
 | 
			
		||||
                        outputToken: TAKER_TOKEN,
 | 
			
		||||
                        nativeOrders: [
 | 
			
		||||
                            createOrder({
 | 
			
		||||
                                makerAssetData: MAKER_ASSET_DATA,
 | 
			
		||||
                                takerAssetData: TAKER_ASSET_DATA,
 | 
			
		||||
                                makerAssetAmount: Web3Wrapper.toBaseUnitAmount(320, 18),
 | 
			
		||||
                                takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 18),
 | 
			
		||||
                            }),
 | 
			
		||||
                        ],
 | 
			
		||||
                        orderFillableAmounts: [Web3Wrapper.toBaseUnitAmount(1, 18)],
 | 
			
		||||
                        rfqtIndicativeQuotes: [],
 | 
			
		||||
                        side: MarketOperation.Sell,
 | 
			
		||||
                        twoHopQuotes: [],
 | 
			
		||||
                        quoteSourceFilters: new SourceFilters(),
 | 
			
		||||
                    };
 | 
			
		||||
                })
 | 
			
		||||
                const result = await mockedMarketOpUtils.object.getMarketSellOrdersAsync(ORDERS, Web3Wrapper.toBaseUnitAmount(1, 18), {
 | 
			
		||||
                    ...DEFAULT_OPTS,
 | 
			
		||||
                    rfqt: {
 | 
			
		||||
                        isIndicative: false,
 | 
			
		||||
                        apiKey: 'foo',
 | 
			
		||||
                        takerAddress: randomAddress(),
 | 
			
		||||
                        intentOnFilling: true,
 | 
			
		||||
                        quoteRequestor: {
 | 
			
		||||
                            requestRfqtFirmQuotesAsync: mockedQuoteRequestor.object.requestRfqtFirmQuotesAsync,
 | 
			
		||||
                        } as any,
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                expect(result.optimizedOrders.length).to.eql(1);
 | 
			
		||||
                expect(requestedComparisonPrice!.toString()).to.eql("320");
 | 
			
		||||
                expect(result.optimizedOrders[0].makerAssetAmount.toString()).to.eql('321000000000000000000');
 | 
			
		||||
                expect(result.optimizedOrders[0].takerAssetAmount.toString()).to.eql('1000000000000000000');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('getMarketSellOrdersAsync() will not rerun the optimizer if no orders are returned', async () => {
 | 
			
		||||
                // Ensure that `_generateOptimizedOrdersAsync` is only called once
 | 
			
		||||
                const mockedMarketOpUtils = TypeMoq.Mock.ofType(
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user