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