initial implementation
This commit is contained in:
parent
a7d502a501
commit
7bc9701c81
@ -683,33 +683,18 @@ export class SwapQuoter {
|
||||
this.expiryBufferMs,
|
||||
);
|
||||
|
||||
if (
|
||||
opts.rfqt && // This is an RFQT-enabled API request
|
||||
opts.rfqt.intentOnFilling && // The requestor is asking for a firm quote
|
||||
opts.rfqt.apiKey &&
|
||||
this._isApiKeyWhitelisted(opts.rfqt.apiKey) && // A valid API key was provided
|
||||
sourceFilters.isAllowed(ERC20BridgeSource.Native) // Native liquidity is not excluded
|
||||
) {
|
||||
if (!opts.rfqt.takerAddress || opts.rfqt.takerAddress === constants.NULL_ADDRESS) {
|
||||
throw new Error('RFQ-T requests must specify a taker address');
|
||||
// If an API key was provided, but the key is not whitelisted, raise a warning and disable RFQ
|
||||
if (opts.rfqt && opts.rfqt.apiKey && !this._isApiKeyWhitelisted(opts.rfqt.apiKey)) {
|
||||
if (rfqtOptions && rfqtOptions.warningLogger) {
|
||||
rfqtOptions.warningLogger({
|
||||
apiKey: opts.rfqt.apiKey,
|
||||
}, 'Attempt at using an RFQ API key that is not whitelisted. Disabling RFQ for the request lifetime.');
|
||||
}
|
||||
orderBatchPromises.push(
|
||||
quoteRequestor
|
||||
.requestRfqtFirmQuotesAsync(
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
assetFillAmount,
|
||||
marketOperation,
|
||||
opts.rfqt,
|
||||
)
|
||||
.then(firmQuotes => firmQuotes.map(quote => quote.signedOrder)),
|
||||
);
|
||||
opts.rfqt = undefined;
|
||||
}
|
||||
|
||||
const orderBatches: SignedOrder[][] = await Promise.all(orderBatchPromises);
|
||||
|
||||
const unsortedOrders: SignedOrder[] = orderBatches.reduce((_orders, batch) => _orders.concat(...batch), []);
|
||||
|
||||
const orders = sortingUtils.sortOrders(unsortedOrders);
|
||||
|
||||
// if no native orders, pass in a dummy order for the sampler to have required metadata for sampling
|
||||
|
@ -232,6 +232,7 @@ export class MarketOperationUtils {
|
||||
ethToInputRate: ethToTakerAssetRate,
|
||||
rfqtIndicativeQuotes,
|
||||
twoHopQuotes,
|
||||
quoteSourceFilters,
|
||||
};
|
||||
}
|
||||
|
||||
@ -345,6 +346,7 @@ export class MarketOperationUtils {
|
||||
ethToInputRate: ethToMakerAssetRate,
|
||||
rfqtIndicativeQuotes,
|
||||
twoHopQuotes,
|
||||
quoteSourceFilters,
|
||||
};
|
||||
}
|
||||
|
||||
@ -362,15 +364,63 @@ export class MarketOperationUtils {
|
||||
opts?: Partial<GetMarketOrdersOpts>,
|
||||
): Promise<OptimizerResultWithReport> {
|
||||
const defaultOpts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
||||
const marketSideLiquidity = await this.getMarketSellLiquidityAsync(nativeOrders, takerAmount, defaultOpts);
|
||||
const optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, {
|
||||
const optimizerOpts: GenerateOptimizedOrdersOpts = {
|
||||
bridgeSlippage: defaultOpts.bridgeSlippage,
|
||||
maxFallbackSlippage: defaultOpts.maxFallbackSlippage,
|
||||
excludedSources: defaultOpts.excludedSources,
|
||||
feeSchedule: defaultOpts.feeSchedule,
|
||||
allowFallback: defaultOpts.allowFallback,
|
||||
shouldBatchBridgeOrders: defaultOpts.shouldBatchBridgeOrders,
|
||||
});
|
||||
};
|
||||
|
||||
// Compute an optimized path for on-chain DEX and open-orderbook. This should not include RFQ liquidity.
|
||||
const marketSideLiquidity = await this.getMarketSellLiquidityAsync(nativeOrders, takerAmount, defaultOpts);
|
||||
let optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
|
||||
|
||||
// If RFQ liquidity is enabled, make a request to check RFQ liquidity
|
||||
const { rfqt } = defaultOpts;
|
||||
if (rfqt && rfqt.quoteRequestor && marketSideLiquidity.quoteSourceFilters.isAllowed(ERC20BridgeSource.Native)) {
|
||||
|
||||
// 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(
|
||||
nativeOrders[0].makerAssetData,
|
||||
nativeOrders[0].takerAssetData,
|
||||
MarketOperation.Sell,
|
||||
takerAmount,
|
||||
defaultOpts,
|
||||
);
|
||||
if (indicativeQuotes.length > 0) {
|
||||
optimizerResult = await this._generateOptimizedOrdersAsync({
|
||||
...marketSideLiquidity,
|
||||
rfqtIndicativeQuotes: indicativeQuotes,
|
||||
}, optimizerOpts);
|
||||
}
|
||||
} else {
|
||||
// A firm quote is being requested. Ensure that `intentOnFilling` is enabled.
|
||||
if (rfqt.intentOnFilling) {
|
||||
|
||||
// Extra validation happens when requesting a firm quote, such as ensuring that the takerAddress
|
||||
// is indeed valid.
|
||||
if (!rfqt.takerAddress || rfqt.takerAddress === NULL_ADDRESS) {
|
||||
throw new Error('RFQ-T requests must specify a taker address');
|
||||
}
|
||||
const firmQuotes = await rfqt.quoteRequestor.requestRfqtFirmQuotesAsync(
|
||||
nativeOrders[0].makerAssetData,
|
||||
nativeOrders[0].takerAssetData,
|
||||
takerAmount,
|
||||
MarketOperation.Sell,
|
||||
rfqt,
|
||||
);
|
||||
if (firmQuotes.length > 0) {
|
||||
optimizerResult = await this._generateOptimizedOrdersAsync({
|
||||
...marketSideLiquidity,
|
||||
nativeOrders: marketSideLiquidity.nativeOrders.concat(firmQuotes.map(quote => quote.signedOrder)),
|
||||
}, optimizerOpts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute Quote Report and return the results.
|
||||
let quoteReport: QuoteReport | undefined;
|
||||
@ -494,6 +544,7 @@ export class MarketOperationUtils {
|
||||
inputToken: makerToken,
|
||||
outputToken: takerToken,
|
||||
twoHopQuotes: [],
|
||||
quoteSourceFilters,
|
||||
},
|
||||
{
|
||||
bridgeSlippage: _opts.bridgeSlippage,
|
||||
@ -516,15 +567,7 @@ export class MarketOperationUtils {
|
||||
|
||||
private async _generateOptimizedOrdersAsync(
|
||||
marketSideLiquidity: MarketSideLiquidity,
|
||||
opts: {
|
||||
runLimit?: number;
|
||||
bridgeSlippage?: number;
|
||||
maxFallbackSlippage?: number;
|
||||
excludedSources?: ERC20BridgeSource[];
|
||||
feeSchedule?: FeeSchedule;
|
||||
allowFallback?: boolean;
|
||||
shouldBatchBridgeOrders?: boolean;
|
||||
},
|
||||
opts: GenerateOptimizedOrdersOpts,
|
||||
): Promise<OptimizerResult> {
|
||||
const {
|
||||
inputToken,
|
||||
|
@ -5,6 +5,7 @@ import { BigNumber } from '@0x/utils';
|
||||
import { RfqtRequestOpts, SignedOrderWithFillableAmounts } from '../../types';
|
||||
import { QuoteRequestor } from '../../utils/quote_requestor';
|
||||
import { QuoteReport } from '../quote_report_generator';
|
||||
import { SourceFilters } from './source_filters';
|
||||
|
||||
/**
|
||||
* Order domain keys: chainId and exchange
|
||||
@ -348,8 +349,19 @@ export interface MarketSideLiquidity {
|
||||
ethToInputRate: BigNumber;
|
||||
rfqtIndicativeQuotes: RFQTIndicativeQuote[];
|
||||
twoHopQuotes: Array<DexSample<MultiHopFillData>>;
|
||||
quoteSourceFilters: SourceFilters;
|
||||
}
|
||||
|
||||
export interface TokenAdjacencyGraph {
|
||||
[token: string]: string[];
|
||||
}
|
||||
|
||||
export interface GenerateOptimizedOrdersOpts {
|
||||
runLimit?: number;
|
||||
bridgeSlippage?: number;
|
||||
maxFallbackSlippage?: number;
|
||||
excludedSources?: ERC20BridgeSource[];
|
||||
feeSchedule?: FeeSchedule;
|
||||
allowFallback?: boolean;
|
||||
shouldBatchBridgeOrders?: boolean;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user