Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Remove token validator provider calls from token properties provider #408

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions cli/base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import {
TenderlySimulator,
TokenPropertiesProvider,
TokenProvider,
TokenValidatorProvider,
UniswapMulticallProvider,
V2PoolProvider,
V3PoolProvider,
Expand Down Expand Up @@ -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
)
Expand Down
8 changes: 6 additions & 2 deletions src/providers/cache-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ export class NodeJSCache<T> implements ICache<T> {
return result;
}

async set(key: string, value: T): Promise<boolean> {
return this.nodeCache.set(key, value);
async set(key: string, value: T, ttl?: number): Promise<boolean> {
if (ttl) {
return this.nodeCache.set(key, value, ttl);
} else {
return this.nodeCache.set(key, value);
}
}

async has(key: string): Promise<boolean> {
Expand Down
2 changes: 1 addition & 1 deletion src/providers/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface ICache<T> {

batchGet(keys: Set<string>): Promise<Record<string, T | undefined>>;

set(key: string, value: T): Promise<boolean>;
set(key: string, value: T, ttl?: number): Promise<boolean>;

has(key: string): Promise<boolean>;
}
68 changes: 30 additions & 38 deletions src/providers/token-properties-provider.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -11,14 +13,15 @@ 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,
};
export const POSITIVE_CACHE_ENTRY_TTL = 600; // 10 minutes in seconds
export const NEGATIVE_CACHE_ENTRY_TTL = 600; // 10 minutes in seconds

type Address = string;
export type TokenPropertiesResult = {
Expand All @@ -40,10 +43,11 @@ export class TokenPropertiesProvider implements ITokenPropertiesProvider {

constructor(
private chainId: ChainId,
private tokenValidatorProvider: ITokenValidatorProvider,
private tokenPropertiesCache: ICache<TokenPropertiesResult>,
private tokenFeeFetcher: ITokenFeeFetcher,
private allowList = DEFAULT_ALLOWLIST,
private positiveCacheEntryTTL = POSITIVE_CACHE_ENTRY_TTL,
private negativeCacheEntryTTL = NEGATIVE_CACHE_ENTRY_TTL
) {}

public async getTokensProperties(
Expand All @@ -56,29 +60,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);

Expand All @@ -101,10 +82,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);
}
}
Expand Down Expand Up @@ -136,26 +118,36 @@ 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)

// update cache concurrently
// 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,
}
tokenPropertiesResult,
this.positiveCacheEntryTTL
);
} else {
metric.putMetric(`TokenPropertiesProviderTokenFeeResultCacheMissNotExists`, 1, MetricLoggerUnit.Count)
return Promise.resolve(true);

const tokenPropertiesResult = {
tokenFeeResult: undefined,
tokenValidationResult: undefined,
}
tokenToResult[address] = tokenPropertiesResult;

return this.tokenPropertiesCache.set(
this.CACHE_KEY(this.chainId, address),
tokenPropertiesResult,
this.negativeCacheEntryTTL
);
}
})
);
Expand Down
1 change: 0 additions & 1 deletion src/routers/alpha-router/alpha-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -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
)
Expand Down
18 changes: 16 additions & 2 deletions test/unit/providers/cache-node.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import NodeCache from 'node-cache';
import { NodeJSCache } from '../../../build/main';
import { NodeJSCache } from '../../../src';

describe('NodeJSCache', () => {
const cache = new NodeJSCache<string>(new NodeCache())
const underlyingCache = new NodeCache()
const cache = new NodeJSCache<string>(underlyingCache)

it('set keys and batchGet', async () => {
await Promise.all([
Expand 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);
})
});
Loading
Loading