UNPKG

@saberhq/sail

Version:

Account caching and batched loading for React-based Solana applications.

216 lines 8.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useToken = exports.useBatchedTokens = exports.useTokens = exports.makeTokenQuery = exports.makeBatchedTokensQuery = exports.useCertifiedToken = exports.useCertifiedTokens = void 0; const solana_contrib_1 = require("@saberhq/solana-contrib"); const token_utils_1 = require("@saberhq/token-utils"); const use_solana_1 = require("@saberhq/use-solana"); const web3_js_1 = require("@solana/web3.js"); const react_1 = require("react"); const react_query_1 = require("react-query"); const __1 = require(".."); const provider_1 = require("../provider"); const utils_1 = require("../utils"); const usePubkey_1 = require("./usePubkey"); // const makeCertifiedTokenInfoURLCDN = (chainId: number, address: string) => // `https://cdn.jsdelivr.net/gh/CLBExchange/certified-token-list/${chainId}/${address}.json`; const makeCertifiedTokenInfoURL = (chainId, address) => `https://raw.githubusercontent.com/CLBExchange/certified-token-list/master/${chainId}/${address}.json`; const normalizeMint = (mint) => { if (!mint) { return mint; } // default pubkey is treated as null if (mint.equals(web3_js_1.PublicKey.default)) { return null; } return mint; }; const makeCertifiedTokenQuery = (network, address) => ({ queryKey: ["sail/certifiedTokenInfo", network, address], queryFn: async ({ signal }) => { if (address === null || address === undefined) { return address; } const chainId = (0, token_utils_1.networkToChainId)(network); const info = await (0, __1.fetchNullableWithSessionCache)(makeCertifiedTokenInfoURL(chainId, address), signal); if (info === null) { return null; } return new token_utils_1.Token(info); }, // these should never be stale, since token mints are immutable (other than supply) staleTime: Infinity, }); /** * Loads multiple tokens from the Certified Token List. * @param mint * @returns */ const useCertifiedTokens = (mints) => { const { network } = (0, use_solana_1.useSolana)(); return (0, react_query_1.useQueries)(mints.map((mint) => makeCertifiedTokenQuery(network, mint))); }; exports.useCertifiedTokens = useCertifiedTokens; /** * Loads a token from the Certified Token List. * @param mint * @returns */ const useCertifiedToken = (mint) => { const { network } = (0, use_solana_1.useSolana)(); return (0, react_query_1.useQuery)(makeCertifiedTokenQuery(network, mint)); }; exports.useCertifiedToken = useCertifiedToken; /** * Constructs a query to load a token from the Certified Token List, or from the blockchain if * it cannot be found. * * @returns Token query */ const makeBatchedTokensQuery = ({ network, addresses, fetchKeys, }) => { var _a; return ({ queryKey: [ "sail/batchedTokens", network, ...((_a = (0, solana_contrib_1.mapSome)(addresses, (a) => a.map((address) => address === null || address === void 0 ? void 0 : address.toString()))) !== null && _a !== void 0 ? _a : [ addresses, ]), ], queryFn: async ({ signal, }) => { const addressesToFetch = []; if (!addresses) { return addresses; } const data = await Promise.all(addresses.map(async (address, i) => { if (address === null || address === undefined) { return address; } const chainId = (0, token_utils_1.networkToChainId)(network); const info = await (0, __1.fetchNullableWithSessionCache)(makeCertifiedTokenInfoURL(chainId, address.toString()), signal); if (info !== null) { return new token_utils_1.Token(info); } addressesToFetch.push({ key: address, index: i }); })); if (signal === null || signal === void 0 ? void 0 : signal.aborted) { throw new Error("Query aborted"); } const tokenDatas = await fetchKeys(addressesToFetch.map((a) => a.key)); tokenDatas.forEach((tokenData, i) => { var _a; const index = (_a = addressesToFetch[i]) === null || _a === void 0 ? void 0 : _a.index; if (index === undefined) { return; } if (!tokenData || !tokenData.data) { data[index] = null; return; } const raw = tokenData.data.accountInfo.data; const parsed = (0, token_utils_1.deserializeMint)(raw); const token = token_utils_1.Token.fromMint(tokenData.data.accountId, parsed.decimals, { chainId: (0, token_utils_1.networkToChainId)(network), }); data[index] = token; }); return data; }, // these should never be stale, since token mints are immutable (other than supply) staleTime: Infinity, }); }; exports.makeBatchedTokensQuery = makeBatchedTokensQuery; /** * Constructs a query to load a token from the Certified Token List, or from the blockchain if * it cannot be found. * * @returns Token query */ const makeTokenQuery = ({ network, address, fetchKeys, }) => ({ queryKey: ["sail/tokenInfo", network, address === null || address === void 0 ? void 0 : address.toString()], queryFn: async ({ signal }) => { if (address === null || address === undefined) { return address; } const chainId = (0, token_utils_1.networkToChainId)(network); const info = await (0, __1.fetchNullableWithSessionCache)(makeCertifiedTokenInfoURL(chainId, address.toString()), signal); if (info !== null) { return new token_utils_1.Token(info); } const [tokenData] = await fetchKeys([address]); if (!tokenData) { return null; } if (!tokenData.data) { return tokenData.data; } const raw = tokenData.data.accountInfo.data; const parsed = (0, token_utils_1.deserializeMint)(raw); return token_utils_1.Token.fromMint(address, parsed.decimals, { chainId: (0, token_utils_1.networkToChainId)(network), }); }, // these should never be stale, since token mints are immutable (other than supply) staleTime: Infinity, }); exports.makeTokenQuery = makeTokenQuery; const useNormalizedMints = (mints) => { return (0, react_1.useMemo)(() => { var _a; return (_a = mints === null || mints === void 0 ? void 0 : mints.map(normalizeMint)) !== null && _a !== void 0 ? _a : []; // eslint-disable-next-line react-hooks/exhaustive-deps }, [(0, utils_1.makeListMemoKey)(mints)]); }; /** * Uses and loads a series of mints as {@link Token}s. * @param mints * @returns */ const useTokens = (mints) => { const { network } = (0, use_solana_1.useSolana)(); const { fetchKeys } = (0, provider_1.useSail)(); const normalizedMints = useNormalizedMints(mints); return (0, react_query_1.useQueries)(normalizedMints.map((mint) => { return (0, exports.makeTokenQuery)({ network, address: mint, fetchKeys, }); })); }; exports.useTokens = useTokens; /** * Uses and loads a series of mints as {@link Token}s using a batched call. * @param mints * @returns */ const useBatchedTokens = (mints) => { const { network } = (0, use_solana_1.useSolana)(); const { fetchKeys } = (0, provider_1.useSail)(); const normalizedMints = useNormalizedMints(mints); return (0, react_query_1.useQuery)((0, exports.makeBatchedTokensQuery)({ network, addresses: normalizedMints, fetchKeys, })); }; exports.useBatchedTokens = useBatchedTokens; /** * Uses and loads a single token. * * @param mint * @returns */ const useToken = (mintRaw) => { const mint = (0, usePubkey_1.usePubkey)(mintRaw); const { network } = (0, use_solana_1.useSolana)(); const { fetchKeys } = (0, provider_1.useSail)(); const normalizedMint = (0, react_1.useMemo)(() => (0, solana_contrib_1.mapSome)(mint, normalizeMint), [mint]); return (0, react_query_1.useQuery)((0, exports.makeTokenQuery)({ network, address: normalizedMint, fetchKeys, })); }; exports.useToken = useToken; //# sourceMappingURL=useToken.js.map