Added initial unit tests and implementation
This commit is contained in:
parent
9e42dce5c4
commit
1588c1f362
@ -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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user