asset-swapper: Restructure RFQ-T request options
There's one subtlety here: apiKey has been moved to be within the rfqt namespace, after talking to Fabio and discovering that he only needs to re-use the API key in 0x API, not in asset-swapper.
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
ForwarderExtensionContractOpts,
|
||||
OrderPrunerOpts,
|
||||
OrderPrunerPermittedFeeTypes,
|
||||
RfqtFirmQuoteRequestOpts,
|
||||
RfqtRequestOpts,
|
||||
SwapQuoteExecutionOpts,
|
||||
SwapQuoteGetOutputOpts,
|
||||
SwapQuoteRequestOpts,
|
||||
@@ -66,7 +66,7 @@ const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = {
|
||||
...DEFAULT_GET_MARKET_ORDERS_OPTS,
|
||||
};
|
||||
|
||||
const DEFAULT_RFQT_FIRM_QUOTE_REQUEST_OPTS: RfqtFirmQuoteRequestOpts = {
|
||||
const DEFAULT_RFQT_REQUEST_OPTS: Partial<RfqtRequestOpts> = {
|
||||
makerEndpointMaxResponseTimeMs: 1000,
|
||||
};
|
||||
|
||||
@@ -86,7 +86,7 @@ export const constants = {
|
||||
DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS,
|
||||
DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
|
||||
DEFAULT_PER_PAGE,
|
||||
DEFAULT_RFQT_FIRM_QUOTE_REQUEST_OPTS,
|
||||
DEFAULT_RFQT_REQUEST_OPTS,
|
||||
NULL_ERC20_ASSET_DATA,
|
||||
PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
|
||||
MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE,
|
||||
|
@@ -45,7 +45,7 @@ export {
|
||||
MarketOperation,
|
||||
MarketSellSwapQuote,
|
||||
MockedRfqtFirmQuoteResponse,
|
||||
RfqtFirmQuoteRequestOpts,
|
||||
RfqtRequestOpts,
|
||||
SwapQuote,
|
||||
SwapQuoteConsumerBase,
|
||||
SwapQuoteConsumerOpts,
|
||||
|
@@ -24,6 +24,7 @@ import { calculateLiquidity } from './utils/calculate_liquidity';
|
||||
import { MarketOperationUtils } from './utils/market_operation_utils';
|
||||
import { createDummyOrderForSampler } from './utils/market_operation_utils/orders';
|
||||
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
||||
import { GetMarketOrdersOpts } from './utils/market_operation_utils/types';
|
||||
import { orderPrunerUtils } from './utils/order_prune_utils';
|
||||
import { OrderStateUtils } from './utils/order_state_utils';
|
||||
import { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||
@@ -533,8 +534,8 @@ export class SwapQuoter {
|
||||
if (
|
||||
opts.rfqt &&
|
||||
opts.rfqt.intentOnFilling &&
|
||||
opts.apiKey &&
|
||||
this._rfqtTakerApiKeyWhitelist.includes(opts.apiKey)
|
||||
opts.rfqt.apiKey &&
|
||||
this._rfqtTakerApiKeyWhitelist.includes(opts.rfqt.apiKey)
|
||||
) {
|
||||
if (!opts.rfqt.takerAddress || opts.rfqt.takerAddress === constants.NULL_ADDRESS) {
|
||||
throw new Error('RFQ-T requests must specify a taker address');
|
||||
@@ -545,8 +546,7 @@ export class SwapQuoter {
|
||||
takerAssetData,
|
||||
assetFillAmount,
|
||||
marketOperation,
|
||||
opts.apiKey,
|
||||
opts.rfqt.takerAddress,
|
||||
opts.rfqt,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@@ -188,16 +188,19 @@ export interface SwapQuoteOrdersBreakdown {
|
||||
[source: string]: BigNumber;
|
||||
}
|
||||
|
||||
export interface RfqtRequestOpts {
|
||||
takerAddress: string;
|
||||
apiKey: string;
|
||||
intentOnFilling?: boolean;
|
||||
makerEndpointMaxResponseTimeMs?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount
|
||||
*/
|
||||
export interface SwapQuoteRequestOpts extends CalculateSwapQuoteOpts {
|
||||
gasPrice?: BigNumber;
|
||||
apiKey?: string;
|
||||
rfqt?: {
|
||||
takerAddress: string;
|
||||
intentOnFilling: boolean;
|
||||
};
|
||||
rfqt?: RfqtRequestOpts;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,10 +277,6 @@ export enum OrderPrunerPermittedFeeTypes {
|
||||
TakerDenominatedTakerFee = 'TAKER_DENOMINATED_TAKER_FEE',
|
||||
}
|
||||
|
||||
export interface RfqtFirmQuoteRequestOpts {
|
||||
makerEndpointMaxResponseTimeMs?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a mocked RFQT maker responses.
|
||||
*/
|
||||
|
@@ -6,7 +6,7 @@ import Axios, { AxiosResponse } from 'axios';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants } from '../constants';
|
||||
import { MarketOperation, RfqtFirmQuoteRequestOpts } from '../types';
|
||||
import { MarketOperation, RfqtRequestOpts } from '../types';
|
||||
|
||||
/**
|
||||
* Request quotes from RFQ-T providers
|
||||
@@ -27,6 +27,17 @@ function getTokenAddressOrThrow(assetData: string): string {
|
||||
throw new Error(`Decoded asset data (${JSON.stringify(decodedAssetData)}) does not contain a token address`);
|
||||
}
|
||||
|
||||
function assertTakerAddressOrThrow(takerAddress: string | undefined): void {
|
||||
if (
|
||||
takerAddress === undefined ||
|
||||
takerAddress === '' ||
|
||||
takerAddress === '0x' ||
|
||||
takerAddress === constants.NULL_ADDRESS
|
||||
) {
|
||||
throw new Error('RFQ-T requires the presence of a taker address');
|
||||
}
|
||||
}
|
||||
|
||||
export class QuoteRequestor {
|
||||
private readonly _rfqtMakerEndpoints: string[];
|
||||
private readonly _schemaValidator: SchemaValidator = new SchemaValidator();
|
||||
@@ -40,11 +51,10 @@ export class QuoteRequestor {
|
||||
takerAssetData: string,
|
||||
assetFillAmount: BigNumber,
|
||||
marketOperation: MarketOperation,
|
||||
takerApiKey: string,
|
||||
takerAddress: string,
|
||||
options?: Partial<RfqtFirmQuoteRequestOpts>,
|
||||
options?: Partial<RfqtRequestOpts>,
|
||||
): Promise<SignedOrder[]> {
|
||||
const { makerEndpointMaxResponseTimeMs } = _.merge({}, constants.DEFAULT_RFQT_FIRM_QUOTE_REQUEST_OPTS, options);
|
||||
const _opts = _.merge({}, constants.DEFAULT_RFQT_REQUEST_OPTS, options);
|
||||
assertTakerAddressOrThrow(_opts.takerAddress);
|
||||
|
||||
const buyToken = getTokenAddressOrThrow(makerAssetData);
|
||||
const sellToken = getTokenAddressOrThrow(takerAssetData);
|
||||
@@ -55,20 +65,22 @@ export class QuoteRequestor {
|
||||
this._rfqtMakerEndpoints.map(async rfqtMakerEndpoint => {
|
||||
try {
|
||||
return await Axios.get<SignedOrder>(`${rfqtMakerEndpoint}/quote`, {
|
||||
headers: { '0x-api-key': takerApiKey },
|
||||
headers: { '0x-api-key': _opts.apiKey },
|
||||
params: {
|
||||
sellToken,
|
||||
buyToken,
|
||||
buyAmount: marketOperation === MarketOperation.Buy ? assetFillAmount.toString() : undefined,
|
||||
sellAmount:
|
||||
marketOperation === MarketOperation.Sell ? assetFillAmount.toString() : undefined,
|
||||
takerAddress,
|
||||
takerAddress: _opts.takerAddress,
|
||||
},
|
||||
timeout: makerEndpointMaxResponseTimeMs,
|
||||
timeout: _opts.makerEndpointMaxResponseTimeMs,
|
||||
});
|
||||
} catch (err) {
|
||||
logUtils.warn(
|
||||
`Failed to get RFQ-T quote from market maker endpoint ${rfqtMakerEndpoint} for API key ${takerApiKey} for taker address ${takerAddress}`,
|
||||
`Failed to get RFQ-T quote from market maker endpoint ${rfqtMakerEndpoint} for API key ${
|
||||
_opts.apiKey
|
||||
} for taker address ${_opts.takerAddress}`,
|
||||
);
|
||||
logUtils.warn(err);
|
||||
return undefined;
|
||||
|
@@ -23,7 +23,7 @@ describe('QuoteRequestor', async () => {
|
||||
describe('requestRfqtFirmQuotesAsync', async () => {
|
||||
it('should return successful RFQT requests', async () => {
|
||||
const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a';
|
||||
const takerApiKey = 'my-ko0l-api-key';
|
||||
const apiKey = 'my-ko0l-api-key';
|
||||
|
||||
// Set up RFQT responses
|
||||
// tslint:disable-next-line:array-type
|
||||
@@ -43,7 +43,7 @@ describe('QuoteRequestor', async () => {
|
||||
});
|
||||
mockedRequests.push({
|
||||
endpoint: 'https://1337.0.0.1',
|
||||
requestApiKey: takerApiKey,
|
||||
requestApiKey: apiKey,
|
||||
requestParams: expectedParams,
|
||||
responseData: successfulOrder1,
|
||||
responseCode: StatusCodes.Success,
|
||||
@@ -51,7 +51,7 @@ describe('QuoteRequestor', async () => {
|
||||
// Test out a bad response code, ensure it doesnt cause throw
|
||||
mockedRequests.push({
|
||||
endpoint: 'https://420.0.0.1',
|
||||
requestApiKey: takerApiKey,
|
||||
requestApiKey: apiKey,
|
||||
requestParams: expectedParams,
|
||||
responseData: { error: 'bad request' },
|
||||
responseCode: StatusCodes.InternalError,
|
||||
@@ -59,7 +59,7 @@ describe('QuoteRequestor', async () => {
|
||||
// Test out a successful response code but an invalid order
|
||||
mockedRequests.push({
|
||||
endpoint: 'https://421.0.0.1',
|
||||
requestApiKey: takerApiKey,
|
||||
requestApiKey: apiKey,
|
||||
requestParams: expectedParams,
|
||||
responseData: { makerAssetData: '123' },
|
||||
responseCode: StatusCodes.Success,
|
||||
@@ -71,7 +71,7 @@ describe('QuoteRequestor', async () => {
|
||||
});
|
||||
mockedRequests.push({
|
||||
endpoint: 'https://422.0.0.1',
|
||||
requestApiKey: takerApiKey,
|
||||
requestApiKey: apiKey,
|
||||
requestParams: expectedParams,
|
||||
responseData: wrongMakerAssetDataOrder,
|
||||
responseCode: StatusCodes.Success,
|
||||
@@ -83,7 +83,7 @@ describe('QuoteRequestor', async () => {
|
||||
});
|
||||
mockedRequests.push({
|
||||
endpoint: 'https://423.0.0.1',
|
||||
requestApiKey: takerApiKey,
|
||||
requestApiKey: apiKey,
|
||||
requestParams: expectedParams,
|
||||
responseData: wrongTakerAssetDataOrder,
|
||||
responseCode: StatusCodes.Success,
|
||||
@@ -97,7 +97,7 @@ describe('QuoteRequestor', async () => {
|
||||
delete unsignedOrder.signature;
|
||||
mockedRequests.push({
|
||||
endpoint: 'https://424.0.0.1',
|
||||
requestApiKey: takerApiKey,
|
||||
requestApiKey: apiKey,
|
||||
requestParams: expectedParams,
|
||||
responseData: unsignedOrder,
|
||||
responseCode: StatusCodes.Success,
|
||||
@@ -107,7 +107,7 @@ describe('QuoteRequestor', async () => {
|
||||
const successfulOrder2 = testOrderFactory.generateTestSignedOrder({ makerAssetData, takerAssetData });
|
||||
mockedRequests.push({
|
||||
endpoint: 'https://37.0.0.1',
|
||||
requestApiKey: takerApiKey,
|
||||
requestApiKey: apiKey,
|
||||
requestParams: expectedParams,
|
||||
responseData: successfulOrder2,
|
||||
responseCode: StatusCodes.Success,
|
||||
@@ -128,8 +128,11 @@ describe('QuoteRequestor', async () => {
|
||||
takerAssetData,
|
||||
new BigNumber(10000),
|
||||
MarketOperation.Sell,
|
||||
takerApiKey,
|
||||
takerAddress,
|
||||
{
|
||||
apiKey,
|
||||
takerAddress,
|
||||
intentOnFilling: true,
|
||||
},
|
||||
);
|
||||
expect(resp.sort()).to.eql([successfulOrder1, successfulOrder2].sort());
|
||||
});
|
||||
|
Reference in New Issue
Block a user