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,
|
takerAssetData: string,
|
||||||
marketOperation: MarketOperation,
|
marketOperation: MarketOperation,
|
||||||
assetFillAmount: BigNumber,
|
assetFillAmount: BigNumber,
|
||||||
|
comparisonPrice: BigNumber | undefined,
|
||||||
opts: Partial<GetMarketOrdersOpts>,
|
opts: Partial<GetMarketOrdersOpts>,
|
||||||
): Promise<RFQTIndicativeQuote[]> {
|
): Promise<RFQTIndicativeQuote[]> {
|
||||||
if (opts.rfqt && opts.rfqt.isIndicative === true && opts.rfqt.quoteRequestor) {
|
if (opts.rfqt && opts.rfqt.isIndicative === true && opts.rfqt.quoteRequestor) {
|
||||||
@ -65,7 +66,7 @@ export async function getRfqtIndicativeQuotesAsync(
|
|||||||
takerAssetData,
|
takerAssetData,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
marketOperation,
|
marketOperation,
|
||||||
undefined,
|
comparisonPrice,
|
||||||
opts.rfqt,
|
opts.rfqt,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -589,6 +590,17 @@ export class MarketOperationUtils {
|
|||||||
// If RFQ liquidity is enabled, make a request to check RFQ liquidity
|
// If RFQ liquidity is enabled, make a request to check RFQ liquidity
|
||||||
const { rfqt } = _opts;
|
const { rfqt } = _opts;
|
||||||
if (rfqt && rfqt.quoteRequestor && marketSideLiquidity.quoteSourceFilters.isAllowed(ERC20BridgeSource.Native)) {
|
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 we are making an indicative quote, make the RFQT request and then re-run the sampler if new orders come back.
|
||||||
if (rfqt.isIndicative) {
|
if (rfqt.isIndicative) {
|
||||||
const indicativeQuotes = await getRfqtIndicativeQuotesAsync(
|
const indicativeQuotes = await getRfqtIndicativeQuotesAsync(
|
||||||
@ -596,6 +608,7 @@ export class MarketOperationUtils {
|
|||||||
nativeOrders[0].takerAssetData,
|
nativeOrders[0].takerAssetData,
|
||||||
side,
|
side,
|
||||||
amount,
|
amount,
|
||||||
|
comparisonPrice,
|
||||||
_opts,
|
_opts,
|
||||||
);
|
);
|
||||||
// Re-run optimizer with the new indicative quote
|
// Re-run optimizer with the new indicative quote
|
||||||
@ -621,7 +634,7 @@ export class MarketOperationUtils {
|
|||||||
nativeOrders[0].takerAssetData,
|
nativeOrders[0].takerAssetData,
|
||||||
amount,
|
amount,
|
||||||
side,
|
side,
|
||||||
undefined,
|
comparisonPrice,
|
||||||
rfqt,
|
rfqt,
|
||||||
);
|
);
|
||||||
if (firmQuotes.length > 0) {
|
if (firmQuotes.length > 0) {
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
import { createFillPaths } from '../src/utils/market_operation_utils/fills';
|
import { createFillPaths } from '../src/utils/market_operation_utils/fills';
|
||||||
import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler';
|
import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler';
|
||||||
import { BATCH_SOURCE_FILTERS } from '../src/utils/market_operation_utils/sampler_operations';
|
import { BATCH_SOURCE_FILTERS } from '../src/utils/market_operation_utils/sampler_operations';
|
||||||
|
import { SourceFilters } from '../src/utils/market_operation_utils/source_filters';
|
||||||
import {
|
import {
|
||||||
AggregationError,
|
AggregationError,
|
||||||
DexSample,
|
DexSample,
|
||||||
@ -471,6 +472,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
TAKER_ASSET_DATA,
|
TAKER_ASSET_DATA,
|
||||||
MarketOperation.Sell,
|
MarketOperation.Sell,
|
||||||
new BigNumber('100e18'),
|
new BigNumber('100e18'),
|
||||||
|
undefined,
|
||||||
{
|
{
|
||||||
rfqt: { quoteRequestor: requestor.object, ...partialRfqt },
|
rfqt: { quoteRequestor: requestor.object, ...partialRfqt },
|
||||||
},
|
},
|
||||||
@ -699,6 +701,106 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
mockedMarketOpUtils.verifyAll();
|
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 () => {
|
it('getMarketSellOrdersAsync() will not rerun the optimizer if no orders are returned', async () => {
|
||||||
// Ensure that `_generateOptimizedOrdersAsync` is only called once
|
// Ensure that `_generateOptimizedOrdersAsync` is only called once
|
||||||
const mockedMarketOpUtils = TypeMoq.Mock.ofType(
|
const mockedMarketOpUtils = TypeMoq.Mock.ofType(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user