diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..d2bc1232 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,30 @@ +name: Lint + +on: + push: + branches: + - main + pull_request: + +jobs: + run-linters: + name: lint + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@v2 + + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: 18.x + registry-url: https://registry.npmjs.org + + - name: Install dependencies + run: | + npm config set '//registry.npmjs.org/:_authToken' "${{ secrets.NPM_AUTH_TOKEN }}" \ + && yarn install --frozen-lockfile + + - name: Run linters + run: yarn lint diff --git a/lib/entities/QuoteRequest.ts b/lib/entities/QuoteRequest.ts index e96fb21a..5f773cee 100644 --- a/lib/entities/QuoteRequest.ts +++ b/lib/entities/QuoteRequest.ts @@ -89,7 +89,7 @@ export class QuoteRequest { ...(this.quoteId && { quoteId: this.quoteId }), }; } - + public toOpposingRequest(): QuoteRequest { const opposingJSON = this.toOpposingCleanJSON(); return new QuoteRequest({ @@ -97,7 +97,7 @@ export class QuoteRequest { amount: BigNumber.from(opposingJSON.amount), type: TradeType[opposingJSON.type as keyof typeof TradeType], }); -} + } public get requestId(): string { return this.data.requestId; diff --git a/lib/entities/analytics-events.ts b/lib/entities/analytics-events.ts index 93345e22..7ef15c09 100644 --- a/lib/entities/analytics-events.ts +++ b/lib/entities/analytics-events.ts @@ -1,9 +1,10 @@ import { v4 as uuidv4 } from 'uuid'; + import { timestampInMstoISOString } from '../util/time'; export enum AnalyticsEventType { WEBHOOK_RESPONSE = 'WebhookQuoterResponse', -}; +} export enum WebhookResponseType { OK = 'OK', @@ -13,7 +14,7 @@ export enum WebhookResponseType { TIMEOUT = 'TIMEOUT', HTTP_ERROR = 'HTTP_ERROR', OTHER_ERROR = 'OTHER_ERROR', -}; +} export class AnalyticsEvent { eventId: string; // gets set in constructor @@ -27,4 +28,4 @@ export class AnalyticsEvent { this.eventTime = timestampInMstoISOString(Date.now()); this.eventProperties = eventProperties; } -}; +} diff --git a/lib/entities/index.ts b/lib/entities/index.ts index 629a371f..2e97229b 100644 --- a/lib/entities/index.ts +++ b/lib/entities/index.ts @@ -1,4 +1,4 @@ +export * from './analytics-events'; export * from './aws-metrics-logger'; export * from './QuoteRequest'; export * from './QuoteResponse'; -export * from './analytics-events'; diff --git a/lib/handlers/quote/injector.ts b/lib/handlers/quote/injector.ts index 72538309..21460a76 100644 --- a/lib/handlers/quote/injector.ts +++ b/lib/handlers/quote/injector.ts @@ -10,8 +10,8 @@ import { FADE_RATE_BUCKET, FADE_RATE_S3_KEY, INTEGRATION_S3_KEY, - PRODUCTION_S3_KEY, PROD_COMPLIANCE_S3_KEY, + PRODUCTION_S3_KEY, WEBHOOK_CONFIG_BUCKET, } from '../../constants'; import { diff --git a/lib/providers/analytics/firehose.ts b/lib/providers/analytics/firehose.ts index 5b84ab46..96ec3ddf 100644 --- a/lib/providers/analytics/firehose.ts +++ b/lib/providers/analytics/firehose.ts @@ -1,8 +1,8 @@ import { FirehoseClient, PutRecordCommand } from '@aws-sdk/client-firehose'; import { default as Logger } from 'bunyan'; -import { IAnalyticsLogger } from '.'; import { AnalyticsEvent } from '../../entities/analytics-events'; +import { IAnalyticsLogger } from '.'; export class FirehoseLogger implements IAnalyticsLogger { private log: Logger; diff --git a/lib/providers/compliance/index.ts b/lib/providers/compliance/index.ts index b50cc4ad..269ac4c5 100644 --- a/lib/providers/compliance/index.ts +++ b/lib/providers/compliance/index.ts @@ -5,7 +5,7 @@ export interface FillerComplianceConfiguration { export interface FillerComplianceConfigurationProvider { getConfigs(): Promise; - // getExcludedAddrToEndpointsMap(): Promise>>; + // getExcludedAddrToEndpointsMap(): Promise>>; getEndpointToExcludedAddrsMap(): Promise>>; } diff --git a/lib/providers/compliance/mock.ts b/lib/providers/compliance/mock.ts index 4e48d1d7..61664760 100644 --- a/lib/providers/compliance/mock.ts +++ b/lib/providers/compliance/mock.ts @@ -6,7 +6,7 @@ export class MockFillerComplianceConfigurationProvider implements FillerComplian async getConfigs(): Promise { return this.configs; } - + async getEndpointToExcludedAddrsMap(): Promise>> { const map = new Map>(); this.configs.forEach((config) => { @@ -18,8 +18,7 @@ export class MockFillerComplianceConfigurationProvider implements FillerComplian map.get(endpoint)?.add(address); }); }); - }) + }); return map; } - -} \ No newline at end of file +} diff --git a/lib/providers/compliance/s3.ts b/lib/providers/compliance/s3.ts index f5248f5d..c2ce6ca7 100644 --- a/lib/providers/compliance/s3.ts +++ b/lib/providers/compliance/s3.ts @@ -1,11 +1,9 @@ - import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'; import { default as Logger } from 'bunyan'; import { checkDefined } from '../../preconditions/preconditions'; import { FillerComplianceConfiguration, FillerComplianceConfigurationProvider } from '.'; - export class S3FillerComplianceConfigurationProvider implements FillerComplianceConfigurationProvider { private log: Logger; private configs: FillerComplianceConfiguration[]; @@ -32,14 +30,12 @@ export class S3FillerComplianceConfigurationProvider implements FillerCompliance this.endpointToExcludedAddrsMap.get(endpoint)?.add(address); }); }); - }) + }); return this.endpointToExcludedAddrsMap; } async getConfigs(): Promise { - if ( - this.configs.length === 0 - ) { + if (this.configs.length === 0) { await this.fetchConfigs(); } return this.configs; @@ -57,4 +53,4 @@ export class S3FillerComplianceConfigurationProvider implements FillerCompliance this.configs = JSON.parse(await s3Body.transformToString()) as FillerComplianceConfiguration[]; this.log.info({ configsLength: this.configs.map((c) => c.addresses.length) }, `Fetched configs`); } -} \ No newline at end of file +} diff --git a/lib/quoters/MockQuoter.ts b/lib/quoters/MockQuoter.ts index 82d9ec4b..e5c91752 100644 --- a/lib/quoters/MockQuoter.ts +++ b/lib/quoters/MockQuoter.ts @@ -1,8 +1,8 @@ import Logger from 'bunyan'; import { BigNumber } from 'ethers'; -import { Quoter, QuoterType } from '.'; import { QuoteRequest, QuoteResponse } from '../entities'; +import { Quoter, QuoterType } from '.'; export const MOCK_FILLER_ADDRESS = '0x0000000000000000000000000000000000000001'; diff --git a/test/entities/QuoteRequest.test.ts b/test/entities/QuoteRequest.test.ts index b6912eb8..58c708b3 100644 --- a/test/entities/QuoteRequest.test.ts +++ b/test/entities/QuoteRequest.test.ts @@ -53,7 +53,7 @@ describe('QuoteRequest', () => { numOutputs: 1, }); }); - + it('toOpposingRequest', async () => { const opposingRequest = request.toOpposingRequest(); expect(opposingRequest.toCleanJSON()).toEqual({ diff --git a/test/handlers/quote/handler.test.ts b/test/handlers/quote/handler.test.ts index 9fb9a096..384bc78c 100644 --- a/test/handlers/quote/handler.test.ts +++ b/test/handlers/quote/handler.test.ts @@ -13,8 +13,8 @@ import { RequestInjected, } from '../../../lib/handlers/quote'; import { QuoteHandler } from '../../../lib/handlers/quote/handler'; -import { FirehoseLogger } from '../../../lib/providers/analytics'; import { MockWebhookConfigurationProvider } from '../../../lib/providers'; +import { FirehoseLogger } from '../../../lib/providers/analytics'; import { MockCircuitBreakerConfigurationProvider } from '../../../lib/providers/circuit-breaker/mock'; import { MockFillerComplianceConfigurationProvider } from '../../../lib/providers/compliance'; import { MOCK_FILLER_ADDRESS, MockQuoter, Quoter, WebhookQuoter } from '../../../lib/quoters'; @@ -34,10 +34,13 @@ const logger = Logger.createLogger({ name: 'test' }); logger.level(Logger.FATAL); const emptyMockComplianceProvider = new MockFillerComplianceConfigurationProvider([]); -const mockComplianceProvider = new MockFillerComplianceConfigurationProvider([{ - endpoints: ['https://uniswap.org', 'google.com'], addresses: [SWAPPER] -}]); -const mockFirehoseLogger = new FirehoseLogger(logger, "arn:aws:deliverystream/dummy"); +const mockComplianceProvider = new MockFillerComplianceConfigurationProvider([ + { + endpoints: ['https://uniswap.org', 'google.com'], + addresses: [SWAPPER], + }, +]); +const mockFirehoseLogger = new FirehoseLogger(logger, 'arn:aws:deliverystream/dummy'); describe('Quote handler', () => { // Creating mocks for all the handler dependencies. @@ -220,37 +223,47 @@ describe('Quote handler', () => { const circuitBreakerProvider = new MockCircuitBreakerConfigurationProvider([ { fadeRate: 0.02, enabled: true, hash: '0xuni' }, ]); - const quoters = [new WebhookQuoter(logger, mockFirehoseLogger, webhookProvider, circuitBreakerProvider, emptyMockComplianceProvider)]; + const quoters = [ + new WebhookQuoter( + logger, + mockFirehoseLogger, + webhookProvider, + circuitBreakerProvider, + emptyMockComplianceProvider + ), + ]; const amountIn = ethers.utils.parseEther('1'); const request = getRequest(amountIn.toString()); - mockedAxios.post.mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: { - amountOut: amountIn.mul(2).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(3).toString(), - requestId: request.requestId, - tokenIn: request.tokenOut, - tokenOut: request.tokenIn, - amountIn: request.amount, - swapper: request.swapper, - chainId: request.tokenInChainId, - quoteId: QUOTE_ID, - }, + mockedAxios.post + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + amountOut: amountIn.mul(2).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(3).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), @@ -284,28 +297,38 @@ describe('Quote handler', () => { const circuitBreakerProvider = new MockCircuitBreakerConfigurationProvider([ { hash: '0xuni', fadeRate: 0.02, enabled: true }, ]); - const quoters = [new WebhookQuoter(logger, mockFirehoseLogger, webhookProvider, circuitBreakerProvider, emptyMockComplianceProvider)]; + const quoters = [ + new WebhookQuoter( + logger, + mockFirehoseLogger, + webhookProvider, + circuitBreakerProvider, + emptyMockComplianceProvider + ), + ]; const amountIn = ethers.utils.parseEther('1'); const request = getRequest(amountIn.toString()); - mockedAxios.post.mockImplementationOnce((_endpoint, _req, options: any) => { - expect(options.headers['X-Authentication']).toEqual('1234'); - return Promise.resolve({ - data: { - ...responseFromRequest(request, { amountOut: amountIn.mul(2).toString() }), - }, - }); - }).mockImplementationOnce((_endpoint, _req, options: any) => { - expect(options.headers['X-Authentication']).toEqual('1234'); - const res = responseFromRequest(request, { amountOut: amountIn.mul(3).toString() }); - return Promise.resolve({ - data: { - ...res, - tokenIn: res.tokenOut, - tokenOut: res.tokenIn, - }, + mockedAxios.post + .mockImplementationOnce((_endpoint, _req, options: any) => { + expect(options.headers['X-Authentication']).toEqual('1234'); + return Promise.resolve({ + data: { + ...responseFromRequest(request, { amountOut: amountIn.mul(2).toString() }), + }, + }); + }) + .mockImplementationOnce((_endpoint, _req, options: any) => { + expect(options.headers['X-Authentication']).toEqual('1234'); + const res = responseFromRequest(request, { amountOut: amountIn.mul(3).toString() }); + return Promise.resolve({ + data: { + ...res, + tokenIn: res.tokenOut, + tokenOut: res.tokenIn, + }, + }); }); - }); const response: APIGatewayProxyResult = await getQuoteHandler(quoters).handler( getEvent(request), @@ -330,7 +353,15 @@ describe('Quote handler', () => { const circuitBreakerProvider = new MockCircuitBreakerConfigurationProvider([ { hash: '0xuni', fadeRate: 0.02, enabled: true }, ]); - const quoters = [new WebhookQuoter(logger, mockFirehoseLogger, webhookProvider, circuitBreakerProvider, emptyMockComplianceProvider)]; + const quoters = [ + new WebhookQuoter( + logger, + mockFirehoseLogger, + webhookProvider, + circuitBreakerProvider, + emptyMockComplianceProvider + ), + ]; const amountIn = ethers.utils.parseEther('1'); const request = getRequest(amountIn.toString()); @@ -356,7 +387,15 @@ describe('Quote handler', () => { const circuitBreakerProvider = new MockCircuitBreakerConfigurationProvider([ { hash: '0xuni', fadeRate: 0.02, enabled: true }, ]); - const quoters = [new WebhookQuoter(logger, mockFirehoseLogger, webhookProvider, circuitBreakerProvider, emptyMockComplianceProvider)]; + const quoters = [ + new WebhookQuoter( + logger, + mockFirehoseLogger, + webhookProvider, + circuitBreakerProvider, + emptyMockComplianceProvider + ), + ]; const amountIn = ethers.utils.parseEther('1'); const request = getRequest(amountIn.toString()); @@ -384,7 +423,13 @@ describe('Quote handler', () => { { hash: '0xuni', fadeRate: 0.02, enabled: true }, ]); const quoters = [ - new WebhookQuoter(logger, mockFirehoseLogger, webhookProvider, circuitBreakerProvider, emptyMockComplianceProvider), + new WebhookQuoter( + logger, + mockFirehoseLogger, + webhookProvider, + circuitBreakerProvider, + emptyMockComplianceProvider + ), new MockQuoter(logger, 1, 1), ]; const amountIn = ethers.utils.parseEther('1'); @@ -416,39 +461,47 @@ describe('Quote handler', () => { { hash: '0xuni', fadeRate: 0.02, enabled: true }, ]); const quoters = [ - new WebhookQuoter(logger, mockFirehoseLogger, webhookProvider, circuitBreakerProvider, emptyMockComplianceProvider), + new WebhookQuoter( + logger, + mockFirehoseLogger, + webhookProvider, + circuitBreakerProvider, + emptyMockComplianceProvider + ), new MockQuoter(logger, 1, 1), ]; const amountIn = ethers.utils.parseEther('1'); const request = getRequest(amountIn.toString()); - mockedAxios.post.mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: { - amountOut: amountIn.mul(2).toString(), - tokenIn: request.tokenIn, - tokenOut: request.tokenOut, - amountIn: request.amount, - swapper: request.swapper, - chainId: request.tokenInChainId, - requestId: request.requestId, - quoteId: QUOTE_ID, - }, - }); - }).mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: { - amountOut: amountIn.div(2).toString(), - tokenIn: request.tokenOut, - tokenOut: request.tokenIn, - amountIn: request.amount, - swapper: request.swapper, - chainId: request.tokenInChainId, - requestId: request.requestId, - quoteId: QUOTE_ID, - }, + mockedAxios.post + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + amountOut: amountIn.mul(2).toString(), + tokenIn: request.tokenIn, + tokenOut: request.tokenOut, + amountIn: request.amount, + swapper: request.swapper, + chainId: request.tokenInChainId, + requestId: request.requestId, + quoteId: QUOTE_ID, + }, + }); + }) + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + amountOut: amountIn.div(2).toString(), + tokenIn: request.tokenOut, + tokenOut: request.tokenIn, + amountIn: request.amount, + swapper: request.swapper, + chainId: request.tokenInChainId, + requestId: request.requestId, + quoteId: QUOTE_ID, + }, + }); }); - }); const response: APIGatewayProxyResult = await getQuoteHandler(quoters).handler( getEvent(request), @@ -470,7 +523,13 @@ describe('Quote handler', () => { { hash: '0xuni', fadeRate: 0.02, enabled: true }, ]); const quoters = [ - new WebhookQuoter(logger, mockFirehoseLogger, webhookProvider, circuitBreakerProvider, emptyMockComplianceProvider), + new WebhookQuoter( + logger, + mockFirehoseLogger, + webhookProvider, + circuitBreakerProvider, + emptyMockComplianceProvider + ), new MockQuoter(logger, 1, 1), ]; const amountIn = ethers.utils.parseEther('1'); @@ -502,7 +561,7 @@ describe('Quote handler', () => { quoteId: expect.any(String), }); }); - + it('respects filler compliance requirements', async () => { const webhookProvider = new MockWebhookConfigurationProvider([ { name: 'uniswap', endpoint: 'https://uniswap.org', headers: {}, hash: '0xuni' }, @@ -527,7 +586,7 @@ describe('Quote handler', () => { errorCode: 'QUOTE_ERROR', detail: 'No quotes available', }) - ) - }) + ); + }); }); }); diff --git a/test/integ/quote.test.ts b/test/integ/quote.test.ts index c7f4261b..66b225bf 100644 --- a/test/integ/quote.test.ts +++ b/test/integ/quote.test.ts @@ -1,4 +1,4 @@ -import chai, {expect} from 'chai'; +import chai, { expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; import chaiSubset from 'chai-subset'; import { v4 as uuidv4 } from 'uuid'; @@ -32,13 +32,13 @@ describe('Quote endpoint integration test', function () { tokenOut: TOKEN_OUT, amount: '1', type: 'EXACT_INPUT', - numOutputs: 1 + numOutputs: 1, }; - const {data, status} = await AxiosUtils.callPassThroughFail('POST', API, quoteReq); + const { data, status } = await AxiosUtils.callPassThroughFail('POST', API, quoteReq); expect([404, 200]).to.include(status); if (status == 404) { - expect(data.detail).to.be.equal('No quotes available') + expect(data.detail).to.be.equal('No quotes available'); } else { expect(data.requestId).to.be.equal(REQUEST_ID); expect(data.swapper).to.be.equal(SWAPPER); diff --git a/test/providers/compliance/s3.test.ts b/test/providers/compliance/s3.test.ts index 0fe689cd..1c8fb9e2 100644 --- a/test/providers/compliance/s3.test.ts +++ b/test/providers/compliance/s3.test.ts @@ -1,7 +1,10 @@ import { S3Client } from '@aws-sdk/client-s3'; import { default as Logger } from 'bunyan'; -import { FillerComplianceConfiguration,S3FillerComplianceConfigurationProvider } from '../../../lib/providers/compliance'; +import { + FillerComplianceConfiguration, + S3FillerComplianceConfigurationProvider, +} from '../../../lib/providers/compliance'; const mockConfigs = [ { @@ -14,7 +17,6 @@ const mockConfigs = [ }, ]; - function applyMock(configs: FillerComplianceConfiguration[]) { jest.spyOn(S3Client.prototype, 'send').mockImplementationOnce(() => Promise.resolve({ @@ -25,7 +27,6 @@ function applyMock(configs: FillerComplianceConfiguration[]) { ); } - // silent logger in tests const logger = Logger.createLogger({ name: 'test' }); logger.level(Logger.FATAL); @@ -33,7 +34,7 @@ logger.level(Logger.FATAL); describe('S3ComplianceConfigurationProvider', () => { const bucket = 'test-bucket'; const key = 'test-key'; - + afterEach(() => { jest.clearAllMocks(); }); @@ -44,16 +45,16 @@ describe('S3ComplianceConfigurationProvider', () => { const endpoints = await provider.getConfigs(); expect(endpoints).toEqual(mockConfigs); }); - + it('generates endpoint to addrs map', async () => { applyMock(mockConfigs); const provider = new S3FillerComplianceConfigurationProvider(logger, bucket, key); - const map = await provider.getEndpointToExcludedAddrsMap(); + const map = await provider.getEndpointToExcludedAddrsMap(); expect(map).toMatchObject( new Map([ ['https://google.com', new Set(['0x1234'])], ['https://meta.com', new Set(['0x1234', '0x5678'])], ]) - ) + ); }); }); diff --git a/test/providers/quoters/WebhookQuoter.test.ts b/test/providers/quoters/WebhookQuoter.test.ts index 4595e51f..333e5b95 100644 --- a/test/providers/quoters/WebhookQuoter.test.ts +++ b/test/providers/quoters/WebhookQuoter.test.ts @@ -2,12 +2,12 @@ import { TradeType } from '@uniswap/sdk-core'; import axios from 'axios'; import { BigNumber, ethers } from 'ethers'; -import { QuoteRequest, AnalyticsEventType, WebhookResponseType } from '../../../lib/entities'; +import { AnalyticsEventType, QuoteRequest, WebhookResponseType } from '../../../lib/entities'; import { MockWebhookConfigurationProvider } from '../../../lib/providers'; +import { FirehoseLogger } from '../../../lib/providers/analytics'; import { MockCircuitBreakerConfigurationProvider } from '../../../lib/providers/circuit-breaker/mock'; import { MockFillerComplianceConfigurationProvider } from '../../../lib/providers/compliance'; import { WebhookQuoter } from '../../../lib/quoters'; -import { FirehoseLogger } from '../../../lib/providers/analytics'; jest.mock('axios'); jest.mock('../../../lib/providers/analytics'); @@ -26,9 +26,12 @@ const WEBHOOK_URL_ONEINCH = 'https://1inch.io'; const WEBHOOK_URL_SEARCHER = 'https://searcher.com'; const emptyMockComplianceProvider = new MockFillerComplianceConfigurationProvider([]); -const mockComplianceProvider = new MockFillerComplianceConfigurationProvider([{ - endpoints: ['https://uniswap.org', 'google.com'], addresses: [SWAPPER] -}]); +const mockComplianceProvider = new MockFillerComplianceConfigurationProvider([ + { + endpoints: ['https://uniswap.org', 'google.com'], + addresses: [SWAPPER], + }, +]); describe('WebhookQuoter tests', () => { afterEach(() => { @@ -36,9 +39,9 @@ describe('WebhookQuoter tests', () => { }); const webhookProvider = new MockWebhookConfigurationProvider([ - { name: 'uniswap', endpoint: WEBHOOK_URL, headers: {}, hash: "0xuni" }, - { name: '1inch', endpoint: WEBHOOK_URL_ONEINCH, headers: {}, hash: "0x1inch" }, - { name: 'searcher', endpoint: WEBHOOK_URL_SEARCHER, headers: {}, hash: "0xsearcher" }, + { name: 'uniswap', endpoint: WEBHOOK_URL, headers: {}, hash: '0xuni' }, + { name: '1inch', endpoint: WEBHOOK_URL_ONEINCH, headers: {}, hash: '0x1inch' }, + { name: 'searcher', endpoint: WEBHOOK_URL_SEARCHER, headers: {}, hash: '0xsearcher' }, ]); const circuitBreakerProvider = new MockCircuitBreakerConfigurationProvider([ { hash: '0xuni', fadeRate: 0.05, enabled: true }, @@ -46,8 +49,14 @@ describe('WebhookQuoter tests', () => { { hash: '0xsearcher', fadeRate: 0.1, enabled: true }, ]); const logger = { child: () => logger, info: jest.fn(), error: jest.fn(), debug: jest.fn() } as any; - const mockFirehoseLogger = new FirehoseLogger(logger, "arn:aws:deliverystream/dummy"); - const webhookQuoter = new WebhookQuoter(logger, mockFirehoseLogger, webhookProvider, circuitBreakerProvider, emptyMockComplianceProvider); + const mockFirehoseLogger = new FirehoseLogger(logger, 'arn:aws:deliverystream/dummy'); + const webhookQuoter = new WebhookQuoter( + logger, + mockFirehoseLogger, + webhookProvider, + circuitBreakerProvider, + emptyMockComplianceProvider + ); const request = new QuoteRequest({ tokenInChainId: CHAIN_ID, @@ -85,32 +94,34 @@ describe('WebhookQuoter tests', () => { }; it('Simple request and response', async () => { - mockedAxios.post.mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: quote, - }); - }).mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: { - ...quote, - tokenIn: request.tokenOut, - tokenOut: request.tokenIn, - } + mockedAxios.post + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: quote, + }); + }) + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + ...quote, + tokenIn: request.tokenOut, + tokenOut: request.tokenIn, + }, + }); }); - }); const response = await webhookQuoter.quote(request); expect(response.length).toEqual(1); expect(response[0].toResponseJSON()).toEqual({ ...quote, quoteId: expect.any(String) }); }); - + it('Respects filler compliance requirements', async () => { const webhookQuoter = new WebhookQuoter( logger, mockFirehoseLogger, webhookProvider, circuitBreakerProvider, - mockComplianceProvider, + mockComplianceProvider ); await expect(webhookQuoter.quote(request)).resolves.toStrictEqual([]); @@ -118,19 +129,21 @@ describe('WebhookQuoter tests', () => { // should only call 'uniswap' and 'searcher' given they are enabled in the config it('Only calls to eligible endpoints', async () => { - mockedAxios.post.mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: quote, - }); - }).mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: { - ...quote, - tokenIn: request.tokenOut, - tokenOut: request.tokenIn, - } + mockedAxios.post + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: quote, + }); + }) + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + ...quote, + tokenIn: request.tokenOut, + tokenOut: request.tokenIn, + }, + }); }); - }); await webhookQuoter.quote(request); expect(mockedAxios.post).toBeCalledWith( @@ -188,10 +201,14 @@ describe('WebhookQuoter tests', () => { data: quote, }); }); - const cbProvider = new MockCircuitBreakerConfigurationProvider([ - { hash: '0xuni', fadeRate: 0.05, enabled: true }, - ]); - const quoter = new WebhookQuoter(logger, mockFirehoseLogger, webhookProvider, cbProvider, emptyMockComplianceProvider); + const cbProvider = new MockCircuitBreakerConfigurationProvider([{ hash: '0xuni', fadeRate: 0.05, enabled: true }]); + const quoter = new WebhookQuoter( + logger, + mockFirehoseLogger, + webhookProvider, + cbProvider, + emptyMockComplianceProvider + ); await quoter.quote(request); expect(mockedAxios.post).toBeCalledWith( WEBHOOK_URL, @@ -214,19 +231,21 @@ describe('WebhookQuoter tests', () => { }); it('Simple request and response no swapper', async () => { - mockedAxios.post.mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: quote, - }); - }).mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: { - ...quote, - tokenIn: request.tokenOut, - tokenOut: request.tokenIn, - } + mockedAxios.post + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: quote, + }); + }) + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + ...quote, + tokenIn: request.tokenOut, + tokenOut: request.tokenIn, + }, + }); }); - }); const response = await webhookQuoter.quote(request); expect(response.length).toEqual(1); @@ -256,19 +275,21 @@ describe('WebhookQuoter tests', () => { filler: FILLER, }; - mockedAxios.post.mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: quote, - }); - }).mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: { - ...quote, - tokenIn: request.tokenOut, - tokenOut: request.tokenIn, - } + mockedAxios.post + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: quote, + }); + }) + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + ...quote, + tokenIn: request.tokenOut, + tokenOut: request.tokenIn, + }, + }); }); - }); const response = await webhookQuoter.quote(request); expect(response.length).toEqual(1); @@ -277,9 +298,15 @@ describe('WebhookQuoter tests', () => { it('Simple request and response with explicit chainId', async () => { const provider = new MockWebhookConfigurationProvider([ - { name: 'uniswap', endpoint: WEBHOOK_URL, headers: {}, chainIds: [1], hash: "0xuni" }, + { name: 'uniswap', endpoint: WEBHOOK_URL, headers: {}, chainIds: [1], hash: '0xuni' }, ]); - const quoter = new WebhookQuoter(logger, mockFirehoseLogger, provider, circuitBreakerProvider, emptyMockComplianceProvider); + const quoter = new WebhookQuoter( + logger, + mockFirehoseLogger, + provider, + circuitBreakerProvider, + emptyMockComplianceProvider + ); const quote = { amountOut: ethers.utils.parseEther('2').toString(), tokenIn: request.tokenIn, @@ -292,19 +319,21 @@ describe('WebhookQuoter tests', () => { filler: FILLER, }; - mockedAxios.post.mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: quote, - }); - }).mockImplementationOnce((_endpoint, _req, _options) => { - return Promise.resolve({ - data: { - ...quote, - tokenIn: request.tokenOut, - tokenOut: request.tokenIn, - } + mockedAxios.post + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: quote, + }); + }) + .mockImplementationOnce((_endpoint, _req, _options) => { + return Promise.resolve({ + data: { + ...quote, + tokenIn: request.tokenOut, + tokenOut: request.tokenIn, + }, + }); }); - }); const response = await quoter.quote(request); expect(response.length).toEqual(1); @@ -313,9 +342,15 @@ describe('WebhookQuoter tests', () => { it('Skips if chainId not configured', async () => { const provider = new MockWebhookConfigurationProvider([ - { name: 'uniswap', endpoint: WEBHOOK_URL, headers: {}, chainIds: [4, 5, 6], hash: "0xuni" }, + { name: 'uniswap', endpoint: WEBHOOK_URL, headers: {}, chainIds: [4, 5, 6], hash: '0xuni' }, ]); - const quoter = new WebhookQuoter(logger, mockFirehoseLogger, provider, circuitBreakerProvider, emptyMockComplianceProvider); + const quoter = new WebhookQuoter( + logger, + mockFirehoseLogger, + provider, + circuitBreakerProvider, + emptyMockComplianceProvider + ); const response = await quoter.quote(request); @@ -416,7 +451,7 @@ describe('WebhookQuoter tests', () => { responseRequestId: quote.requestId, }, 'Webhook ResponseId does not match request' - ); + ); expect(mockFirehoseLogger.sendAnalyticsEvent).toHaveBeenCalledWith( expect.objectContaining({ eventType: AnalyticsEventType.WEBHOOK_RESPONSE, @@ -426,8 +461,8 @@ describe('WebhookQuoter tests', () => { data: quote, responseType: WebhookResponseType.REQUEST_ID_MISMATCH, mismatchedRequestId: quote.requestId, - } - }), + }, + }) ); expect(response).toEqual([]); }); @@ -455,8 +490,8 @@ describe('WebhookQuoter tests', () => { status: 404, data: '', responseType: WebhookResponseType.NON_QUOTE, - } - }), + }, + }) ); expect(response.length).toEqual(0); }); @@ -498,8 +533,8 @@ describe('WebhookQuoter tests', () => { status: 200, data: quote, responseType: WebhookResponseType.NON_QUOTE, - } - }), + }, + }) ); }); @@ -552,8 +587,8 @@ describe('WebhookQuoter tests', () => { status: 200, data: quote, responseType: WebhookResponseType.NON_QUOTE, - } - }), + }, + }) ); }); }); diff --git a/test/providers/webhook/s3.test.ts b/test/providers/webhook/s3.test.ts index c2bcefd4..4884dbeb 100644 --- a/test/providers/webhook/s3.test.ts +++ b/test/providers/webhook/s3.test.ts @@ -11,13 +11,13 @@ const mockEndpoints = [ 'x-api-key': '1234', }, addresses: ['google.com'], - hash: '0xgoogle' + hash: '0xgoogle', }, { name: 'meta', endpoint: 'https://meta.com', addresses: ['facebook.com', 'meta.com'], - hash: '0xmeta' + hash: '0xmeta', }, ]; diff --git a/test/repositories/switch-repository.test.ts b/test/repositories/switch-repository.test.ts index b0882a6f..908e0268 100644 --- a/test/repositories/switch-repository.test.ts +++ b/test/repositories/switch-repository.test.ts @@ -62,7 +62,7 @@ describe('put switch tests', () => { const enabled = await switchRepository.syntheticQuoteForTradeEnabled(SWITCH); expect(enabled).toBe(false); - }) + }); it('should return false for non-existent switch', async () => { await expect(switchRepository.syntheticQuoteForTradeEnabled(NONEXISTENT_SWITCH)).resolves.toBe(false); @@ -72,12 +72,12 @@ describe('put switch tests', () => { describe('static helper function tests', () => { it('correctly serializes key from trade', () => { expect(SwitchRepository.getKey(SWITCH)).toBe('usdc#1#uni#1#EXACT_INPUT'); - }) + }); it('should throw error for invalid key on parse', () => { expect(() => { // missing type SwitchRepository.parseKey('token0#1#token1#1'); }).toThrowError('Invalid key: token0#1#token1#1'); - }) -}) + }); +}); diff --git a/test/util/axios.ts b/test/util/axios.ts index 1aea8b53..94a8e6f4 100644 --- a/test/util/axios.ts +++ b/test/util/axios.ts @@ -51,13 +51,13 @@ export default class AxiosUtils { return { data, status, - } + }; } catch (err: any) { if (err.response) { return { data: err.response.data, status: err.response.status, - } + }; } throw err; } diff --git a/test/util/token-configs.test.ts b/test/util/token-configs.test.ts index 68e5e358..c21c0fbe 100644 --- a/test/util/token-configs.test.ts +++ b/test/util/token-configs.test.ts @@ -1,92 +1,92 @@ -import { ResultRowType, TokenConfig, filterResults, validateConfigs } from "../../lib/cron/synth-switch"; +import { filterResults, ResultRowType, TokenConfig, validateConfigs } from '../../lib/cron/synth-switch'; const EXAMPLE_ROW_RESULT = { - tokenin: '0xa', - tokenout: '0xb', - tokeninchainid: 1, - tokenoutchainid: 1, - dutch_amountin: '0', - dutch_amountout: '0', - classic_amountin: '0', - classic_amountout: '0', - classic_amountingasadjusted: '0', - classic_amountoutgasadjusted: '0', - dutch_amountingasadjusted: '0', - dutch_amountoutgasadjusted: '0', - filler: '0', - filltimestamp: '0', - settledAmountIn: '0', - settledAmountOut: '0', -} + tokenin: '0xa', + tokenout: '0xb', + tokeninchainid: 1, + tokenoutchainid: 1, + dutch_amountin: '0', + dutch_amountout: '0', + classic_amountin: '0', + classic_amountout: '0', + classic_amountingasadjusted: '0', + classic_amountoutgasadjusted: '0', + dutch_amountingasadjusted: '0', + dutch_amountoutgasadjusted: '0', + filler: '0', + filltimestamp: '0', + settledAmountIn: '0', + settledAmountOut: '0', +}; describe('synth-switch util tests', () => { - describe('validateConfigs', () => { - it('filters out bad configs', () => { - const badAddresses: TokenConfig[] = [ - { - tokenIn: '0xdead', - tokenOut: '0xbeef', - tokenInChainId: 1, - tokenOutChainId: 1, - tradeTypes: ['EXACT_INPUT'], - lowerBound: ['0'] - } - ] - expect(validateConfigs(badAddresses)).toStrictEqual([]); - }) - }) + describe('validateConfigs', () => { + it('filters out bad configs', () => { + const badAddresses: TokenConfig[] = [ + { + tokenIn: '0xdead', + tokenOut: '0xbeef', + tokenInChainId: 1, + tokenOutChainId: 1, + tradeTypes: ['EXACT_INPUT'], + lowerBound: ['0'], + }, + ]; + expect(validateConfigs(badAddresses)).toStrictEqual([]); + }); + }); - describe('filterResults', () => { - it('filters out rows that do not have matching token pairs in the configs', () => { - const configs: TokenConfig[] = [ - { - tokenIn: '0xa', - tokenOut: '0xb', - tokenInChainId: 1, - tokenOutChainId: 1, - tradeTypes: ['EXACT_INPUT'], - lowerBound: ['0'] - }, - { - tokenIn: '0xc', - tokenOut: '0xd', - tokenInChainId: 1, - tokenOutChainId: 1, - tradeTypes: ['EXACT_INPUT'], - lowerBound: ['0'] - } - ] - const results: ResultRowType[] = [ - { - ...EXAMPLE_ROW_RESULT, - tokenin: '0xa', - tokenout: '0xb', - }, - { - ...EXAMPLE_ROW_RESULT, - tokenin: '0xa', - tokenout: '0xc', - }, - { - ...EXAMPLE_ROW_RESULT, - tokenin: '0xb', - tokenout: '0xc', - }, - { - ...EXAMPLE_ROW_RESULT, - tokenin: '0xc', - tokenout: '0xd', - }, - ] - const filteredResults = filterResults(configs, results); - // should only have 2 results, a -> b and c -> d - expect(filteredResults).toHaveLength(2); - expect(filteredResults[0]).toMatchObject(results[0]); - expect(filteredResults[0].tokenin).toBe(configs[0].tokenIn); - expect(filteredResults[0].tokenout).toBe(configs[0].tokenOut); - expect(filteredResults[1]).toMatchObject(results[3]); - expect(filteredResults[1].tokenin).toBe(configs[1].tokenIn); - expect(filteredResults[1].tokenout).toBe(configs[1].tokenOut); - }) - }) -}); \ No newline at end of file + describe('filterResults', () => { + it('filters out rows that do not have matching token pairs in the configs', () => { + const configs: TokenConfig[] = [ + { + tokenIn: '0xa', + tokenOut: '0xb', + tokenInChainId: 1, + tokenOutChainId: 1, + tradeTypes: ['EXACT_INPUT'], + lowerBound: ['0'], + }, + { + tokenIn: '0xc', + tokenOut: '0xd', + tokenInChainId: 1, + tokenOutChainId: 1, + tradeTypes: ['EXACT_INPUT'], + lowerBound: ['0'], + }, + ]; + const results: ResultRowType[] = [ + { + ...EXAMPLE_ROW_RESULT, + tokenin: '0xa', + tokenout: '0xb', + }, + { + ...EXAMPLE_ROW_RESULT, + tokenin: '0xa', + tokenout: '0xc', + }, + { + ...EXAMPLE_ROW_RESULT, + tokenin: '0xb', + tokenout: '0xc', + }, + { + ...EXAMPLE_ROW_RESULT, + tokenin: '0xc', + tokenout: '0xd', + }, + ]; + const filteredResults = filterResults(configs, results); + // should only have 2 results, a -> b and c -> d + expect(filteredResults).toHaveLength(2); + expect(filteredResults[0]).toMatchObject(results[0]); + expect(filteredResults[0].tokenin).toBe(configs[0].tokenIn); + expect(filteredResults[0].tokenout).toBe(configs[0].tokenOut); + expect(filteredResults[1]).toMatchObject(results[3]); + expect(filteredResults[1].tokenin).toBe(configs[1].tokenIn); + expect(filteredResults[1].tokenout).toBe(configs[1].tokenOut); + }); + }); +});