asset-swapper: RFQ-T indicative quotes
These changes have been exercised via mocha tests in the 0x-api repo.
Not sure why I had to add GetMarketOrdersRfqtOpts to the package
exports. `yarn test:generate_docs:circleci` said:
$ node ./packages/monorepo-scripts/lib/doc_generate.js --package @0x/asset-swapper
GENERATE_DOCS: Generating Typedoc JSON for @0x/asset-swapper...
GENERATE_DOCS: Generating Typedoc Markdown for @0x/asset-swapper...
GENERATE_DOCS: Modifying Markdown To Exclude Unexported Items...
Error: @0x/asset-swapper package needs to export:
GetMarketOrdersRfqtOpts
From it's index.ts. If any are from external dependencies, then add them to the EXTERNAL_TYPE_MAP.
at DocGenerateUtils._lookForMissingReferenceExportsThrowIfExists (/root/repo/packages/monorepo-scripts/lib/utils/doc_generate_utils.js:288:19)
at DocGenerateUtils.<anonymous> (/root/repo/packages/monorepo-scripts/lib/utils/doc_generate_utils.js:255:34)
at step (/root/repo/packages/monorepo-scripts/lib/utils/doc_generate_utils.js:32:23)
at Object.next (/root/repo/packages/monorepo-scripts/lib/utils/doc_generate_utils.js:13:53)
at fulfilled (/root/repo/packages/monorepo-scripts/lib/utils/doc_generate_utils.js:4:58)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7)
This commit is contained in:
@@ -64,8 +64,9 @@ export {
|
|||||||
CollapsedFill,
|
CollapsedFill,
|
||||||
NativeCollapsedFill,
|
NativeCollapsedFill,
|
||||||
OptimizedMarketOrder,
|
OptimizedMarketOrder,
|
||||||
|
GetMarketOrdersRfqtOpts,
|
||||||
} from './utils/market_operation_utils/types';
|
} from './utils/market_operation_utils/types';
|
||||||
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
||||||
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||||
export { QuoteRequestor } from './utils/quote_requestor';
|
export { QuoteRequestor, RfqtIndicativeQuoteResponse } from './utils/quote_requestor';
|
||||||
export { rfqtMocker } from './utils/rfqt_mocker';
|
export { rfqtMocker } from './utils/rfqt_mocker';
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import { constants } from './constants';
|
import { constants } from './constants';
|
||||||
import {
|
import {
|
||||||
|
CalculateSwapQuoteOpts,
|
||||||
LiquidityForTakerMakerAssetDataPair,
|
LiquidityForTakerMakerAssetDataPair,
|
||||||
MarketBuySwapQuote,
|
MarketBuySwapQuote,
|
||||||
MarketOperation,
|
MarketOperation,
|
||||||
@@ -24,7 +25,6 @@ import { calculateLiquidity } from './utils/calculate_liquidity';
|
|||||||
import { MarketOperationUtils } from './utils/market_operation_utils';
|
import { MarketOperationUtils } from './utils/market_operation_utils';
|
||||||
import { createDummyOrderForSampler } from './utils/market_operation_utils/orders';
|
import { createDummyOrderForSampler } from './utils/market_operation_utils/orders';
|
||||||
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
||||||
import { GetMarketOrdersOpts } from './utils/market_operation_utils/types';
|
|
||||||
import { orderPrunerUtils } from './utils/order_prune_utils';
|
import { orderPrunerUtils } from './utils/order_prune_utils';
|
||||||
import { OrderStateUtils } from './utils/order_state_utils';
|
import { OrderStateUtils } from './utils/order_state_utils';
|
||||||
import { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
import { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||||
@@ -565,19 +565,30 @@ export class SwapQuoter {
|
|||||||
|
|
||||||
let swapQuote: SwapQuote;
|
let swapQuote: SwapQuote;
|
||||||
|
|
||||||
|
const calcOpts: CalculateSwapQuoteOpts = opts;
|
||||||
|
if (
|
||||||
|
// we should request indicative quotes:
|
||||||
|
calcOpts.rfqt &&
|
||||||
|
!calcOpts.rfqt.intentOnFilling &&
|
||||||
|
calcOpts.rfqt.apiKey &&
|
||||||
|
this._rfqtTakerApiKeyWhitelist.includes(calcOpts.rfqt.apiKey)
|
||||||
|
) {
|
||||||
|
calcOpts.rfqt.quoteRequestor = this._quoteRequestor;
|
||||||
|
}
|
||||||
|
|
||||||
if (marketOperation === MarketOperation.Buy) {
|
if (marketOperation === MarketOperation.Buy) {
|
||||||
swapQuote = await this._swapQuoteCalculator.calculateMarketBuySwapQuoteAsync(
|
swapQuote = await this._swapQuoteCalculator.calculateMarketBuySwapQuoteAsync(
|
||||||
orders,
|
orders,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
opts,
|
calcOpts,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
swapQuote = await this._swapQuoteCalculator.calculateMarketSellSwapQuoteAsync(
|
swapQuote = await this._swapQuoteCalculator.calculateMarketSellSwapQuoteAsync(
|
||||||
orders,
|
orders,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
opts,
|
calcOpts,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -287,3 +287,16 @@ export interface MockedRfqtFirmQuoteResponse {
|
|||||||
responseData: any;
|
responseData: any;
|
||||||
responseCode: number;
|
responseCode: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a mocked RFQT maker responses.
|
||||||
|
*/
|
||||||
|
export interface MockedRfqtIndicativeQuoteResponse {
|
||||||
|
endpoint: string;
|
||||||
|
requestApiKey: string;
|
||||||
|
requestParams: {
|
||||||
|
[key: string]: string | undefined;
|
||||||
|
};
|
||||||
|
responseData: any;
|
||||||
|
responseCode: number;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { SignedOrder } from '@0x/types';
|
|||||||
import { BigNumber, NULL_ADDRESS } from '@0x/utils';
|
import { BigNumber, NULL_ADDRESS } from '@0x/utils';
|
||||||
|
|
||||||
import { MarketOperation } from '../../types';
|
import { MarketOperation } from '../../types';
|
||||||
|
import { RfqtIndicativeQuoteResponse } from '../quote_requestor';
|
||||||
import { difference } from '../utils';
|
import { difference } from '../utils';
|
||||||
|
|
||||||
import { BUY_SOURCES, DEFAULT_GET_MARKET_ORDERS_OPTS, FEE_QUOTE_SOURCES, ONE_ETHER, SELL_SOURCES } from './constants';
|
import { BUY_SOURCES, DEFAULT_GET_MARKET_ORDERS_OPTS, FEE_QUOTE_SOURCES, ONE_ETHER, SELL_SOURCES } from './constants';
|
||||||
@@ -13,7 +14,12 @@ import {
|
|||||||
getPathAdjustedSlippage,
|
getPathAdjustedSlippage,
|
||||||
getPathSize,
|
getPathSize,
|
||||||
} from './fills';
|
} from './fills';
|
||||||
import { createOrdersFromPath, createSignedOrdersWithFillableAmounts, getNativeOrderTokens } from './orders';
|
import {
|
||||||
|
createOrdersFromPath,
|
||||||
|
createSignedOrdersFromRfqtIndicativeQuotes,
|
||||||
|
createSignedOrdersWithFillableAmounts,
|
||||||
|
getNativeOrderTokens,
|
||||||
|
} from './orders';
|
||||||
import { findOptimalPath } from './path_optimizer';
|
import { findOptimalPath } from './path_optimizer';
|
||||||
import { DexOrderSampler, getSampleAmounts } from './sampler';
|
import { DexOrderSampler, getSampleAmounts } from './sampler';
|
||||||
import {
|
import {
|
||||||
@@ -57,12 +63,7 @@ export class MarketOperationUtils {
|
|||||||
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
||||||
const [makerToken, takerToken] = getNativeOrderTokens(nativeOrders[0]);
|
const [makerToken, takerToken] = getNativeOrderTokens(nativeOrders[0]);
|
||||||
// Call the sampler contract.
|
// Call the sampler contract.
|
||||||
const [
|
const samplerPromise = this._sampler.executeAsync(
|
||||||
orderFillableAmounts,
|
|
||||||
liquidityProviderAddress,
|
|
||||||
ethToMakerAssetRate,
|
|
||||||
dexQuotes,
|
|
||||||
] = await this._sampler.executeAsync(
|
|
||||||
// Get native order fillable amounts.
|
// Get native order fillable amounts.
|
||||||
DexOrderSampler.ops.getOrderFillableTakerAmounts(nativeOrders),
|
DexOrderSampler.ops.getOrderFillableTakerAmounts(nativeOrders),
|
||||||
// Get the custom liquidity provider from registry.
|
// Get the custom liquidity provider from registry.
|
||||||
@@ -92,10 +93,25 @@ export class MarketOperationUtils {
|
|||||||
this._liquidityProviderRegistry,
|
this._liquidityProviderRegistry,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const rfqtPromise =
|
||||||
|
_opts !== undefined && _opts.rfqt !== undefined && _opts.rfqt.quoteRequestor !== undefined
|
||||||
|
? _opts.rfqt.quoteRequestor.requestRfqtIndicativeQuotesAsync(
|
||||||
|
nativeOrders[0].makerAssetData,
|
||||||
|
nativeOrders[0].takerAssetData,
|
||||||
|
takerAmount,
|
||||||
|
MarketOperation.Sell,
|
||||||
|
_opts.rfqt,
|
||||||
|
)
|
||||||
|
: Promise.resolve<RfqtIndicativeQuoteResponse[]>([]);
|
||||||
|
const [
|
||||||
|
[orderFillableAmounts, liquidityProviderAddress, ethToMakerAssetRate, dexQuotes],
|
||||||
|
rfqtIndicativeQuotes,
|
||||||
|
] = await Promise.all([samplerPromise, rfqtPromise]);
|
||||||
return this._generateOptimizedOrders({
|
return this._generateOptimizedOrders({
|
||||||
orderFillableAmounts,
|
orderFillableAmounts,
|
||||||
nativeOrders,
|
nativeOrders,
|
||||||
dexQuotes,
|
dexQuotes,
|
||||||
|
rfqtIndicativeQuotes,
|
||||||
liquidityProviderAddress,
|
liquidityProviderAddress,
|
||||||
inputToken: takerToken,
|
inputToken: takerToken,
|
||||||
outputToken: makerToken,
|
outputToken: makerToken,
|
||||||
@@ -130,12 +146,7 @@ export class MarketOperationUtils {
|
|||||||
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
||||||
const [makerToken, takerToken] = getNativeOrderTokens(nativeOrders[0]);
|
const [makerToken, takerToken] = getNativeOrderTokens(nativeOrders[0]);
|
||||||
// Call the sampler contract.
|
// Call the sampler contract.
|
||||||
const [
|
const samplerPromise = this._sampler.executeAsync(
|
||||||
orderFillableAmounts,
|
|
||||||
liquidityProviderAddress,
|
|
||||||
ethToTakerAssetRate,
|
|
||||||
dexQuotes,
|
|
||||||
] = await this._sampler.executeAsync(
|
|
||||||
// Get native order fillable amounts.
|
// Get native order fillable amounts.
|
||||||
DexOrderSampler.ops.getOrderFillableMakerAmounts(nativeOrders),
|
DexOrderSampler.ops.getOrderFillableMakerAmounts(nativeOrders),
|
||||||
// Get the custom liquidity provider from registry.
|
// Get the custom liquidity provider from registry.
|
||||||
@@ -165,11 +176,26 @@ export class MarketOperationUtils {
|
|||||||
this._liquidityProviderRegistry,
|
this._liquidityProviderRegistry,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const rfqtPromise =
|
||||||
|
opts !== undefined && _opts.rfqt !== undefined && _opts.rfqt.quoteRequestor !== undefined
|
||||||
|
? _opts.rfqt.quoteRequestor.requestRfqtIndicativeQuotesAsync(
|
||||||
|
nativeOrders[0].makerAssetData,
|
||||||
|
nativeOrders[0].takerAssetData,
|
||||||
|
makerAmount,
|
||||||
|
MarketOperation.Buy,
|
||||||
|
_opts.rfqt,
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
const [
|
||||||
|
[orderFillableAmounts, liquidityProviderAddress, ethToTakerAssetRate, dexQuotes],
|
||||||
|
rfqtIndicativeQuotes,
|
||||||
|
] = await Promise.all([samplerPromise, rfqtPromise]);
|
||||||
|
|
||||||
return this._generateOptimizedOrders({
|
return this._generateOptimizedOrders({
|
||||||
orderFillableAmounts,
|
orderFillableAmounts,
|
||||||
nativeOrders,
|
nativeOrders,
|
||||||
dexQuotes,
|
dexQuotes,
|
||||||
|
rfqtIndicativeQuotes,
|
||||||
liquidityProviderAddress,
|
liquidityProviderAddress,
|
||||||
inputToken: makerToken,
|
inputToken: makerToken,
|
||||||
outputToken: takerToken,
|
outputToken: takerToken,
|
||||||
@@ -246,6 +272,7 @@ export class MarketOperationUtils {
|
|||||||
orderFillableAmounts,
|
orderFillableAmounts,
|
||||||
nativeOrders,
|
nativeOrders,
|
||||||
dexQuotes,
|
dexQuotes,
|
||||||
|
rfqtIndicativeQuotes: [],
|
||||||
inputToken: makerToken,
|
inputToken: makerToken,
|
||||||
outputToken: takerToken,
|
outputToken: takerToken,
|
||||||
side: MarketOperation.Buy,
|
side: MarketOperation.Buy,
|
||||||
@@ -274,6 +301,7 @@ export class MarketOperationUtils {
|
|||||||
nativeOrders: SignedOrder[];
|
nativeOrders: SignedOrder[];
|
||||||
orderFillableAmounts: BigNumber[];
|
orderFillableAmounts: BigNumber[];
|
||||||
dexQuotes: DexSample[][];
|
dexQuotes: DexSample[][];
|
||||||
|
rfqtIndicativeQuotes: RfqtIndicativeQuoteResponse[];
|
||||||
runLimit?: number;
|
runLimit?: number;
|
||||||
ethToOutputRate?: BigNumber;
|
ethToOutputRate?: BigNumber;
|
||||||
bridgeSlippage?: number;
|
bridgeSlippage?: number;
|
||||||
@@ -290,7 +318,10 @@ export class MarketOperationUtils {
|
|||||||
const paths = createFillPaths({
|
const paths = createFillPaths({
|
||||||
side,
|
side,
|
||||||
// Augment native orders with their fillable amounts.
|
// Augment native orders with their fillable amounts.
|
||||||
orders: createSignedOrdersWithFillableAmounts(side, opts.nativeOrders, opts.orderFillableAmounts),
|
orders: [
|
||||||
|
...createSignedOrdersWithFillableAmounts(side, opts.nativeOrders, opts.orderFillableAmounts),
|
||||||
|
...createSignedOrdersFromRfqtIndicativeQuotes(opts.rfqtIndicativeQuotes),
|
||||||
|
],
|
||||||
dexQuotes: opts.dexQuotes,
|
dexQuotes: opts.dexQuotes,
|
||||||
targetInput: inputAmount,
|
targetInput: inputAmount,
|
||||||
ethToOutputRate: opts.ethToOutputRate,
|
ethToOutputRate: opts.ethToOutputRate,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { ERC20BridgeAssetData, SignedOrder } from '@0x/types';
|
|||||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types';
|
import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types';
|
||||||
|
import { RfqtIndicativeQuoteResponse } from '../quote_requestor';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_CURVE_OPTS,
|
DEFAULT_CURVE_OPTS,
|
||||||
@@ -358,3 +359,32 @@ function createNativeOrder(fill: CollapsedFill): OptimizedMarketOrder {
|
|||||||
...(fill as NativeCollapsedFill).nativeOrder,
|
...(fill as NativeCollapsedFill).nativeOrder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createSignedOrdersFromRfqtIndicativeQuotes(
|
||||||
|
quotes: RfqtIndicativeQuoteResponse[],
|
||||||
|
): SignedOrderWithFillableAmounts[] {
|
||||||
|
return quotes.map(quote => {
|
||||||
|
return {
|
||||||
|
fillableMakerAssetAmount: quote.makerAssetAmount,
|
||||||
|
fillableTakerAssetAmount: quote.takerAssetAmount,
|
||||||
|
makerAssetAmount: quote.makerAssetAmount,
|
||||||
|
takerAssetAmount: quote.takerAssetAmount,
|
||||||
|
makerAssetData: quote.makerAssetData,
|
||||||
|
takerAssetData: quote.takerAssetData,
|
||||||
|
takerAddress: NULL_ADDRESS,
|
||||||
|
makerAddress: NULL_ADDRESS,
|
||||||
|
senderAddress: NULL_ADDRESS,
|
||||||
|
feeRecipientAddress: NULL_ADDRESS,
|
||||||
|
salt: ZERO_AMOUNT, // generatePseudoRandomSalt(),
|
||||||
|
expirationTimeSeconds: new BigNumber(Math.floor(Date.now() / ONE_SECOND_MS) + ONE_HOUR_IN_SECONDS),
|
||||||
|
makerFeeAssetData: NULL_BYTES,
|
||||||
|
takerFeeAssetData: NULL_BYTES,
|
||||||
|
makerFee: ZERO_AMOUNT,
|
||||||
|
takerFee: ZERO_AMOUNT,
|
||||||
|
fillableTakerFeeAmount: ZERO_AMOUNT,
|
||||||
|
signature: WALLET_SIGNATURE,
|
||||||
|
chainId: 0, // HACK !!!!!!!!! how can we get at this from this context?
|
||||||
|
exchangeAddress: NULL_ADDRESS, // HACK !!!!!!!!! how can we get at this from this context?
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { IERC20BridgeSamplerContract } from '@0x/contract-wrappers';
|
import { IERC20BridgeSamplerContract } from '@0x/contract-wrappers';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { SignedOrderWithFillableAmounts } from '../../types';
|
import { RfqtRequestOpts, SignedOrderWithFillableAmounts } from '../../types';
|
||||||
|
import { QuoteRequestor, RfqtIndicativeQuoteResponse } from '../../utils/quote_requestor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Order domain keys: chainId and exchange
|
* Order domain keys: chainId and exchange
|
||||||
@@ -34,6 +35,7 @@ export enum ERC20BridgeSource {
|
|||||||
CurveUsdcDaiUsdtTusd = 'Curve_USDC_DAI_USDT_TUSD',
|
CurveUsdcDaiUsdtTusd = 'Curve_USDC_DAI_USDT_TUSD',
|
||||||
LiquidityProvider = 'LiquidityProvider',
|
LiquidityProvider = 'LiquidityProvider',
|
||||||
CurveUsdcDaiUsdtBusd = 'Curve_USDC_DAI_USDT_BUSD',
|
CurveUsdcDaiUsdtBusd = 'Curve_USDC_DAI_USDT_BUSD',
|
||||||
|
Rfqt = 'Rfqt',
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal `fillData` field for `Fill` objects.
|
// Internal `fillData` field for `Fill` objects.
|
||||||
@@ -44,6 +46,10 @@ export interface NativeFillData extends FillData {
|
|||||||
order: SignedOrderWithFillableAmounts;
|
order: SignedOrderWithFillableAmounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RfqtFillData extends FillData {
|
||||||
|
quote: RfqtIndicativeQuoteResponse;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an individual DEX sample from the sampler contract.
|
* Represents an individual DEX sample from the sampler contract.
|
||||||
*/
|
*/
|
||||||
@@ -130,6 +136,10 @@ export interface OptimizedMarketOrder extends SignedOrderWithFillableAmounts {
|
|||||||
fills: CollapsedFill[];
|
fills: CollapsedFill[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GetMarketOrdersRfqtOpts extends RfqtRequestOpts {
|
||||||
|
quoteRequestor?: QuoteRequestor;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for `getMarketSellOrdersAsync()` and `getMarketBuyOrdersAsync()`.
|
* Options for `getMarketSellOrdersAsync()` and `getMarketBuyOrdersAsync()`.
|
||||||
*/
|
*/
|
||||||
@@ -183,6 +193,7 @@ export interface GetMarketOrdersOpts {
|
|||||||
* sources. Defaults to `true`.
|
* sources. Defaults to `true`.
|
||||||
*/
|
*/
|
||||||
allowFallback: boolean;
|
allowFallback: boolean;
|
||||||
|
rfqt?: GetMarketOrdersRfqtOpts;
|
||||||
/**
|
/**
|
||||||
* Whether to combine contiguous bridge orders into a single DexForwarderBridge
|
* Whether to combine contiguous bridge orders into a single DexForwarderBridge
|
||||||
* order. Defaults to `true`.
|
* order. Defaults to `true`.
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ import { MarketOperation, RfqtRequestOpts } from '../types';
|
|||||||
* Request quotes from RFQ-T providers
|
* Request quotes from RFQ-T providers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export interface RfqtIndicativeQuoteResponse {
|
||||||
|
makerAssetData: string;
|
||||||
|
makerAssetAmount: BigNumber;
|
||||||
|
takerAssetData: string;
|
||||||
|
takerAssetAmount: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
function getTokenAddressOrThrow(assetData: string): string {
|
function getTokenAddressOrThrow(assetData: string): string {
|
||||||
const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
|
const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
|
||||||
if (decodedAssetData.hasOwnProperty('tokenAddress')) {
|
if (decodedAssetData.hasOwnProperty('tokenAddress')) {
|
||||||
@@ -141,4 +148,81 @@ export class QuoteRequestor {
|
|||||||
|
|
||||||
return orders;
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async requestRfqtIndicativeQuotesAsync(
|
||||||
|
makerAssetData: string,
|
||||||
|
takerAssetData: string,
|
||||||
|
assetFillAmount: BigNumber,
|
||||||
|
marketOperation: MarketOperation,
|
||||||
|
options: RfqtRequestOpts,
|
||||||
|
): Promise<RfqtIndicativeQuoteResponse[]> {
|
||||||
|
const _opts = _.merge({}, constants.DEFAULT_RFQT_REQUEST_OPTS, options);
|
||||||
|
assertTakerAddressOrThrow(_opts.takerAddress);
|
||||||
|
|
||||||
|
const axiosResponsesIfDefined: Array<
|
||||||
|
undefined | AxiosResponse<RfqtIndicativeQuoteResponse>
|
||||||
|
> = await Promise.all(
|
||||||
|
this._rfqtMakerEndpoints.map(async rfqtMakerEndpoint => {
|
||||||
|
try {
|
||||||
|
return await Axios.get<RfqtIndicativeQuoteResponse>(`${rfqtMakerEndpoint}/price`, {
|
||||||
|
headers: { '0x-api-key': options.apiKey },
|
||||||
|
params: {
|
||||||
|
takerAddress: options.takerAddress,
|
||||||
|
...inferQueryParams(marketOperation, makerAssetData, takerAssetData, assetFillAmount),
|
||||||
|
},
|
||||||
|
timeout: options.makerEndpointMaxResponseTimeMs,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logUtils.warn(
|
||||||
|
`Failed to get RFQ-T quote from market maker endpoint ${rfqtMakerEndpoint} for API key ${
|
||||||
|
options.apiKey
|
||||||
|
} for taker address ${options.takerAddress}`,
|
||||||
|
);
|
||||||
|
logUtils.warn(err);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const axiosResponses = axiosResponsesIfDefined.filter(
|
||||||
|
(respIfDefd): respIfDefd is AxiosResponse<RfqtIndicativeQuoteResponse> => respIfDefd !== undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
const responsesWithStringInts = axiosResponses.map(response => response.data); // not yet BigNumber
|
||||||
|
|
||||||
|
const validResponsesWithStringInts = responsesWithStringInts.filter(response => {
|
||||||
|
if (this._isValidRfqtIndicativeQuoteResponse(response)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
logUtils.warn(`Invalid RFQ-T indicative quote received, filtering out: ${JSON.stringify(response)}`);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const responses = validResponsesWithStringInts.map(response => {
|
||||||
|
return {
|
||||||
|
...response,
|
||||||
|
makerAssetAmount: new BigNumber(response.makerAssetAmount),
|
||||||
|
takerAssetAmount: new BigNumber(response.takerAssetAmount),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return responses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _isValidRfqtIndicativeQuoteResponse(response: RfqtIndicativeQuoteResponse): boolean {
|
||||||
|
const hasValidMakerAssetAmount = this._schemaValidator.isValid(
|
||||||
|
response.makerAssetAmount,
|
||||||
|
schemas.wholeNumberSchema,
|
||||||
|
);
|
||||||
|
const hasValidTakerAssetAmount = this._schemaValidator.isValid(
|
||||||
|
response.takerAssetAmount,
|
||||||
|
schemas.wholeNumberSchema,
|
||||||
|
);
|
||||||
|
const hasValidMakerAssetData = this._schemaValidator.isValid(response.makerAssetData, schemas.hexSchema);
|
||||||
|
const hasValidTakerAssetData = this._schemaValidator.isValid(response.takerAssetData, schemas.hexSchema);
|
||||||
|
if (hasValidMakerAssetAmount && hasValidTakerAssetAmount && hasValidMakerAssetData && hasValidTakerAssetData) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,27 @@ export const rfqtMocker = {
|
|||||||
.replyOnce(responseCode, responseData);
|
.replyOnce(responseCode, responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await performFn();
|
||||||
|
} finally {
|
||||||
|
// Ensure we always restore axios afterwards
|
||||||
|
mockedAxios.restore();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
withMockedRfqtIndicativeQuotes: async (
|
||||||
|
mockedResponses: MockedRfqtFirmQuoteResponse[],
|
||||||
|
performFn: () => Promise<void>,
|
||||||
|
) => {
|
||||||
|
const mockedAxios = new AxiosMockAdapter(axios);
|
||||||
|
try {
|
||||||
|
// Mock out RFQT responses
|
||||||
|
for (const mockedResponse of mockedResponses) {
|
||||||
|
const { endpoint, requestApiKey, requestParams, responseData, responseCode } = mockedResponse;
|
||||||
|
const requestHeaders = { Accept: 'application/json, text/plain, */*', '0x-api-key': requestApiKey };
|
||||||
|
mockedAxios
|
||||||
|
.onGet(`${endpoint}/price`, { params: requestParams }, requestHeaders)
|
||||||
|
.replyOnce(responseCode, responseData);
|
||||||
|
}
|
||||||
|
|
||||||
await performFn();
|
await performFn();
|
||||||
} finally {
|
} finally {
|
||||||
// Ensure we always restore axios afterwards
|
// Ensure we always restore axios afterwards
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ describe('QuoteRequestor', async () => {
|
|||||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerToken);
|
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerToken);
|
||||||
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerToken);
|
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerToken);
|
||||||
|
|
||||||
describe('requestRfqtFirmQuotesAsync', async () => {
|
describe('requestRfqtFirmQuotesAsync for firm quotes', async () => {
|
||||||
it('should return successful RFQT requests', async () => {
|
it('should return successful RFQT requests', async () => {
|
||||||
const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a';
|
const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a';
|
||||||
const apiKey = 'my-ko0l-api-key';
|
const apiKey = 'my-ko0l-api-key';
|
||||||
|
|||||||
Reference in New Issue
Block a user