import { computePoolAddress } from "@uniswap/v3-sdk"; import { V3_CORE_FACTORY_ADDRESSES } from "../constants/addresses"; import { IUniswapV3PoolStateInterface } from "../types/v3/IUniswapV3PoolState"; import { Token, Currency } from "@uniswap/sdk-core"; import { useMemo } from "react"; import { useWeb3 } from "../web3"; import { useMultipleContractSingleData } from "../state/gmulticall/hooks"; import { Pool, FeeAmount } from "@uniswap/v3-sdk"; import { abi as IUniswapV3PoolStateABI } from "@uniswap/v3-core/artifacts/contracts/interfaces/pool/IUniswapV3PoolState.sol/IUniswapV3PoolState.json"; import { Interface } from "@ethersproject/abi"; const POOL_STATE_INTERFACE = new Interface( IUniswapV3PoolStateABI ) as IUniswapV3PoolStateInterface; export enum PoolState { LOADING, NOT_EXISTS, EXISTS, INVALID, } export function usePools( poolKeys: [ Currency | undefined, Currency | undefined, FeeAmount | undefined ][] ): [PoolState, Pool | null][] { const { chainId } = useWeb3(); const transformed: ([Token, Token, FeeAmount] | null)[] = useMemo(() => { return poolKeys.map(([currencyA, currencyB, feeAmount]) => { if (!chainId || !currencyA || !currencyB || !feeAmount) return null; const tokenA = currencyA?.wrapped; const tokenB = currencyB?.wrapped; if (!tokenA || !tokenB || tokenA.equals(tokenB)) return null; const [token0, token1] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]; return [token0, token1, feeAmount]; }); }, [chainId, poolKeys]); const poolAddresses: (string | undefined)[] = useMemo(() => { const v3CoreFactoryAddress = chainId && V3_CORE_FACTORY_ADDRESSES[chainId]; return transformed.map((value) => { if (!v3CoreFactoryAddress || !value) return undefined; return computePoolAddress({ factoryAddress: v3CoreFactoryAddress, tokenA: value[0], tokenB: value[1], fee: value[2], }); }); }, [chainId, transformed]); const slot0s = useMultipleContractSingleData( poolAddresses, POOL_STATE_INTERFACE, "slot0" ); const liquidities = useMultipleContractSingleData( poolAddresses, POOL_STATE_INTERFACE, "liquidity" ); return useMemo(() => { return poolKeys.map((_key, index) => { const [token0, token1, fee] = transformed[index] ?? []; if (!token0 || !token1 || !fee) return [PoolState.INVALID, null]; const { result: slot0, loading: slot0Loading, valid: slot0Valid, } = slot0s[index]; const { result: liquidity, loading: liquidityLoading, valid: liquidityValid, } = liquidities[index]; if (!slot0Valid || !liquidityValid) return [PoolState.INVALID, null]; if (slot0Loading || liquidityLoading) return [PoolState.LOADING, null]; if (!slot0 || !liquidity) return [PoolState.NOT_EXISTS, null]; if (!slot0.sqrtPriceX96 || slot0.sqrtPriceX96.eq(0)) return [PoolState.NOT_EXISTS, null]; try { return [ PoolState.EXISTS, new Pool( token0, token1, fee, slot0.sqrtPriceX96, liquidity[0], slot0.tick ), ]; } catch (error) { console.error("Error when constructing the pool", error); return [PoolState.NOT_EXISTS, null]; } }); }, [liquidities, poolKeys, slot0s, transformed]); } export function usePool( currencyA: Currency | undefined, currencyB: Currency | undefined, feeAmount: FeeAmount | undefined ): [PoolState, Pool | null] { const poolKeys: [ Currency | undefined, Currency | undefined, FeeAmount | undefined ][] = useMemo(() => [[currencyA, currencyB, feeAmount]], [ currencyA, currencyB, feeAmount, ]); return usePools(poolKeys)[0]; }