From cc4fff49cc78ed4b168dff509eb36d4222c8dc71 Mon Sep 17 00:00:00 2001 From: ConjunctiveNormalForm Date: Tue, 11 Feb 2025 09:45:44 -0500 Subject: [PATCH 1/3] default protocols to v2 --- lib/quoters/WebhookQuoter.ts | 2 +- test/fixtures.ts | 1 + test/providers/quoters/WebhookQuoter.test.ts | 23 +++++++++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/quoters/WebhookQuoter.ts b/lib/quoters/WebhookQuoter.ts index cb41c03..946ce95 100644 --- a/lib/quoters/WebhookQuoter.ts +++ b/lib/quoters/WebhookQuoter.ts @@ -348,7 +348,7 @@ function isNonQuote(request: QuoteRequest, hookResponse: AxiosResponse, parsedRe export function getEndpointSupportedProtocols(e: WebhookConfiguration) { if (!e.supportedVersions || e.supportedVersions.length == 0) { - return [ProtocolVersion.V1]; + return [ProtocolVersion.V2]; } return e.supportedVersions; } diff --git a/test/fixtures.ts b/test/fixtures.ts index 6dac13c..774df86 100644 --- a/test/fixtures.ts +++ b/test/fixtures.ts @@ -5,6 +5,7 @@ const now = Math.floor(Date.now() / 1000); export const WEBHOOK_URL = 'https://uniswap.org'; export const WEBHOOK_URL_ONEINCH = 'https://1inch.io'; export const WEBHOOK_URL_SEARCHER = 'https://searcher.com'; +export const WEBHOOK_URL_FOO = 'https://foo.com'; export const MOCK_V2_CB_PROVIDER = new MockV2CircuitBreakerConfigurationProvider( [WEBHOOK_URL, WEBHOOK_URL_ONEINCH, WEBHOOK_URL_SEARCHER], diff --git a/test/providers/quoters/WebhookQuoter.test.ts b/test/providers/quoters/WebhookQuoter.test.ts index 2568f70..319b193 100644 --- a/test/providers/quoters/WebhookQuoter.test.ts +++ b/test/providers/quoters/WebhookQuoter.test.ts @@ -9,7 +9,13 @@ import { FirehoseLogger } from '../../../lib/providers/analytics'; import { MockFillerComplianceConfigurationProvider } from '../../../lib/providers/compliance'; import { WebhookQuoter } from '../../../lib/quoters'; import { MockFillerAddressRepository } from '../../../lib/repositories/filler-address-repository'; -import { MOCK_V2_CB_PROVIDER, WEBHOOK_URL, WEBHOOK_URL_ONEINCH, WEBHOOK_URL_SEARCHER } from '../../fixtures'; +import { + MOCK_V2_CB_PROVIDER, + WEBHOOK_URL, + WEBHOOK_URL_FOO, + WEBHOOK_URL_ONEINCH, + WEBHOOK_URL_SEARCHER, +} from '../../fixtures'; jest.mock('axios'); jest.mock('../../../lib/providers/analytics'); @@ -280,6 +286,12 @@ describe('WebhookQuoter tests', () => { hash: '0xsearcher', supportedVersions: [ProtocolVersion.V1, ProtocolVersion.V2], }, + { + name: 'foo', + endpoint: WEBHOOK_URL_FOO, + headers: {}, + hash: '0xfoo', + }, ]); const webhookQuoter = new WebhookQuoter( logger, @@ -322,6 +334,11 @@ describe('WebhookQuoter tests', () => { headers: {}, timeout: 500, }); + // empty supportedVersions defaults to v2 only + expect(mockedAxios.post).not.toBeCalledWith(WEBHOOK_URL_FOO, request.toCleanJSON(), { + headers: {}, + timeout: 500, + }); }); it('v2 quote request only sent to fillers supporting v2', async () => { @@ -356,8 +373,8 @@ describe('WebhookQuoter tests', () => { timeout: 500, } ); - // empty config defaults to v1 only - expect(mockedAxios.post).not.toBeCalledWith( + // empty config defaults to v2 only + expect(mockedAxios.post).toBeCalledWith( WEBHOOK_URL_ONEINCH, { quoteId: expect.any(String), ...request.toCleanJSON() }, { From 555516f2132b52ba10655e06df2aa1ca87490f5d Mon Sep 17 00:00:00 2001 From: ConjunctiveNormalForm Date: Tue, 11 Feb 2025 10:01:46 -0500 Subject: [PATCH 2/3] fix unit tests --- test/providers/quoters/WebhookQuoter.test.ts | 28 +++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/test/providers/quoters/WebhookQuoter.test.ts b/test/providers/quoters/WebhookQuoter.test.ts index 319b193..5c7a65b 100644 --- a/test/providers/quoters/WebhookQuoter.test.ts +++ b/test/providers/quoters/WebhookQuoter.test.ts @@ -44,9 +44,21 @@ describe('WebhookQuoter tests', () => { }); const webhookProvider = new MockWebhookConfigurationProvider([ - { name: 'uniswap', endpoint: WEBHOOK_URL, headers: {}, hash: '0xuni' }, + { + name: 'uniswap', + endpoint: WEBHOOK_URL, + headers: {}, + hash: '0xuni', + supportedVersions: [ProtocolVersion.V1, ProtocolVersion.V2], + }, { name: '1inch', endpoint: WEBHOOK_URL_ONEINCH, headers: {}, hash: '0x1inch' }, - { name: 'searcher', endpoint: WEBHOOK_URL_SEARCHER, headers: {}, hash: '0xsearcher' }, + { + name: 'searcher', + endpoint: WEBHOOK_URL_SEARCHER, + headers: {}, + hash: '0xsearcher', + supportedVersions: [ProtocolVersion.V1, ProtocolVersion.V2], + }, ]); const logger = { child: () => logger, info: jest.fn(), error: jest.fn(), debug: jest.fn() } as any; @@ -277,7 +289,13 @@ describe('WebhookQuoter tests', () => { describe('Supported protocols tests', () => { const webhookProvider = new MockWebhookConfigurationProvider([ - { name: 'uniswap', endpoint: WEBHOOK_URL, headers: {}, hash: '0xuni', supportedVersions: [ProtocolVersion.V2] }, + { + name: 'uniswap', + endpoint: WEBHOOK_URL, + headers: {}, + hash: '0xuni', + supportedVersions: [ProtocolVersion.V1, ProtocolVersion.V2], + }, { name: '1inch', endpoint: WEBHOOK_URL_ONEINCH, headers: {}, hash: '0x1inch' }, { name: 'searcher', @@ -375,7 +393,7 @@ describe('WebhookQuoter tests', () => { ); // empty config defaults to v2 only expect(mockedAxios.post).toBeCalledWith( - WEBHOOK_URL_ONEINCH, + WEBHOOK_URL_FOO, { quoteId: expect.any(String), ...request.toCleanJSON() }, { headers: {}, @@ -463,6 +481,7 @@ describe('WebhookQuoter tests', () => { emptyMockComplianceProvider, repository ); + const request = makeQuoteRequest({ tokenInChainId: 1, tokenOutChainId: 1, protocol: ProtocolVersion.V2 }); const quote = { amountOut: ethers.utils.parseEther('2').toString(), tokenIn: request.tokenIn, @@ -497,6 +516,7 @@ describe('WebhookQuoter tests', () => { }); it('Skips if chainId not configured', async () => { + const request = makeQuoteRequest({ protocol: ProtocolVersion.V2 }); const provider = new MockWebhookConfigurationProvider([ { name: 'uniswap', endpoint: WEBHOOK_URL, headers: {}, chainIds: [4, 5, 6], hash: '0xuni' }, ]); From ab0d55baead7cbbd8967a3817a7063a018c17f18 Mon Sep 17 00:00:00 2001 From: ConjunctiveNormalForm Date: Tue, 11 Feb 2025 10:38:07 -0500 Subject: [PATCH 3/3] fix unit tests --- test/handlers/quote/handler.test.ts | 79 +++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/test/handlers/quote/handler.test.ts b/test/handlers/quote/handler.test.ts index bc6c932..087e0a7 100644 --- a/test/handlers/quote/handler.test.ts +++ b/test/handlers/quote/handler.test.ts @@ -115,9 +115,9 @@ describe('Quote handler', () => { }; it('Simple request and response', async () => { - const quoters = [new MockQuoter(logger, 1, 1)]; + const quoters = [new MockQuoter(logger, 1, 1), new MockQuoter(logger, 1, 2)]; const amountIn = ethers.utils.parseEther('1'); - const request = getRequest(amountIn.toString()); + const request = getRequest(amountIn.toString(), 'EXACT_INPUT', ProtocolVersion.V2); const response: APIGatewayProxyResult = await getQuoteHandler(quoters).handler( getEvent(request), @@ -129,9 +129,9 @@ describe('Quote handler', () => { }); it('Handles hex amount', async () => { - const quoters = [new MockQuoter(logger, 1, 1)]; + const quoters = [new MockQuoter(logger, 1, 1), new MockQuoter(logger, 1, 2)]; const amountIn = ethers.utils.parseEther('1'); - const request = getRequest(amountIn.toHexString()); + const request = getRequest(amountIn.toHexString(), 'EXACT_INPUT', ProtocolVersion.V2); const response: APIGatewayProxyResult = await getQuoteHandler(quoters).handler( getEvent(request), @@ -235,6 +235,7 @@ describe('Quote handler', () => { it('Simple request and response', async () => { const webhookProvider = new MockWebhookConfigurationProvider([ { endpoint: 'https://uniswap.org', headers: {}, name: 'uniswap', hash: '0xuni' }, + { endpoint: 'https://foo.org', headers: {}, name: 'foo', hash: '0xfoo' }, ]); const quoters = [ @@ -248,7 +249,7 @@ describe('Quote handler', () => { ), ]; const amountIn = ethers.utils.parseEther('1'); - const request = getRequest(amountIn.toString()); + const request = getRequest(amountIn.toString(), 'EXACT_INPUT', ProtocolVersion.V2); mockedAxios.post .mockImplementationOnce((_endpoint, _req, _options) => { @@ -278,8 +279,35 @@ describe('Quote handler', () => { quoteId: QUOTE_ID, }, }); + }) + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + amountOut: amountIn.mul(1).toString(), + requestId: request.requestId, + tokenIn: request.tokenIn, + tokenOut: request.tokenOut, + amountIn: request.amount, + swapper: request.swapper, + chainId: request.tokenInChainId, + quoteId: QUOTE_ID, + }, + }); + }) + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + amountOut: amountIn.mul(1).toString(), + requestId: request.requestId, + tokenIn: request.tokenOut, + tokenOut: request.tokenIn, + amountIn: request.amount, + swapper: request.swapper, + chainId: request.tokenInChainId, + quoteId: QUOTE_ID, + }, + }); }); - const response: APIGatewayProxyResult = await getQuoteHandler(quoters).handler( getEvent(request), {} as unknown as Context @@ -308,6 +336,12 @@ describe('Quote handler', () => { }, hash: '0xuni', }, + { + name: 'foo', + endpoint: 'https://foo.org', + headers: {}, + hash: '0xfoo', + }, ]); const quoters = [ new WebhookQuoter( @@ -320,7 +354,7 @@ describe('Quote handler', () => { ), ]; const amountIn = ethers.utils.parseEther('1'); - const request = getRequest(amountIn.toString()); + const request = getRequest(amountIn.toString(), 'EXACT_INPUT', ProtocolVersion.V2); mockedAxios.post .mockImplementationOnce((_endpoint, _req, options: any) => { @@ -341,6 +375,30 @@ describe('Quote handler', () => { tokenOut: res.tokenIn, }, }); + }) + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + amountOut: amountIn.mul(1).toString(), + requestId: request.requestId, + tokenIn: request.tokenIn, + tokenOut: request.tokenOut, + amountIn: request.amount, + swapper: request.swapper, + chainId: request.tokenInChainId, + quoteId: QUOTE_ID, + }, + }); + }) + .mockImplementationOnce((_endpoint, _req, _options) => { + const res = responseFromRequest(request, { amountOut: amountIn.mul(1).toString() }); + return Promise.resolve({ + data: { + ...res, + tokenIn: res.tokenOut, + tokenOut: res.tokenIn, + }, + }); }); const response: APIGatewayProxyResult = await getQuoteHandler(quoters).handler( @@ -427,6 +485,7 @@ describe('Quote handler', () => { it('uses backup on failure', async () => { const webhookProvider = new MockWebhookConfigurationProvider([ { name: 'uniswap', endpoint: 'https://uniswap.org', headers: {}, hash: '0xuni' }, + { name: 'foo', endpoint: 'https://foo.org', headers: {}, hash: '0xfoo' }, ]); const quoters = [ new WebhookQuoter( @@ -438,9 +497,10 @@ describe('Quote handler', () => { repository ), new MockQuoter(logger, 1, 1), + new MockQuoter(logger, 1, 2), ]; const amountIn = ethers.utils.parseEther('1'); - const request = getRequest(amountIn.toString()); + const request = getRequest(amountIn.toString(), 'EXACT_INPUT', ProtocolVersion.V2); mockedAxios.post.mockImplementationOnce((_endpoint, _req, _options) => { return Promise.resolve({ @@ -463,6 +523,7 @@ describe('Quote handler', () => { it('uses if better than backup', async () => { const webhookProvider = new MockWebhookConfigurationProvider([ { name: 'uniswap', endpoint: 'https://uniswap.org', headers: {}, hash: '0xuni' }, + { name: 'foo', endpoint: 'https://foo.org', headers: {}, hash: '0xfoo' }, ]); const quoters = [ new WebhookQuoter( @@ -476,7 +537,7 @@ describe('Quote handler', () => { new MockQuoter(logger, 1, 1), ]; const amountIn = ethers.utils.parseEther('1'); - const request = getRequest(amountIn.toString()); + const request = getRequest(amountIn.toString(), 'EXACT_INPUT', ProtocolVersion.V2); mockedAxios.post .mockImplementationOnce((_endpoint, _req, _options) => {