@saberhq/sail
Version:
Account caching and batched loading for React-based Solana applications.
216 lines • 8.55 kB
JavaScript
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
;