fix: Changed price-aware RFQ flag to be a argument parameter (#13)
* Changed price-aware RFQ flag to be a argument parameter * prettified tests * lint
This commit is contained in:
parent
99f5be8378
commit
689a8881c2
@ -89,6 +89,7 @@ const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = {
|
|||||||
|
|
||||||
const DEFAULT_RFQT_REQUEST_OPTS: Partial<RfqtRequestOpts> = {
|
const DEFAULT_RFQT_REQUEST_OPTS: Partial<RfqtRequestOpts> = {
|
||||||
makerEndpointMaxResponseTimeMs: 1000,
|
makerEndpointMaxResponseTimeMs: 1000,
|
||||||
|
isPriceAwareRFQEnabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_INFO_LOGGER: LogFunction = (obj, msg) =>
|
export const DEFAULT_INFO_LOGGER: LogFunction = (obj, msg) =>
|
||||||
|
@ -8,7 +8,7 @@ import { BlockParamLiteral, SupportedProvider, ZeroExProvider } from 'ethereum-t
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
import { artifacts } from './artifacts';
|
||||||
import { BRIDGE_ADDRESSES_BY_CHAIN, constants, IS_PRICE_AWARE_RFQ_ENABLED } from './constants';
|
import { BRIDGE_ADDRESSES_BY_CHAIN, constants } from './constants';
|
||||||
import {
|
import {
|
||||||
AssetSwapperContractAddresses,
|
AssetSwapperContractAddresses,
|
||||||
CalculateSwapQuoteOpts,
|
CalculateSwapQuoteOpts,
|
||||||
@ -701,8 +701,8 @@ export class SwapQuoter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!IS_PRICE_AWARE_RFQ_ENABLED && // Price-aware RFQ is disabled.
|
|
||||||
opts.rfqt && // This is an RFQT-enabled API request
|
opts.rfqt && // This is an RFQT-enabled API request
|
||||||
|
!opts.rfqt.isPriceAwareRFQEnabled && // If Price-aware RFQ is enabled, firm quotes are requested later on in the process.
|
||||||
opts.rfqt.intentOnFilling && // The requestor is asking for a firm quote
|
opts.rfqt.intentOnFilling && // The requestor is asking for a firm quote
|
||||||
opts.rfqt.apiKey &&
|
opts.rfqt.apiKey &&
|
||||||
this._isApiKeyWhitelisted(opts.rfqt.apiKey) && // A valid API key was provided
|
this._isApiKeyWhitelisted(opts.rfqt.apiKey) && // A valid API key was provided
|
||||||
|
@ -251,6 +251,17 @@ export interface RfqtRequestOpts {
|
|||||||
isIndicative?: boolean;
|
isIndicative?: boolean;
|
||||||
makerEndpointMaxResponseTimeMs?: number;
|
makerEndpointMaxResponseTimeMs?: number;
|
||||||
nativeExclusivelyRFQT?: boolean;
|
nativeExclusivelyRFQT?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This feature flag allows us to merge the price-aware RFQ pricing
|
||||||
|
* project while still controlling when to activate the feature. We plan to do some
|
||||||
|
* data analysis work and address some of the issues with maker fillable amounts
|
||||||
|
* in later milestones. Once the feature is fully rolled out and is providing value
|
||||||
|
* and we have assessed that there is no user impact, we will proceed in cleaning up
|
||||||
|
* the feature flag. When that time comes, follow this PR to "undo" the feature flag:
|
||||||
|
* https://github.com/0xProject/0x-monorepo/pull/2735
|
||||||
|
*/
|
||||||
|
isPriceAwareRFQEnabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,7 +4,6 @@ import { BigNumber, NULL_ADDRESS } from '@0x/utils';
|
|||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { IS_PRICE_AWARE_RFQ_ENABLED } from '../../constants';
|
|
||||||
import { AssetSwapperContractAddresses, MarketOperation, Omit } from '../../types';
|
import { AssetSwapperContractAddresses, MarketOperation, Omit } from '../../types';
|
||||||
import { QuoteRequestor } from '../quote_requestor';
|
import { QuoteRequestor } from '../quote_requestor';
|
||||||
|
|
||||||
@ -216,8 +215,9 @@ export class MarketOperationUtils {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isPriceAwareRfqEnabled = _opts.rfqt && _opts.rfqt.isPriceAwareRFQEnabled;
|
||||||
const rfqtPromise =
|
const rfqtPromise =
|
||||||
!IS_PRICE_AWARE_RFQ_ENABLED && quoteSourceFilters.isAllowed(ERC20BridgeSource.Native)
|
!isPriceAwareRfqEnabled && quoteSourceFilters.isAllowed(ERC20BridgeSource.Native)
|
||||||
? getRfqtIndicativeQuotesAsync(
|
? getRfqtIndicativeQuotesAsync(
|
||||||
nativeOrders[0].makerAssetData,
|
nativeOrders[0].makerAssetData,
|
||||||
nativeOrders[0].takerAssetData,
|
nativeOrders[0].takerAssetData,
|
||||||
@ -364,8 +364,9 @@ export class MarketOperationUtils {
|
|||||||
this._liquidityProviderRegistry,
|
this._liquidityProviderRegistry,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const isPriceAwareRfqEnabled = _opts.rfqt && _opts.rfqt.isPriceAwareRFQEnabled;
|
||||||
const rfqtPromise =
|
const rfqtPromise =
|
||||||
!IS_PRICE_AWARE_RFQ_ENABLED && quoteSourceFilters.isAllowed(ERC20BridgeSource.Native)
|
!isPriceAwareRfqEnabled && quoteSourceFilters.isAllowed(ERC20BridgeSource.Native)
|
||||||
? getRfqtIndicativeQuotesAsync(
|
? getRfqtIndicativeQuotesAsync(
|
||||||
nativeOrders[0].makerAssetData,
|
nativeOrders[0].makerAssetData,
|
||||||
nativeOrders[0].takerAssetData,
|
nativeOrders[0].takerAssetData,
|
||||||
@ -677,8 +678,8 @@ 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 (
|
if (
|
||||||
IS_PRICE_AWARE_RFQ_ENABLED &&
|
|
||||||
rfqt &&
|
rfqt &&
|
||||||
|
rfqt.isPriceAwareRFQEnabled &&
|
||||||
rfqt.quoteRequestor &&
|
rfqt.quoteRequestor &&
|
||||||
marketSideLiquidity.quoteSourceFilters.isAllowed(ERC20BridgeSource.Native)
|
marketSideLiquidity.quoteSourceFilters.isAllowed(ERC20BridgeSource.Native)
|
||||||
) {
|
) {
|
||||||
|
@ -17,7 +17,6 @@ import * as _ from 'lodash';
|
|||||||
import * as TypeMoq from 'typemoq';
|
import * as TypeMoq from 'typemoq';
|
||||||
|
|
||||||
import { MarketOperation, QuoteRequestor, RfqtRequestOpts, SignedOrderWithFillableAmounts } from '../src';
|
import { MarketOperation, QuoteRequestor, RfqtRequestOpts, SignedOrderWithFillableAmounts } from '../src';
|
||||||
import { IS_PRICE_AWARE_RFQ_ENABLED } from '../src/constants';
|
|
||||||
import { getRfqtIndicativeQuotesAsync, MarketOperationUtils } from '../src/utils/market_operation_utils/';
|
import { getRfqtIndicativeQuotesAsync, MarketOperationUtils } from '../src/utils/market_operation_utils/';
|
||||||
import { BalancerPoolsCache } from '../src/utils/market_operation_utils/balancer_utils';
|
import { BalancerPoolsCache } from '../src/utils/market_operation_utils/balancer_utils';
|
||||||
import {
|
import {
|
||||||
@ -746,10 +745,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it(
|
it('getMarketSellOrdersAsync() optimizer will be called once only if price-aware RFQ is disabled', async () => {
|
||||||
'getMarketSellOrdersAsync() optimizer will be called once only if RFQ if not defined',
|
|
||||||
IS_PRICE_AWARE_RFQ_ENABLED
|
|
||||||
? async () => {
|
|
||||||
const mockedMarketOpUtils = TypeMoq.Mock.ofType(
|
const mockedMarketOpUtils = TypeMoq.Mock.ofType(
|
||||||
MarketOperationUtils,
|
MarketOperationUtils,
|
||||||
TypeMoq.MockBehavior.Loose,
|
TypeMoq.MockBehavior.Loose,
|
||||||
@ -767,28 +763,14 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
.verifiable(TypeMoq.Times.once());
|
.verifiable(TypeMoq.Times.once());
|
||||||
|
|
||||||
const totalAssetAmount = ORDERS.map(o => o.takerAssetAmount).reduce((a, b) => a.plus(b));
|
const totalAssetAmount = ORDERS.map(o => o.takerAssetAmount).reduce((a, b) => a.plus(b));
|
||||||
await mockedMarketOpUtils.object.getMarketSellOrdersAsync(
|
await mockedMarketOpUtils.object.getMarketSellOrdersAsync(ORDERS, totalAssetAmount, DEFAULT_OPTS);
|
||||||
ORDERS,
|
|
||||||
totalAssetAmount,
|
|
||||||
DEFAULT_OPTS,
|
|
||||||
);
|
|
||||||
mockedMarketOpUtils.verifyAll();
|
mockedMarketOpUtils.verifyAll();
|
||||||
}
|
});
|
||||||
: undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
it('optimizer will send in a comparison price to RFQ providers', async () => {
|
||||||
'optimizer will send in a comparison price to RFQ providers',
|
|
||||||
IS_PRICE_AWARE_RFQ_ENABLED
|
|
||||||
? async () => {
|
|
||||||
// Set up mocked quote requestor, will return an order that is better
|
// Set up mocked quote requestor, will return an order that is better
|
||||||
// than the best of the orders.
|
// than the best of the orders.
|
||||||
const mockedQuoteRequestor = TypeMoq.Mock.ofType(
|
const mockedQuoteRequestor = TypeMoq.Mock.ofType(QuoteRequestor, TypeMoq.MockBehavior.Loose, false, {});
|
||||||
QuoteRequestor,
|
|
||||||
TypeMoq.MockBehavior.Loose,
|
|
||||||
false,
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
let requestedComparisonPrice: BigNumber | undefined;
|
let requestedComparisonPrice: BigNumber | undefined;
|
||||||
mockedQuoteRequestor
|
mockedQuoteRequestor
|
||||||
@ -839,11 +821,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
mockedMarketOpUtils.callBase = true;
|
mockedMarketOpUtils.callBase = true;
|
||||||
mockedMarketOpUtils
|
mockedMarketOpUtils
|
||||||
.setup(mou =>
|
.setup(mou =>
|
||||||
mou.getMarketSellLiquidityAsync(
|
mou.getMarketSellLiquidityAsync(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()),
|
||||||
TypeMoq.It.isAny(),
|
|
||||||
TypeMoq.It.isAny(),
|
|
||||||
TypeMoq.It.isAny(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.returns(async () => {
|
.returns(async () => {
|
||||||
return {
|
return {
|
||||||
@ -880,9 +858,9 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
apiKey: 'foo',
|
apiKey: 'foo',
|
||||||
takerAddress: randomAddress(),
|
takerAddress: randomAddress(),
|
||||||
intentOnFilling: true,
|
intentOnFilling: true,
|
||||||
|
isPriceAwareRFQEnabled: true,
|
||||||
quoteRequestor: {
|
quoteRequestor: {
|
||||||
requestRfqtFirmQuotesAsync:
|
requestRfqtFirmQuotesAsync: mockedQuoteRequestor.object.requestRfqtFirmQuotesAsync,
|
||||||
mockedQuoteRequestor.object.requestRfqtFirmQuotesAsync,
|
|
||||||
} as any,
|
} as any,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -892,14 +870,9 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
expect(requestedComparisonPrice!.toString()).to.eql('320');
|
expect(requestedComparisonPrice!.toString()).to.eql('320');
|
||||||
expect(result.optimizedOrders[0].makerAssetAmount.toString()).to.eql('321000000');
|
expect(result.optimizedOrders[0].makerAssetAmount.toString()).to.eql('321000000');
|
||||||
expect(result.optimizedOrders[0].takerAssetAmount.toString()).to.eql('1000000000000000000');
|
expect(result.optimizedOrders[0].takerAssetAmount.toString()).to.eql('1000000000000000000');
|
||||||
}
|
});
|
||||||
: undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
it('getMarketSellOrdersAsync() will not rerun the optimizer if no orders are returned', async () => {
|
||||||
'getMarketSellOrdersAsync() will not rerun the optimizer if no orders are returned',
|
|
||||||
IS_PRICE_AWARE_RFQ_ENABLED
|
|
||||||
? 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(
|
||||||
MarketOperationUtils,
|
MarketOperationUtils,
|
||||||
@ -925,6 +898,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
apiKey: 'foo',
|
apiKey: 'foo',
|
||||||
takerAddress: randomAddress(),
|
takerAddress: randomAddress(),
|
||||||
intentOnFilling: true,
|
intentOnFilling: true,
|
||||||
|
isPriceAwareRFQEnabled: true,
|
||||||
quoteRequestor: {
|
quoteRequestor: {
|
||||||
requestRfqtFirmQuotesAsync: requestor.object.requestRfqtFirmQuotesAsync,
|
requestRfqtFirmQuotesAsync: requestor.object.requestRfqtFirmQuotesAsync,
|
||||||
} as any,
|
} as any,
|
||||||
@ -932,19 +906,10 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
});
|
});
|
||||||
mockedMarketOpUtils.verifyAll();
|
mockedMarketOpUtils.verifyAll();
|
||||||
requestor.verifyAll();
|
requestor.verifyAll();
|
||||||
}
|
});
|
||||||
: undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
it('getMarketSellOrdersAsync() will rerun the optimizer if one or more indicative are returned', async () => {
|
||||||
'getMarketSellOrdersAsync() will rerun the optimizer if one or more indicative are returned',
|
const requestor = getMockedQuoteRequestor('indicative', [ORDERS[0], ORDERS[1]], TypeMoq.Times.once());
|
||||||
IS_PRICE_AWARE_RFQ_ENABLED
|
|
||||||
? async () => {
|
|
||||||
const requestor = getMockedQuoteRequestor(
|
|
||||||
'indicative',
|
|
||||||
[ORDERS[0], ORDERS[1]],
|
|
||||||
TypeMoq.Times.once(),
|
|
||||||
);
|
|
||||||
|
|
||||||
const numOrdersInCall: number[] = [];
|
const numOrdersInCall: number[] = [];
|
||||||
const numIndicativeQuotesInCall: number[] = [];
|
const numIndicativeQuotesInCall: number[] = [];
|
||||||
@ -976,11 +941,11 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
rfqt: {
|
rfqt: {
|
||||||
isIndicative: true,
|
isIndicative: true,
|
||||||
apiKey: 'foo',
|
apiKey: 'foo',
|
||||||
|
isPriceAwareRFQEnabled: true,
|
||||||
takerAddress: randomAddress(),
|
takerAddress: randomAddress(),
|
||||||
intentOnFilling: true,
|
intentOnFilling: true,
|
||||||
quoteRequestor: {
|
quoteRequestor: {
|
||||||
requestRfqtIndicativeQuotesAsync:
|
requestRfqtIndicativeQuotesAsync: requestor.object.requestRfqtIndicativeQuotesAsync,
|
||||||
requestor.object.requestRfqtIndicativeQuotesAsync,
|
|
||||||
} as any,
|
} as any,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -998,14 +963,9 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
expect(numIndicativeQuotesInCall.length).to.eql(2);
|
expect(numIndicativeQuotesInCall.length).to.eql(2);
|
||||||
expect(numIndicativeQuotesInCall[0]).to.eql(0);
|
expect(numIndicativeQuotesInCall[0]).to.eql(0);
|
||||||
expect(numIndicativeQuotesInCall[1]).to.eql(2);
|
expect(numIndicativeQuotesInCall[1]).to.eql(2);
|
||||||
}
|
});
|
||||||
: undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
it('getMarketSellOrdersAsync() will rerun the optimizer if one or more RFQ orders are returned', async () => {
|
||||||
'getMarketSellOrdersAsync() will rerun the optimizer if one or more RFQ orders are returned',
|
|
||||||
IS_PRICE_AWARE_RFQ_ENABLED
|
|
||||||
? async () => {
|
|
||||||
const requestor = getMockedQuoteRequestor('firm', [ORDERS[0]], TypeMoq.Times.once());
|
const requestor = getMockedQuoteRequestor('firm', [ORDERS[0]], TypeMoq.Times.once());
|
||||||
|
|
||||||
// Ensure that `_generateOptimizedOrdersAsync` is only called once
|
// Ensure that `_generateOptimizedOrdersAsync` is only called once
|
||||||
@ -1040,6 +1000,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
apiKey: 'foo',
|
apiKey: 'foo',
|
||||||
takerAddress: randomAddress(),
|
takerAddress: randomAddress(),
|
||||||
intentOnFilling: true,
|
intentOnFilling: true,
|
||||||
|
isPriceAwareRFQEnabled: true,
|
||||||
quoteRequestor: {
|
quoteRequestor: {
|
||||||
requestRfqtFirmQuotesAsync: requestor.object.requestRfqtFirmQuotesAsync,
|
requestRfqtFirmQuotesAsync: requestor.object.requestRfqtFirmQuotesAsync,
|
||||||
} as any,
|
} as any,
|
||||||
@ -1054,21 +1015,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
// The first call to optimizer was with an extra RFQ order.
|
// The first call to optimizer was with an extra RFQ order.
|
||||||
expect(numOrdersInCall[0]).to.eql(2);
|
expect(numOrdersInCall[0]).to.eql(2);
|
||||||
expect(numOrdersInCall[1]).to.eql(3);
|
expect(numOrdersInCall[1]).to.eql(3);
|
||||||
}
|
});
|
||||||
: undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
it('getMarketSellOrdersAsync() will not raise a NoOptimalPath error if no initial path was found during on-chain DEX optimization, but a path was found after RFQ optimization', async () => {
|
||||||
'getMarketSellOrdersAsync() will not raise a NoOptimalPath error if no initial path was found during on-chain DEX optimization, but a path was found after RFQ optimization',
|
|
||||||
IS_PRICE_AWARE_RFQ_ENABLED
|
|
||||||
? async () => {
|
|
||||||
let hasFirstOptimizationRun = false;
|
let hasFirstOptimizationRun = false;
|
||||||
let hasSecondOptimizationRun = false;
|
let hasSecondOptimizationRun = false;
|
||||||
const requestor = getMockedQuoteRequestor(
|
const requestor = getMockedQuoteRequestor('firm', [ORDERS[0], ORDERS[1]], TypeMoq.Times.once());
|
||||||
'firm',
|
|
||||||
[ORDERS[0], ORDERS[1]],
|
|
||||||
TypeMoq.Times.once(),
|
|
||||||
);
|
|
||||||
|
|
||||||
const mockedMarketOpUtils = TypeMoq.Mock.ofType(
|
const mockedMarketOpUtils = TypeMoq.Mock.ofType(
|
||||||
MarketOperationUtils,
|
MarketOperationUtils,
|
||||||
@ -1104,6 +1056,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
isIndicative: false,
|
isIndicative: false,
|
||||||
apiKey: 'foo',
|
apiKey: 'foo',
|
||||||
takerAddress: randomAddress(),
|
takerAddress: randomAddress(),
|
||||||
|
isPriceAwareRFQEnabled: true,
|
||||||
intentOnFilling: true,
|
intentOnFilling: true,
|
||||||
quoteRequestor: {
|
quoteRequestor: {
|
||||||
requestRfqtFirmQuotesAsync: requestor.object.requestRfqtFirmQuotesAsync,
|
requestRfqtFirmQuotesAsync: requestor.object.requestRfqtFirmQuotesAsync,
|
||||||
@ -1116,9 +1069,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
|
|
||||||
expect(hasFirstOptimizationRun).to.eql(true);
|
expect(hasFirstOptimizationRun).to.eql(true);
|
||||||
expect(hasSecondOptimizationRun).to.eql(true);
|
expect(hasSecondOptimizationRun).to.eql(true);
|
||||||
}
|
});
|
||||||
: undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
it('getMarketSellOrdersAsync() will raise a NoOptimalPath error if no path was found during on-chain DEX optimization and RFQ optimization', async () => {
|
it('getMarketSellOrdersAsync() will raise a NoOptimalPath error if no path was found during on-chain DEX optimization and RFQ optimization', async () => {
|
||||||
const mockedMarketOpUtils = TypeMoq.Mock.ofType(
|
const mockedMarketOpUtils = TypeMoq.Mock.ofType(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user