From e9c5ef13c61f83f0f09465c74c71981dcce04971 Mon Sep 17 00:00:00 2001 From: jsy1218 <91580504+jsy1218@users.noreply.github.com> Date: Fri, 22 Sep 2023 09:05:33 -0700 Subject: [PATCH 1/4] remove token validator from token properties --- src/providers/token-properties-provider.ts | 38 ++++----------- src/routers/alpha-router/alpha-router.ts | 1 - .../token-properties-provider.test.ts | 47 ------------------- 3 files changed, 8 insertions(+), 78 deletions(-) diff --git a/src/providers/token-properties-provider.ts b/src/providers/token-properties-provider.ts index 87aa183e0..49e5631e6 100644 --- a/src/providers/token-properties-provider.ts +++ b/src/providers/token-properties-provider.ts @@ -1,6 +1,8 @@ +import { BigNumber } from '@ethersproject/bignumber'; import { ChainId, Token } from '@uniswap/sdk-core'; import { log, metric, MetricLoggerUnit } from '../util'; + import { ICache } from './cache'; import { ProviderConfig } from './provider'; import { @@ -11,10 +13,9 @@ import { } from './token-fee-fetcher'; import { DEFAULT_ALLOWLIST, - ITokenValidatorProvider, TokenValidationResult, } from './token-validator-provider'; -import { BigNumber } from '@ethersproject/bignumber'; + export const DEFAULT_TOKEN_PROPERTIES_RESULT: TokenPropertiesResult = { tokenFeeResult: DEFAULT_TOKEN_FEE_RESULT, @@ -40,7 +41,6 @@ export class TokenPropertiesProvider implements ITokenPropertiesProvider { constructor( private chainId: ChainId, - private tokenValidatorProvider: ITokenValidatorProvider, private tokenPropertiesCache: ICache, private tokenFeeFetcher: ITokenFeeFetcher, private allowList = DEFAULT_ALLOWLIST, @@ -56,29 +56,6 @@ export class TokenPropertiesProvider implements ITokenPropertiesProvider { return tokenToResult; } - const nonAllowlistTokens = tokens.filter( - (token) => !this.allowList.has(token.address.toLowerCase()) - ); - const tokenValidationResults = - await this.tokenValidatorProvider.validateTokens( - nonAllowlistTokens, - providerConfig - ); - - tokens.forEach((token) => { - if (this.allowList.has(token.address.toLowerCase())) { - // if the token is in the allowlist, make it UNKNOWN so that we don't fetch the FOT fee on-chain - tokenToResult[token.address.toLowerCase()] = { - tokenValidationResult: TokenValidationResult.UNKN, - }; - } else { - tokenToResult[token.address.toLowerCase()] = { - tokenValidationResult: - tokenValidationResults.getValidationByToken(token), - }; - } - }); - const addressesToFetchFeesOnchain: string[] = []; const addressesRaw = this.buildAddressesRaw(tokens); @@ -101,10 +78,11 @@ export class TokenPropertiesProvider implements ITokenPropertiesProvider { } tokenToResult[address] = cachedValue; - } else if ( - tokenToResult[address]?.tokenValidationResult === - TokenValidationResult.FOT - ) { + } else if (this.allowList.has(address)) { + tokenToResult[address] = { + tokenValidationResult: TokenValidationResult.UNKN + } + } else { addressesToFetchFeesOnchain.push(address); } } diff --git a/src/routers/alpha-router/alpha-router.ts b/src/routers/alpha-router/alpha-router.ts index 290c4d8e0..88c2f7023 100644 --- a/src/routers/alpha-router/alpha-router.ts +++ b/src/routers/alpha-router/alpha-router.ts @@ -596,7 +596,6 @@ export class AlphaRouter } else { this.tokenPropertiesProvider = new TokenPropertiesProvider( this.chainId, - this.tokenValidatorProvider!, new NodeJSCache(new NodeCache({ stdTTL: 86400, useClones: false })), new OnChainTokenFeeFetcher(this.chainId, provider) ) diff --git a/test/unit/providers/token-properties-provider.test.ts b/test/unit/providers/token-properties-provider.test.ts index 62e6938eb..85b5b589a 100644 --- a/test/unit/providers/token-properties-provider.test.ts +++ b/test/unit/providers/token-properties-provider.test.ts @@ -8,27 +8,18 @@ import { } from '../../../src/providers/token-fee-fetcher'; import { BigNumber } from '@ethersproject/bignumber'; import { - CallSameFunctionOnContractWithMultipleParams, ICache, - IMulticallProvider, ITokenPropertiesProvider, - ITokenValidatorProvider, NodeJSCache, TokenPropertiesProvider, TokenPropertiesResult, TokenValidationResult, - TokenValidatorProvider, - UniswapMulticallConfig, - UniswapMulticallProvider, USDC_MAINNET } from '../../../src'; describe('TokenPropertiesProvider', () => { let tokenPropertiesProvider: ITokenPropertiesProvider - let tokenValidatorProvider: ITokenValidatorProvider let tokenPropertiesResultCache: ICache - let tokenValidationResultCache: ICache - let mockMulticall2Provider: sinon.SinonStubbedInstance> let mockTokenFeeFetcher: sinon.SinonStubbedInstance const CACHE_KEY = (chainId: ChainId, address: string) => @@ -36,38 +27,14 @@ describe('TokenPropertiesProvider', () => { beforeEach(async () => { tokenPropertiesResultCache = new NodeJSCache(new NodeCache({ stdTTL: 3600, useClones: false })); - tokenValidationResultCache = new NodeJSCache(new NodeCache({ stdTTL: 3600, useClones: false })); mockTokenFeeFetcher = sinon.createStubInstance(OnChainTokenFeeFetcher) - mockMulticall2Provider = sinon.createStubInstance(UniswapMulticallProvider) - - tokenValidatorProvider = new TokenValidatorProvider( - ChainId.MAINNET, - mockMulticall2Provider, - tokenValidationResultCache, - ) tokenPropertiesProvider = new TokenPropertiesProvider( ChainId.MAINNET, - tokenValidatorProvider, tokenPropertiesResultCache, mockTokenFeeFetcher, ) - type functionParams = [string, string[], string][] - mockMulticall2Provider.callSameFunctionOnContractWithMultipleParams.callsFake(async ( - params: CallSameFunctionOnContractWithMultipleParams) => { - return { - blockNumber: BigNumber.from(100), - approxGasUsedPerSuccessCall: 100, - results: params.functionParams.map((_?: functionParams) => { - return ({ - success: true, - result: [TokenValidationResult.FOT] - }) - }) - }; - }) - mockTokenFeeFetcher.fetchFees.callsFake(async (addresses) => { const tokenToResult: TokenFeeMap = {}; addresses.forEach((address) => tokenToResult[address] = { @@ -173,18 +140,6 @@ describe('TokenPropertiesProvider', () => { return tokenToResult }); - type functionParams = [string, string[], string][] - mockMulticall2Provider.callSameFunctionOnContractWithMultipleParams.callsFake(async ( - params: CallSameFunctionOnContractWithMultipleParams) => { - return { - blockNumber: BigNumber.from(100), - approxGasUsedPerSuccessCall: 100, - results: params.functionParams.map(() => { - return { success: false, returnData: 'Not FOT' } - }) - }; - }) - const tokenPropertiesMap = await tokenPropertiesProvider.getTokensProperties(tokens, { enableFeeOnTransferFeeFetching: true }); for (const token of tokens) { @@ -243,5 +198,3 @@ describe('TokenPropertiesProvider', () => { expect(tokenProperties?.tokenValidationResult).toEqual(expectedTokenValidationResult); } }); - - From 4d01e4bf12006c909825d226fcd787d457835912 Mon Sep 17 00:00:00 2001 From: jsy1218 <91580504+jsy1218@users.noreply.github.com> Date: Mon, 25 Sep 2023 15:17:41 -0700 Subject: [PATCH 2/4] remove token validators call from token properties provider, and make NodeJSCache per entry TTL --- src/providers/cache-node.ts | 8 ++++++-- src/providers/cache.ts | 2 +- src/providers/token-properties-provider.ts | 16 ++++++++++++++-- test/unit/providers/cache-node.test.ts | 16 +++++++++++++++- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/providers/cache-node.ts b/src/providers/cache-node.ts index 35055f80e..aa4b3f327 100644 --- a/src/providers/cache-node.ts +++ b/src/providers/cache-node.ts @@ -22,8 +22,12 @@ export class NodeJSCache implements ICache { return result; } - async set(key: string, value: T): Promise { - return this.nodeCache.set(key, value); + async set(key: string, value: T, ttl?: number): Promise { + if (ttl) { + return this.nodeCache.set(key, value, ttl); + } else { + return this.nodeCache.set(key, value); + } } async has(key: string): Promise { diff --git a/src/providers/cache.ts b/src/providers/cache.ts index f204b78c6..8d85ff954 100644 --- a/src/providers/cache.ts +++ b/src/providers/cache.ts @@ -11,7 +11,7 @@ export interface ICache { batchGet(keys: Set): Promise>; - set(key: string, value: T): Promise; + set(key: string, value: T, ttl?: number): Promise; has(key: string): Promise; } diff --git a/src/providers/token-properties-provider.ts b/src/providers/token-properties-provider.ts index 49e5631e6..612421df8 100644 --- a/src/providers/token-properties-provider.ts +++ b/src/providers/token-properties-provider.ts @@ -20,6 +20,8 @@ import { export const DEFAULT_TOKEN_PROPERTIES_RESULT: TokenPropertiesResult = { tokenFeeResult: DEFAULT_TOKEN_FEE_RESULT, }; +export const POSITIVE_CACHE_ENTRY_TTL = 600; // 10 minutes in seconds +export const NEGATIVE_CACHE_ENTRY_TTL = 10; // 10 seconds type Address = string; export type TokenPropertiesResult = { @@ -44,6 +46,8 @@ export class TokenPropertiesProvider implements ITokenPropertiesProvider { private tokenPropertiesCache: ICache, private tokenFeeFetcher: ITokenFeeFetcher, private allowList = DEFAULT_ALLOWLIST, + private positiveCacheEntryTTL = POSITIVE_CACHE_ENTRY_TTL, + private negativeCacheEntryTTL = NEGATIVE_CACHE_ENTRY_TTL ) {} public async getTokensProperties( @@ -129,11 +133,19 @@ export class TokenPropertiesProvider implements ITokenPropertiesProvider { { tokenFeeResult: tokenFee, tokenValidationResult: TokenValidationResult.FOT, - } + } as TokenPropertiesResult, + this.positiveCacheEntryTTL ); } else { metric.putMetric(`TokenPropertiesProviderTokenFeeResultCacheMissNotExists`, 1, MetricLoggerUnit.Count) - return Promise.resolve(true); + return this.tokenPropertiesCache.set( + this.CACHE_KEY(this.chainId, address), + { + tokenFeeResult: undefined, + tokenValidationResult: undefined, + } as TokenPropertiesResult, + this.negativeCacheEntryTTL + ); } }) ); diff --git a/test/unit/providers/cache-node.test.ts b/test/unit/providers/cache-node.test.ts index 88bd5ebef..acafbc45f 100644 --- a/test/unit/providers/cache-node.test.ts +++ b/test/unit/providers/cache-node.test.ts @@ -2,7 +2,8 @@ import NodeCache from 'node-cache'; import { NodeJSCache } from '../../../build/main'; describe('NodeJSCache', () => { - const cache = new NodeJSCache(new NodeCache()) + const underlyingCache = new NodeCache() + const cache = new NodeJSCache(underlyingCache) it('set keys and batchGet', async () => { await Promise.all([ @@ -15,4 +16,17 @@ describe('NodeJSCache', () => { expect(batchGet['key2']).toEqual('value2'); expect(batchGet['key3']).toBeUndefined(); }); + + it('set keys with ttl', async () => { + const currentEpochTimeInSeconds = Math.floor(Date.now() / 1000); + + await Promise.all([ + cache.set('key1', 'value1', 600), + cache.set('key2', 'value2', 10) + ]); + + // rounded milliseconds to seconds, so that the flaky test failure due to millisecond difference is avoided + expect(Math.floor((underlyingCache.getTtl('key1') ?? 0) / 1000)).toEqual(currentEpochTimeInSeconds + 600); + expect(Math.floor((underlyingCache.getTtl('key2') ?? 0) / 1000)).toEqual(currentEpochTimeInSeconds + 10); + }) }); From 3c11ae544f3ad389a76629b165168b9a41227619 Mon Sep 17 00:00:00 2001 From: jsy1218 <91580504+jsy1218@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:03:56 -0700 Subject: [PATCH 3/4] complete unit tests --- cli/base-command.ts | 7 -- src/providers/token-properties-provider.ts | 24 ++-- .../alpha-router.integration.test.ts | 13 --- test/unit/providers/cache-node.test.ts | 2 +- .../token-properties-provider.test.ts | 104 ++++++++++-------- 5 files changed, 74 insertions(+), 76 deletions(-) diff --git a/cli/base-command.ts b/cli/base-command.ts index 13af14441..243ccdee2 100644 --- a/cli/base-command.ts +++ b/cli/base-command.ts @@ -41,7 +41,6 @@ import { TenderlySimulator, TokenPropertiesProvider, TokenProvider, - TokenValidatorProvider, UniswapMulticallProvider, V2PoolProvider, V3PoolProvider, @@ -287,18 +286,12 @@ export abstract class BaseCommand extends Command { new V3PoolProvider(chainId, multicall2Provider), new NodeJSCache(new NodeCache({ stdTTL: 360, useClones: false })) ); - const tokenValidatorProvider = new TokenValidatorProvider( - chainId, - multicall2Provider, - new NodeJSCache(new NodeCache({ stdTTL: 360, useClones: false })) - ) const tokenFeeFetcher = new OnChainTokenFeeFetcher( chainId, provider ) const tokenPropertiesProvider = new TokenPropertiesProvider( chainId, - tokenValidatorProvider, new NodeJSCache(new NodeCache({ stdTTL: 360, useClones: false })), tokenFeeFetcher ) diff --git a/src/providers/token-properties-provider.ts b/src/providers/token-properties-provider.ts index 612421df8..fd2da70c4 100644 --- a/src/providers/token-properties-provider.ts +++ b/src/providers/token-properties-provider.ts @@ -118,11 +118,12 @@ export class TokenPropertiesProvider implements ITokenPropertiesProvider { // in the form of metrics. // if we log as logging, given prod traffic volume, the logging volume will be high. metric.putMetric(`TokenPropertiesProviderTokenFeeResultCacheMissExists${tokenFeeResultExists}`, 1, MetricLoggerUnit.Count) - const tokenResultForAddress = tokenToResult[address]; - if (tokenResultForAddress) { - tokenResultForAddress.tokenFeeResult = tokenFee; + const tokenPropertiesResult = { + tokenFeeResult: tokenFee, + tokenValidationResult: TokenValidationResult.FOT, } + tokenToResult[address] = tokenPropertiesResult; metric.putMetric("TokenPropertiesProviderBatchGetCacheMiss", 1, MetricLoggerUnit.Count) @@ -130,20 +131,21 @@ export class TokenPropertiesProvider implements ITokenPropertiesProvider { // at this point, we are confident that the tokens are FOT, so we can hardcode the validation result return this.tokenPropertiesCache.set( this.CACHE_KEY(this.chainId, address), - { - tokenFeeResult: tokenFee, - tokenValidationResult: TokenValidationResult.FOT, - } as TokenPropertiesResult, + tokenPropertiesResult, this.positiveCacheEntryTTL ); } else { metric.putMetric(`TokenPropertiesProviderTokenFeeResultCacheMissNotExists`, 1, MetricLoggerUnit.Count) + + const tokenPropertiesResult = { + tokenFeeResult: undefined, + tokenValidationResult: undefined, + } + tokenToResult[address] = tokenPropertiesResult; + return this.tokenPropertiesCache.set( this.CACHE_KEY(this.chainId, address), - { - tokenFeeResult: undefined, - tokenValidationResult: undefined, - } as TokenPropertiesResult, + tokenPropertiesResult, this.negativeCacheEntryTTL ); } diff --git a/test/integ/routers/alpha-router/alpha-router.integration.test.ts b/test/integ/routers/alpha-router/alpha-router.integration.test.ts index 36204e96d..020849753 100644 --- a/test/integ/routers/alpha-router/alpha-router.integration.test.ts +++ b/test/integ/routers/alpha-router/alpha-router.integration.test.ts @@ -68,7 +68,6 @@ import { WETH9, WNATIVE_ON, TokenPropertiesProvider, - TokenValidatorProvider, } from '../../../../src'; import { OnChainTokenFeeFetcher } from '../../../../src/providers/token-fee-fetcher'; import { DEFAULT_ROUTING_CONFIG_BY_CHAIN } from '../../../../src/routers/alpha-router/config'; @@ -484,18 +483,12 @@ describe('alpha router integration', () => { new V3PoolProvider(ChainId.MAINNET, multicall2Provider), new NodeJSCache(new NodeCache({ stdTTL: 360, useClones: false })) ); - const tokenValidatorProvider = new TokenValidatorProvider( - ChainId.MAINNET, - multicall2Provider, - new NodeJSCache(new NodeCache({ stdTTL: 360, useClones: false })) - ) const tokenFeeFetcher = new OnChainTokenFeeFetcher( ChainId.MAINNET, hardhat.provider ) const tokenPropertiesProvider = new TokenPropertiesProvider( ChainId.MAINNET, - tokenValidatorProvider, new NodeJSCache(new NodeCache({ stdTTL: 360, useClones: false })), tokenFeeFetcher ) @@ -2708,18 +2701,12 @@ describe('quote for other networks', () => { new V3PoolProvider(chain, multicall2Provider), new NodeJSCache(new NodeCache({ stdTTL: 360, useClones: false })) ); - const tokenValidatorProvider = new TokenValidatorProvider( - ChainId.MAINNET, - multicall2Provider, - new NodeJSCache(new NodeCache({ stdTTL: 360, useClones: false })) - ) const tokenFeeFetcher = new OnChainTokenFeeFetcher( ChainId.MAINNET, hardhat.provider ) const tokenPropertiesProvider = new TokenPropertiesProvider( ChainId.MAINNET, - tokenValidatorProvider, new NodeJSCache(new NodeCache({ stdTTL: 360, useClones: false })), tokenFeeFetcher ) diff --git a/test/unit/providers/cache-node.test.ts b/test/unit/providers/cache-node.test.ts index acafbc45f..236543403 100644 --- a/test/unit/providers/cache-node.test.ts +++ b/test/unit/providers/cache-node.test.ts @@ -1,5 +1,5 @@ import NodeCache from 'node-cache'; -import { NodeJSCache } from '../../../build/main'; +import { NodeJSCache } from '../../../src'; describe('NodeJSCache', () => { const underlyingCache = new NodeCache() diff --git a/test/unit/providers/token-properties-provider.test.ts b/test/unit/providers/token-properties-provider.test.ts index 85b5b589a..2026402fa 100644 --- a/test/unit/providers/token-properties-provider.test.ts +++ b/test/unit/providers/token-properties-provider.test.ts @@ -8,9 +8,9 @@ import { } from '../../../src/providers/token-fee-fetcher'; import { BigNumber } from '@ethersproject/bignumber'; import { - ICache, - ITokenPropertiesProvider, NodeJSCache, + POSITIVE_CACHE_ENTRY_TTL, + NEGATIVE_CACHE_ENTRY_TTL, TokenPropertiesProvider, TokenPropertiesResult, TokenValidationResult, @@ -18,23 +18,14 @@ import { } from '../../../src'; describe('TokenPropertiesProvider', () => { - let tokenPropertiesProvider: ITokenPropertiesProvider - let tokenPropertiesResultCache: ICache let mockTokenFeeFetcher: sinon.SinonStubbedInstance const CACHE_KEY = (chainId: ChainId, address: string) => `token-properties-${chainId}-${address}`; beforeEach(async () => { - tokenPropertiesResultCache = new NodeJSCache(new NodeCache({ stdTTL: 3600, useClones: false })); mockTokenFeeFetcher = sinon.createStubInstance(OnChainTokenFeeFetcher) - tokenPropertiesProvider = new TokenPropertiesProvider( - ChainId.MAINNET, - tokenPropertiesResultCache, - mockTokenFeeFetcher, - ) - mockTokenFeeFetcher.fetchFees.callsFake(async (addresses) => { const tokenToResult: TokenFeeMap = {}; addresses.forEach((address) => tokenToResult[address] = { @@ -48,7 +39,16 @@ describe('TokenPropertiesProvider', () => { describe('get token fees by address', () => { it('succeeds to get token fee and updates cache', async () => { + const underlyingCache: NodeCache = new NodeCache({ stdTTL: 3600, useClones: false }) + const tokenPropertiesResultCache: NodeJSCache = new NodeJSCache(underlyingCache); + const tokenPropertiesProvider = new TokenPropertiesProvider( + ChainId.MAINNET, + tokenPropertiesResultCache, + mockTokenFeeFetcher, + ) + const token = USDC_MAINNET + const currentEpochTimeInSeconds = Math.floor(Date.now() / 1000); expect(await tokenPropertiesResultCache.get(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase()))).toBeUndefined(); const tokenPropertiesMap = await tokenPropertiesProvider.getTokensProperties([token], { enableFeeOnTransferFeeFetching: true }); @@ -58,10 +58,22 @@ describe('TokenPropertiesProvider', () => { const cachedTokenProperties = await tokenPropertiesResultCache.get(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) expect(cachedTokenProperties).toBeDefined(); assertExpectedTokenProperties(cachedTokenProperties, BigNumber.from(213), BigNumber.from(800), TokenValidationResult.FOT); + + underlyingCache.getTtl(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) + expect(Math.floor((underlyingCache.getTtl(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) ?? 0) / 1000)).toEqual(currentEpochTimeInSeconds + POSITIVE_CACHE_ENTRY_TTL); }) it('succeeds to get token fee cache hit and second token fee fetcher call is skipped', async function() { + const underlyingCache: NodeCache = new NodeCache({ stdTTL: 3600, useClones: false }) + const tokenPropertiesResultCache: NodeJSCache = new NodeJSCache(underlyingCache); + const tokenPropertiesProvider = new TokenPropertiesProvider( + ChainId.MAINNET, + tokenPropertiesResultCache, + mockTokenFeeFetcher, + ) + const token = USDC_MAINNET + const currentEpochTimeInSeconds = Math.floor(Date.now() / 1000); expect(await tokenPropertiesResultCache.get(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase()))).toBeUndefined(); const tokenPropertiesMap = await tokenPropertiesProvider.getTokensProperties([token], { enableFeeOnTransferFeeFetching: true }); @@ -73,9 +85,20 @@ describe('TokenPropertiesProvider', () => { expect(cachedTokenProperties).toBeDefined(); assertExpectedTokenProperties(cachedTokenProperties, BigNumber.from(213), BigNumber.from(800), TokenValidationResult.FOT); sinon.assert.calledOnce(mockTokenFeeFetcher.fetchFees) + + underlyingCache.getTtl(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) + expect(Math.floor((underlyingCache.getTtl(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) ?? 0) / 1000)).toEqual(currentEpochTimeInSeconds + POSITIVE_CACHE_ENTRY_TTL); }); it('succeeds to get token allowlist with no on-chain calls nor caching', async function() { + const underlyingCache: NodeCache = new NodeCache({ stdTTL: 3600, useClones: false }) + const tokenPropertiesResultCache: NodeJSCache = new NodeJSCache(underlyingCache); + const tokenPropertiesProvider = new TokenPropertiesProvider( + ChainId.MAINNET, + tokenPropertiesResultCache, + mockTokenFeeFetcher, + ) + const allowListToken = new Token(1, '0x777E2ae845272a2F540ebf6a3D03734A5a8f618e', 18); const tokenPropertiesMap = await tokenPropertiesProvider.getTokensProperties([allowListToken], { enableFeeOnTransferFeeFetching: true }); @@ -87,6 +110,15 @@ describe('TokenPropertiesProvider', () => { }); it('succeeds to get token properties in a single batch', async function() { + const underlyingCache: NodeCache = new NodeCache({ stdTTL: 3600, useClones: false }) + const tokenPropertiesResultCache: NodeJSCache = new NodeJSCache(underlyingCache); + const tokenPropertiesProvider = new TokenPropertiesProvider( + ChainId.MAINNET, + tokenPropertiesResultCache, + mockTokenFeeFetcher, + ) + const currentEpochTimeInSeconds = Math.floor(Date.now() / 1000); + const token1 = new Token(1, '0x0000000000000000000000000000000000000012', 18); const token2 = new Token(1, '0x0000000000000000000000000000000000000034', 18); const token3 = new Token(1, '0x0000000000000000000000000000000000000056', 18); @@ -118,42 +150,22 @@ describe('TokenPropertiesProvider', () => { const cachedTokenProperties = await tokenPropertiesResultCache.get(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) expect(cachedTokenProperties).toBeDefined(); assertExpectedTokenProperties(cachedTokenProperties, expectedBuyFeeBps, expectedSellFeeBps, TokenValidationResult.FOT); - } - }); - it('all tokens in the batch failed to get token validation result, no fees fetched', async function() { - const token1 = new Token(1, '0x0000000000000000000000000000000000000012', 18); - const token2 = new Token(1, '0x0000000000000000000000000000000000000034', 18); - const token3 = new Token(1, '0x0000000000000000000000000000000000000056', 18); - - const tokens = [token1, token2, token3] - - mockTokenFeeFetcher.fetchFees.callsFake(async (addresses) => { - const tokenToResult: TokenFeeMap = {}; - addresses.forEach((address) => { - tokenToResult[address] = { - buyFeeBps: BigNumber.from(parseInt(address[address.length - 2]!)), - sellFeeBps: BigNumber.from(parseInt(address[address.length - 1]!)) - } - }); - - return tokenToResult - }); - - const tokenPropertiesMap = await tokenPropertiesProvider.getTokensProperties(tokens, { enableFeeOnTransferFeeFetching: true }); - - for (const token of tokens) { - const address = token.address.toLowerCase() - expect(tokenPropertiesMap[address]).toBeDefined(); - expect(tokenPropertiesMap[address]?.tokenFeeResult).toBeUndefined(); - assertExpectedTokenProperties(tokenPropertiesMap[address], undefined, undefined, undefined); - - const cachedTokenProperties = await tokenPropertiesResultCache.get(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) - expect(cachedTokenProperties).toBeUndefined(); + underlyingCache.getTtl(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) + expect(Math.floor((underlyingCache.getTtl(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) ?? 0) / 1000)).toEqual(currentEpochTimeInSeconds + POSITIVE_CACHE_ENTRY_TTL); } }); it('all token fee fetch failed', async function() { + const underlyingCache: NodeCache = new NodeCache({ stdTTL: 3600, useClones: false }) + const tokenPropertiesResultCache: NodeJSCache = new NodeJSCache(underlyingCache); + const tokenPropertiesProvider = new TokenPropertiesProvider( + ChainId.MAINNET, + tokenPropertiesResultCache, + mockTokenFeeFetcher, + ) + const currentEpochTimeInSeconds = Math.floor(Date.now() / 1000); + const token1 = new Token(1, '0x0000000000000000000000000000000000000012', 18); const token2 = new Token(1, '0x0000000000000000000000000000000000000034', 18); const token3 = new Token(1, '0x0000000000000000000000000000000000000056', 18); @@ -168,11 +180,15 @@ describe('TokenPropertiesProvider', () => { const address = token.address.toLowerCase() expect(tokenPropertiesMap[address]).toBeDefined(); expect(tokenPropertiesMap[address]?.tokenFeeResult).toBeUndefined(); - assertExpectedTokenProperties(tokenPropertiesMap[address], undefined, undefined, TokenValidationResult.FOT); + expect(tokenPropertiesMap[address]?.tokenValidationResult).toBeUndefined(); const cachedTokenProperties = await tokenPropertiesResultCache.get(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) - expect(cachedTokenProperties).toBeUndefined(); + expect(cachedTokenProperties).toBeDefined(); + expect(cachedTokenProperties?.tokenFeeResult).toBeUndefined(); + expect(cachedTokenProperties?.tokenValidationResult).toBeUndefined(); + underlyingCache.getTtl(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) + expect(Math.floor((underlyingCache.getTtl(CACHE_KEY(ChainId.MAINNET, token.address.toLowerCase())) ?? 0) / 1000)).toEqual(currentEpochTimeInSeconds + NEGATIVE_CACHE_ENTRY_TTL); } }); }); From 061338f187ecade50b88844dccc14ee8acf8087b Mon Sep 17 00:00:00 2001 From: jsy1218 <91580504+jsy1218@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:51:48 -0700 Subject: [PATCH 4/4] negative cache entry ttl 10 min --- src/providers/token-properties-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/token-properties-provider.ts b/src/providers/token-properties-provider.ts index fd2da70c4..08e2d3072 100644 --- a/src/providers/token-properties-provider.ts +++ b/src/providers/token-properties-provider.ts @@ -21,7 +21,7 @@ export const DEFAULT_TOKEN_PROPERTIES_RESULT: TokenPropertiesResult = { tokenFeeResult: DEFAULT_TOKEN_FEE_RESULT, }; export const POSITIVE_CACHE_ENTRY_TTL = 600; // 10 minutes in seconds -export const NEGATIVE_CACHE_ENTRY_TTL = 10; // 10 seconds +export const NEGATIVE_CACHE_ENTRY_TTL = 600; // 10 minutes in seconds type Address = string; export type TokenPropertiesResult = {