otterscan/src/api/address-resolver/UniswapV3Resolver.ts
2021-12-03 16:14:09 -03:00

91 lines
2.7 KiB
TypeScript

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<UniswapV3PairMeta> {
async resolveAddress(
provider: BaseProvider,
address: string
): Promise<UniswapV3PairMeta | undefined> {
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;
}
}