Skip to content

Commit

Permalink
Add token validator (Uniswap#59)
Browse files Browse the repository at this point in the history
* Add token validator provider and drop fot tokens from v3
  • Loading branch information
willpote authored Feb 1, 2022
1 parent e331089 commit 66289dc
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 17 deletions.
62 changes: 54 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"scripts": {
"compile-v3-types": "npx typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
"compile-v2-types": "npx typechain --target ethers-v5 --out-dir src/types/v2 './node_modules/@uniswap/?(v2-core|v2-periphery)/build/*UniswapV2*.json'",
"compile-router": "npx typechain --target ethers-v5 --out-dir src/types/other './node_modules/@uniswap/swap-router-contracts/artifacts/contracts/SwapRouter02.sol/SwapRouter02.json'",
"compile-router": "npx typechain --target ethers-v5 --out-dir src/types/other './node_modules/@uniswap/swap-router-contracts/artifacts/contracts/**/*.json'",
"compile-external-types": "npx typechain --target ethers-v5 --out-dir src/types/other 'src/abis/**/*.json'",
"build": "run-p compile-v3-types compile-v2-types compile-router compile-external-types && run-p build:*",
"build:main": "tsc -p tsconfig.json",
Expand Down Expand Up @@ -44,7 +44,7 @@
"@types/stats-lite": "^2.2.0",
"@uniswap/default-token-list": "^2.0.0",
"@uniswap/router-sdk": "^1.0.5",
"@uniswap/swap-router-contracts": "1.1.0",
"@uniswap/swap-router-contracts": "1.2.0",
"@uniswap/token-lists": "^1.0.0-beta.25",
"@uniswap/v2-core": "^1.0.1",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
Expand Down
146 changes: 146 additions & 0 deletions src/providers/token-validator-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { Token } from '@uniswap/sdk-core';
import _ from 'lodash';
import { ITokenValidator__factory } from '../types/other/factories/ITokenValidator__factory';
import { ChainId, log, WRAPPED_NATIVE_CURRENCY } from '../util';
import { ICache } from './cache';
import { IMulticallProvider } from './multicall-provider';
import { ProviderConfig } from './provider';

export enum TokenValidationResult {
UNKN = 0,
FOT = 1,
STF = 2,
}

export interface TokenValidationResults {
getValidationByToken(token: Token): TokenValidationResult | undefined;
}

const TOKEN_VALIDATOR_ADDRESS = '0xb5ee1690b7dcc7859771148d0889be838fe108e0';
const AMOUNT_TO_FLASH_BORROW = '1000';
const GAS_LIMIT_PER_VALIDATE = 300_000;

/**
* Provider for getting token data.
*
* @export
* @interface ITokenValidatorProvider
*/
export interface ITokenValidatorProvider {
/**
* Gets the token at each address. Any addresses that are not valid ERC-20 are ignored.
*
* @param addresses The token addresses to get.
* @param [providerConfig] The provider config.
* @returns A token accessor with methods for accessing the tokens.
*/
validateTokens(
tokens: Token[],
providerConfig?: ProviderConfig
): Promise<TokenValidationResults>;
}

export class TokenValidatorProvider implements ITokenValidatorProvider {
private CACHE_KEY = (chainId: ChainId, address: string) =>
`token-${chainId}-${address}`;

private BASES: string[];

constructor(
protected chainId: ChainId,
protected multicall2Provider: IMulticallProvider,
private tokenValidationCache: ICache<TokenValidationResult>,
private tokenValidatorAddress = TOKEN_VALIDATOR_ADDRESS
) {
this.BASES = [WRAPPED_NATIVE_CURRENCY[this.chainId]!.address];
}

public async validateTokens(
tokens: Token[],
providerConfig?: ProviderConfig
): Promise<TokenValidationResults> {
const tokenAddressToToken = _.keyBy(tokens, 'address');
const addressesRaw = _(tokens)
.map((token) => token.address)
.uniq()
.value();

let addresses: string[] = [];
const tokenToResult: { [tokenAddress: string]: TokenValidationResult } = {};

// Check if we have cached token validation results for any tokens.
for (const address of addressesRaw) {
if (
await this.tokenValidationCache.has(
this.CACHE_KEY(this.chainId, address)
)
) {
tokenToResult[address.toLowerCase()] =
(await this.tokenValidationCache.get(
this.CACHE_KEY(this.chainId, address)
))!;
} else {
addresses.push(address);
}
}

log.info(
`Got token validation results for ${
addressesRaw.length - addresses.length
} tokens from cache. Getting ${addresses.length} on-chain.`
);

const functionParams = _(addresses)
.map((address) => [address, this.BASES, AMOUNT_TO_FLASH_BORROW])
.value() as [string, string[], string][];

// We use the validate function instead of batchValidate to avoid poison pill problem.
// One token that consumes too much gas could cause the entire batch to fail.
const multicallResult =
await this.multicall2Provider.callSameFunctionOnContractWithMultipleParams<
[string, string[], string], // address, base token addresses, amount to borrow
[number]
>({
address: this.tokenValidatorAddress,
contractInterface: ITokenValidator__factory.createInterface(),
functionName: 'validate',
functionParams: functionParams,
providerConfig,
additionalConfig: {
gasLimitPerCallOverride: GAS_LIMIT_PER_VALIDATE,
},
});

for (let i = 0; i < multicallResult.results.length; i++) {
const resultWrapper = multicallResult.results[i]!;
const tokenAddress = addresses[i]!;
const token = tokenAddressToToken[tokenAddress]!;

// Could happen if the tokens transfer consumes too much gas so we revert. Just
// drop the token in that case.
if (!resultWrapper.success) {
log.info(
{ result: resultWrapper },
`Failed to validate token ${token.symbol}`
);

continue;
}

const validationResults = resultWrapper.result[0]!;

tokenToResult[token.address.toLowerCase()] =
validationResults as TokenValidationResult;

await this.tokenValidationCache.set(
this.CACHE_KEY(this.chainId, token.address.toLowerCase()),
tokenToResult[token.address.toLowerCase()]!
);
}

return {
getValidationByToken: (token: Token) =>
tokenToResult[token.address.toLowerCase()],
};
}
}
Loading

0 comments on commit 66289dc

Please sign in to comment.