UNPKG

viem

Version:

TypeScript Interface for Ethereum

188 lines • 7.03 kB
import { erc20Abi } from '../../constants/abis.js'; import { isAddress } from '../../utils/address/isAddress.js'; import { isAddressEqual } from '../../utils/address/isAddressEqual.js'; import { encodeFunctionData } from '../../utils/index.js'; import { formatUnits } from '../../utils/unit/formatUnits.js'; import { parseUnits } from '../../utils/unit/parseUnits.js'; import { readContract } from '../public/readContract.js'; /** * Shapes a base-unit value into an {@link Amount} (base units, `decimals`, and * human-readable `formatted` string). * * @param amount - Amount in base units. * @param decimals - Token decimals used to format the amount. * @returns The {@link Amount}. */ export function toAmount(amount, decimals) { return { amount, decimals, formatted: formatUnits(amount, decimals) }; } /** * Resolves a write amount to base units. * * @param amount - Base-unit amount, or formatted helper. * @param decimals - Token decimals used to parse formatted amounts. * @returns Amount in base units. */ export function toBaseUnits(amount, decimals) { if (typeof amount === 'bigint') return amount; const resolved = amount.decimals ?? decimals; return parseUnits(amount.formatted, requireTokenDecimals(resolved)); } /** * Requires resolved token decimals for parsing or formatting amounts. * * @param decimals - Token decimals. * @returns Token decimals. * @internal */ export function requireTokenDecimals(decimals) { if (decimals === undefined) throw new Error('Token decimals are required. Pass `amount.decimals` or select a declared token.'); return decimals; } /** * Resolves the decimals used for formatting a write amount. * * @param amount - Base-unit amount, or formatted helper. * @param decimals - Token decimals. * @returns Token decimals, overridden by the formatted helper when present. */ export function resolveAmountDecimals(amount, decimals) { if (typeof amount === 'bigint') return decimals; return amount.decimals ?? decimals; } /** * Resolves the token contract `address` and `decimals` from a `token`, which is * either the symbol of a token declared on the client's `tokens` array or a * contract `address`. * * When `token` is a declared symbol, the `address` and `decimals` are resolved * from the Client's `tokens` array for the Client's `chain.id` (`decimals` can * be overridden via the explicit `decimals`). When `token` is an address, its * `decimals` is inferred from the Client's `tokens` array when the address * matches a declared token, otherwise taken from the explicit `decimals`. * * @param client - Client. * @param parameters - Parameters. * @returns The resolved `address` and `decimals`. */ export function resolveToken(client, parameters) { const { decimals, token } = parameters; const declared = findDeclaredToken(client, token); if (declared) return { address: declared.address, decimals: decimals ?? declared.decimals, }; if (isAddress(token, { strict: false })) return { address: token, decimals: decimals ?? inferDecimals(client, token), }; throw new Error(`Token "${token}" is not a declared ERC-20 token on the client's \`tokens\` array (with an address for the client's chain), and is not a valid address.`); } /** * Finds the declared token on the Client's `tokens` array matching `token` * (either a token symbol or contract `address`), resolved to a {@link ResolvedToken} * for the Client's `chain.id`. Returns `undefined` when no declared token * matches, or the matching token has no address for the Client's chain. * * @param client - Client. * @param token - Token symbol (declared on the client's `tokens` array) or contract address. * @returns The matching resolved token config, or `undefined`. */ export function findDeclaredToken(client, token) { const tokens = client.tokens; const chainId = client.chain?.id; if (!tokens || chainId === undefined) return undefined; const bySymbol = findTokenBySymbol(tokens, token); if (bySymbol) return resolveTokenForChain(bySymbol, chainId); if (isAddress(token, { strict: false })) for (const token_ of tokens) { const resolved = resolveTokenForChain(token_, chainId); if (resolved && isAddressEqual(resolved.address, token)) return resolved; } return undefined; } /** * Resolves a {@link Token} to a {@link ResolvedToken} for `chainId`, or * `undefined` when the token has no address for `chainId`. @internal */ function resolveTokenForChain(token, chainId) { const address = token.addresses[chainId]; if (!address) return undefined; return { address, currency: token.currency, decimals: token.decimals, name: token.name, popular: token.popular, symbol: token.symbol, }; } /** Finds a {@link Token} by symbol (case-insensitively) on a `tokens` array. @internal */ function findTokenBySymbol(tokens, symbol) { const lowerSymbol = symbol.toLowerCase(); for (const token of tokens) { if (token.symbol?.toLowerCase() === lowerSymbol) return token; } return undefined; } /** * Infers a token's `decimals` from the Client's `tokens` array by matching * `address` against each token's address for the Client's `chain.id`. * @internal */ function inferDecimals(client, address) { const tokens = client.tokens; const chainId = client.chain?.id; if (tokens && chainId !== undefined) for (const token of tokens) { const resolved = resolveTokenForChain(token, chainId); if (resolved && isAddressEqual(resolved.address, address)) return resolved.decimals; } return undefined; } /** * Resolves token decimals, fetching from the token contract when they are not * provided explicitly or declared on the client. * @internal */ export async function resolveTokenWithDecimals(client, parameters) { const { address, decimals } = resolveToken(client, parameters); if (decimals !== undefined) return { address, decimals }; return { address, decimals: await readContract(client, { abi: erc20Abi, address, functionName: 'decimals', }), }; } /** * Picks the transaction-override fields shared by write actions, so the * action-specific args (`token`, `amount`, `to`, etc.) don't leak into * `estimateContractGas` / `simulateContract` requests. @internal */ export function pickWriteParameters(parameters) { const { account, chain, gas, maxFeePerGas, maxPriorityFeePerGas, nonce } = parameters; return { account, chain, gas, maxFeePerGas, maxPriorityFeePerGas, nonce }; } export function defineCall(call) { return { ...call, data: encodeFunctionData(call), to: call.address, }; } //# sourceMappingURL=internal.js.map