164 lines
4.8 KiB
TypeScript
164 lines
4.8 KiB
TypeScript
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<FeedRegistryFetcherData, FeedRegistryFetcherKey> =>
|
|
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<any | undefined, ["ethusd", BlockTag | undefined]> =>
|
|
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<any | undefined, ["gasgwei", BlockTag | undefined]> =>
|
|
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;
|
|
};
|