diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index 39d1b31c0c..3e2e444a2e 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -56,7 +56,7 @@ "@0x/quote-server": "^2.0.2", "@0x/utils": "^5.5.1", "@0x/web3-wrapper": "^7.2.0", - "@balancer-labs/sor": "^0.3.0", + "@balancer-labs/sor": "0.3.2", "axios": "^0.19.2", "axios-mock-adapter": "^1.18.1", "decimal.js": "^10.2.0", diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts index 1f75620bd2..698e294d39 100644 --- a/packages/asset-swapper/src/index.ts +++ b/packages/asset-swapper/src/index.ts @@ -101,3 +101,5 @@ export { QuoteRequestor } from './utils/quote_requestor'; export { rfqtMocker } from './utils/rfqt_mocker'; import { ERC20BridgeSource } from './utils/market_operation_utils/types'; export type Native = ERC20BridgeSource.Native; + +export { AxiosInstance } from 'axios'; diff --git a/packages/asset-swapper/src/utils/quote_requestor.ts b/packages/asset-swapper/src/utils/quote_requestor.ts index 90dec24245..03e4550551 100644 --- a/packages/asset-swapper/src/utils/quote_requestor.ts +++ b/packages/asset-swapper/src/utils/quote_requestor.ts @@ -3,11 +3,18 @@ import { assetDataUtils, orderCalculationUtils, orderHashUtils, SignedOrder } fr import { RFQTFirmQuote, RFQTIndicativeQuote, TakerRequest } from '@0x/quote-server'; import { ERC20AssetData } from '@0x/types'; import { BigNumber, logUtils } from '@0x/utils'; -import Axios from 'axios'; +import Axios, { AxiosInstance } from 'axios'; +import { Agent as HttpAgent } from 'http'; +import { Agent as HttpsAgent } from 'https'; import { constants } from '../constants'; import { MarketOperation, RfqtMakerAssetOfferings, RfqtRequestOpts } from '../types'; +export const quoteRequestorHttpClient: AxiosInstance = Axios.create({ + httpAgent: new HttpAgent({ keepAlive: true }), + httpsAgent: new HttpsAgent({ keepAlive: true }), +}); + /** * Request quotes from RFQ-T providers */ @@ -330,7 +337,7 @@ export class QuoteRequestor { throw new Error(`Unexpected quote type ${quoteType}`); } })(); - const response = await Axios.get(`${url}/${quotePath}`, { + const response = await quoteRequestorHttpClient.get(`${url}/${quotePath}`, { headers: { '0x-api-key': options.apiKey }, params: requestParams, timeout: options.makerEndpointMaxResponseTimeMs, diff --git a/packages/asset-swapper/src/utils/rfqt_mocker.ts b/packages/asset-swapper/src/utils/rfqt_mocker.ts index 77e16e8925..81c89d3d91 100644 --- a/packages/asset-swapper/src/utils/rfqt_mocker.ts +++ b/packages/asset-swapper/src/utils/rfqt_mocker.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +import axios, { AxiosInstance } from 'axios'; import AxiosMockAdapter from 'axios-mock-adapter'; import { MockedRfqtFirmQuoteResponse } from '../types'; @@ -16,8 +16,9 @@ export const rfqtMocker = { withMockedRfqtFirmQuotes: async ( mockedResponses: MockedRfqtFirmQuoteResponse[], performFn: () => Promise, + axiosClient: AxiosInstance = axios, ) => { - const mockedAxios = new AxiosMockAdapter(axios); + const mockedAxios = new AxiosMockAdapter(axiosClient); try { // Mock out RFQT responses for (const mockedResponse of mockedResponses) { @@ -37,8 +38,9 @@ export const rfqtMocker = { withMockedRfqtIndicativeQuotes: async ( mockedResponses: MockedRfqtFirmQuoteResponse[], performFn: () => Promise, + axiosClient: AxiosInstance = axios, ) => { - const mockedAxios = new AxiosMockAdapter(axios); + const mockedAxios = new AxiosMockAdapter(axiosClient); try { // Mock out RFQT responses for (const mockedResponse of mockedResponses) { diff --git a/packages/asset-swapper/test/quote_requestor_test.ts b/packages/asset-swapper/test/quote_requestor_test.ts index 4bbc1e9447..afdd3e0fc6 100644 --- a/packages/asset-swapper/test/quote_requestor_test.ts +++ b/packages/asset-swapper/test/quote_requestor_test.ts @@ -7,7 +7,7 @@ import 'mocha'; import { constants } from '../src/constants'; import { MarketOperation, MockedRfqtFirmQuoteResponse, MockedRfqtIndicativeQuoteResponse } from '../src/types'; -import { QuoteRequestor } from '../src/utils/quote_requestor'; +import { QuoteRequestor, quoteRequestorHttpClient } from '../src/utils/quote_requestor'; import { rfqtMocker } from '../src/utils/rfqt_mocker'; import { chaiSetup } from './utils/chai_setup'; @@ -153,35 +153,39 @@ describe('QuoteRequestor', async () => { responseCode: StatusCodes.Success, }); - return rfqtMocker.withMockedRfqtFirmQuotes(mockedRequests, async () => { - const qr = new QuoteRequestor({ - 'https://1337.0.0.1': [[makerToken, takerToken]], - 'https://420.0.0.1': [[makerToken, takerToken]], - 'https://421.0.0.1': [[makerToken, takerToken]], - 'https://421.1.0.1': [[makerToken, takerToken]], - 'https://422.0.0.1': [[makerToken, takerToken]], - 'https://423.0.0.1': [[makerToken, takerToken]], - 'https://424.0.0.1': [[makerToken, takerToken]], - 'https://425.0.0.1': [[makerToken, takerToken]], - 'https://426.0.0.1': [] /* Shouldn't ping an RFQ-T + return rfqtMocker.withMockedRfqtFirmQuotes( + mockedRequests, + async () => { + const qr = new QuoteRequestor({ + 'https://1337.0.0.1': [[makerToken, takerToken]], + 'https://420.0.0.1': [[makerToken, takerToken]], + 'https://421.0.0.1': [[makerToken, takerToken]], + 'https://421.1.0.1': [[makerToken, takerToken]], + 'https://422.0.0.1': [[makerToken, takerToken]], + 'https://423.0.0.1': [[makerToken, takerToken]], + 'https://424.0.0.1': [[makerToken, takerToken]], + 'https://425.0.0.1': [[makerToken, takerToken]], + 'https://426.0.0.1': [] /* Shouldn't ping an RFQ-T provider when they don't support the requested asset pair. */, - 'https://37.0.0.1': [[makerToken, takerToken]], - }); - const resp = await qr.requestRfqtFirmQuotesAsync( - makerAssetData, - takerAssetData, - new BigNumber(10000), - MarketOperation.Sell, - { - apiKey, - takerAddress, - intentOnFilling: true, - }, - ); - expect(resp.sort()).to.eql( - [{ signedOrder: successfulOrder1 }, { signedOrder: successfulOrder2 }].sort(), - ); - }); + 'https://37.0.0.1': [[makerToken, takerToken]], + }); + const resp = await qr.requestRfqtFirmQuotesAsync( + makerAssetData, + takerAssetData, + new BigNumber(10000), + MarketOperation.Sell, + { + apiKey, + takerAddress, + intentOnFilling: true, + }, + ); + expect(resp.sort()).to.eql( + [{ signedOrder: successfulOrder1 }, { signedOrder: successfulOrder2 }].sort(), + ); + }, + quoteRequestorHttpClient, + ); }); }); describe('requestRfqtIndicativeQuotesAsync for Indicative quotes', async () => { @@ -255,29 +259,33 @@ describe('QuoteRequestor', async () => { responseCode: StatusCodes.Success, }); - return rfqtMocker.withMockedRfqtIndicativeQuotes(mockedRequests, async () => { - const qr = new QuoteRequestor({ - 'https://1337.0.0.1': [[makerToken, takerToken]], - 'https://420.0.0.1': [[makerToken, takerToken]], - 'https://421.0.0.1': [[makerToken, takerToken]], - 'https://422.0.0.1': [[makerToken, takerToken]], - 'https://423.0.0.1': [[makerToken, takerToken]], - 'https://424.0.0.1': [[makerToken, takerToken]], - 'https://37.0.0.1': [[makerToken, takerToken]], - }); - const resp = await qr.requestRfqtIndicativeQuotesAsync( - makerAssetData, - takerAssetData, - new BigNumber(10000), - MarketOperation.Sell, - { - apiKey, - takerAddress, - intentOnFilling: true, - }, - ); - expect(resp.sort()).to.eql([successfulQuote1, successfulQuote1].sort()); - }); + return rfqtMocker.withMockedRfqtIndicativeQuotes( + mockedRequests, + async () => { + const qr = new QuoteRequestor({ + 'https://1337.0.0.1': [[makerToken, takerToken]], + 'https://420.0.0.1': [[makerToken, takerToken]], + 'https://421.0.0.1': [[makerToken, takerToken]], + 'https://422.0.0.1': [[makerToken, takerToken]], + 'https://423.0.0.1': [[makerToken, takerToken]], + 'https://424.0.0.1': [[makerToken, takerToken]], + 'https://37.0.0.1': [[makerToken, takerToken]], + }); + const resp = await qr.requestRfqtIndicativeQuotesAsync( + makerAssetData, + takerAssetData, + new BigNumber(10000), + MarketOperation.Sell, + { + apiKey, + takerAddress, + intentOnFilling: true, + }, + ); + expect(resp.sort()).to.eql([successfulQuote1, successfulQuote1].sort()); + }, + quoteRequestorHttpClient, + ); }); it('should return successful RFQT indicative quote requests', async () => { const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a'; @@ -309,21 +317,25 @@ describe('QuoteRequestor', async () => { responseCode: StatusCodes.Success, }); - return rfqtMocker.withMockedRfqtIndicativeQuotes(mockedRequests, async () => { - const qr = new QuoteRequestor({ 'https://1337.0.0.1': [[makerToken, takerToken]] }); - const resp = await qr.requestRfqtIndicativeQuotesAsync( - makerAssetData, - takerAssetData, - new BigNumber(10000), - MarketOperation.Buy, - { - apiKey, - takerAddress, - intentOnFilling: true, - }, - ); - expect(resp.sort()).to.eql([successfulQuote1].sort()); - }); + return rfqtMocker.withMockedRfqtIndicativeQuotes( + mockedRequests, + async () => { + const qr = new QuoteRequestor({ 'https://1337.0.0.1': [[makerToken, takerToken]] }); + const resp = await qr.requestRfqtIndicativeQuotesAsync( + makerAssetData, + takerAssetData, + new BigNumber(10000), + MarketOperation.Buy, + { + apiKey, + takerAddress, + intentOnFilling: true, + }, + ); + expect(resp.sort()).to.eql([successfulQuote1].sort()); + }, + quoteRequestorHttpClient, + ); }); }); }); diff --git a/yarn.lock b/yarn.lock index 32da34e8ad..00129a7803 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1047,10 +1047,10 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@balancer-labs/sor@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-0.3.0.tgz#c221225d9a3d1791ebfc3c566f7a76843bca98fa" - integrity sha512-QTVkeDmcGCaEgBhcVSu8c7cz6HA1ueWRbniuT+Yh0N/sqcZIcDMdoCFcpq66SD+hOxQ88RvzShmJ+P/3vKbXfg== +"@balancer-labs/sor@0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-0.3.2.tgz#b05c63a07031c2ea13ed0d2670f5105cfaa40523" + integrity sha512-wmYmTm/zhnRPd/OaqzJJGo9mRIp4PvNNS/AG0EVWJmLmZvYkm2sl6/dBL3KC88rub9duVQr2M8BHCosseFuGLw== dependencies: bignumber.js "^9.0.0" ethers "^4.0.39"