import { JsonRpcProvider, BlockTag } from "@ethersproject/providers"; import { Contract } from "@ethersproject/contracts"; import { BigNumber } from "@ethersproject/bignumber"; import { AddressZero } from "@ethersproject/constants"; import AggregatorV3Interface from "@chainlink/contracts/abi/v0.8/AggregatorV3Interface.json"; import FeedRegistryInterface from "@chainlink/contracts/abi/v0.8/FeedRegistryInterface.json"; import { Fetcher } from "swr"; import useSWRImmutable from "swr/immutable"; import { ChecksummedAddress } from "./types"; const FEED_REGISTRY_MAINNET: ChecksummedAddress = "0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf"; // The USD "token" address for Chainlink feed registry's purposes const USD = "0x0000000000000000000000000000000000000348"; type FeedRegistryFetcherKey = [ChecksummedAddress, BlockTag]; type FeedRegistryFetcherData = [BigNumber | undefined, number | undefined]; const feedRegistryFetcherKey = ( tokenAddress: ChecksummedAddress, blockTag: BlockTag | undefined ): FeedRegistryFetcherKey | null => { if (blockTag === undefined) { return null; } return [tokenAddress, blockTag]; }; const FEED_REGISTRY_MAINNET_PROTOTYPE = new Contract( FEED_REGISTRY_MAINNET, FeedRegistryInterface ); const feedRegistryFetcher = ( provider: JsonRpcProvider | undefined ): Fetcher => async (tokenAddress, blockTag) => { if (provider === undefined) { return [undefined, undefined]; } // It work works on ethereum mainnet and kovan, see: // https://docs.chain.link/docs/feed-registry/ if (provider!.network.chainId !== 1) { throw new Error("FeedRegistry is supported only on mainnet"); } // Let SWR handle error const feedRegistry = FEED_REGISTRY_MAINNET_PROTOTYPE.connect(provider); const priceData = await feedRegistry.latestRoundData(tokenAddress, USD, { blockTag, }); const quote = BigNumber.from(priceData.answer); const decimals = await feedRegistry.decimals(tokenAddress, USD, { blockTag, }); return [quote, decimals]; }; export const useTokenUSDOracle = ( provider: JsonRpcProvider | undefined, blockTag: BlockTag | undefined, tokenAddress: ChecksummedAddress ): [BigNumber | undefined, number | undefined] => { const fetcher = feedRegistryFetcher(provider); const { data, error } = useSWRImmutable( feedRegistryFetcherKey(tokenAddress, blockTag), fetcher ); if (error) { return [undefined, undefined]; } return data ?? [undefined, undefined]; }; const ethUSDFetcherKey = (blockTag: BlockTag | undefined) => { if (blockTag === undefined) { return null; } return ["ethusd", blockTag]; }; const ETH_USD_FEED_PROTOTYPE = new Contract(AddressZero, AggregatorV3Interface); const ethUSDFetcher = ( provider: JsonRpcProvider | undefined ): Fetcher => async (_, blockTag) => { if (provider?.network.chainId !== 1) { return undefined; } const c = ETH_USD_FEED_PROTOTYPE.connect(provider).attach("eth-usd.data.eth"); const priceData = await c.latestRoundData({ blockTag }); return priceData; }; export const useETHUSDOracle = ( provider: JsonRpcProvider | undefined, blockTag: BlockTag | undefined ): BigNumber | undefined => { const fetcher = ethUSDFetcher(provider); const { data, error } = useSWRImmutable(ethUSDFetcherKey(blockTag), fetcher); if (error) { return undefined; } return data !== undefined ? BigNumber.from(data.answer) : undefined; }; export const useETHUSDRawOracle = ( provider: JsonRpcProvider | undefined, blockTag: BlockTag | undefined ): any | undefined => { const fetcher = ethUSDFetcher(provider); const { data, error } = useSWRImmutable(ethUSDFetcherKey(blockTag), fetcher); if (error) { return undefined; } return data; }; const fastGasFetcherKey = (blockTag: BlockTag | undefined) => { if (blockTag === undefined) { return null; } return ["gasgwei", blockTag]; }; const FAST_GAS_FEED_PROTOTYPE = new Contract( AddressZero, AggregatorV3Interface ); const fastGasFetcher = ( provider: JsonRpcProvider | undefined ): Fetcher => async (_, blockTag) => { if (provider?.network.chainId !== 1) { return undefined; } const c = FAST_GAS_FEED_PROTOTYPE.connect(provider).attach( "fast-gas-gwei.data.eth" ); const priceData = await c.latestRoundData({ blockTag }); return priceData; }; export const useFastGasRawOracle = ( provider: JsonRpcProvider | undefined, blockTag: BlockTag | undefined ): any | undefined => { const fetcher = fastGasFetcher(provider); const { data, error } = useSWRImmutable(fastGasFetcherKey(blockTag), fetcher); if (error) { return undefined; } return data; };