initial implementation

This commit is contained in:
Daniel Pyrathon 2020-09-28 13:15:44 -07:00
parent a7d502a501
commit 7bc9701c81
3 changed files with 74 additions and 34 deletions

View File

@ -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

View File

@ -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,

View File

@ -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;
}