import { BaseProvider } from "@ethersproject/providers"; import { Contract } from "@ethersproject/contracts"; import { IAddressResolver } from "./address-resolver"; import { ChecksummedAddress, TokenMeta } from "../../types"; import { ERCTokenResolver } from "./ERCTokenResolver"; const UNISWAP_V3_FACTORY = "0x1F98431c8aD98523631AE4a59f267346ea31F984"; const UNISWAP_V3_FACTORY_ABI = [ "function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool)", ]; const UNISWAP_V3_PAIR_ABI = [ "function factory() external view returns (address)", "function token0() external view returns (address)", "function token1() external view returns (address)", "function fee() external view returns (uint24)", ]; export type UniswapV3TokenMeta = { address: ChecksummedAddress; } & TokenMeta; export type UniswapV3PairMeta = { pair: ChecksummedAddress; token0: UniswapV3TokenMeta; token1: UniswapV3TokenMeta; fee: number; }; const ercResolver = new ERCTokenResolver(); export class UniswapV3Resolver implements IAddressResolver { async resolveAddress( provider: BaseProvider, address: string ): Promise { const poolContract = new Contract(address, UNISWAP_V3_PAIR_ABI, provider); const factoryContract = new Contract( UNISWAP_V3_FACTORY, UNISWAP_V3_FACTORY_ABI, provider ); try { // First, probe the factory() function; if it responds with UniswapV2 factory // address, it may be a pair const factoryAddress = (await poolContract.factory()) as string; if (factoryAddress !== UNISWAP_V3_FACTORY) { return undefined; } // Probe the token0/token1/fee const [token0, token1, fee] = await Promise.all([ poolContract.token0() as string, poolContract.token1() as string, poolContract.fee() as number, ]); // Probe the factory to ensure it is a legit pair const expectedPoolAddress = await factoryContract.getPool( token0, token1, fee ); if (expectedPoolAddress !== address) { return undefined; } const [meta0, meta1] = await Promise.all([ ercResolver.resolveAddress(provider, token0), ercResolver.resolveAddress(provider, token1), ]); if (meta0 === undefined || meta1 === undefined) { return undefined; } return { pair: address, token0: { address: token0, ...meta0 }, token1: { address: token1, ...meta1 }, fee, }; } catch (err) { // Ignore on purpose; this indicates the probe failed and the address // is not a token } return undefined; } }