@lifi/sdk
Version:
LI.FI Any-to-Any Cross-Chain-Swap SDK
150 lines (139 loc) • 5.07 kB
text/typescript
import type {
GetWalletBalanceExtendedResponse,
RequestOptions,
Token,
TokenAmount,
TokenAmountExtended,
TokenExtended,
WalletTokenExtended,
} from '@lifi/types'
import { config } from '../config.js'
import { ValidationError } from '../errors/errors.js'
import { request } from '../request.js'
import { isToken } from '../typeguards.js'
/**
* Returns the balances of a specific token a wallet holds across all aggregated chains.
* @param walletAddress - A wallet address.
* @param token - A Token object.
* @returns An object containing the token and the amounts on different chains.
* @throws {BaseError} Throws a ValidationError if parameters are invalid.
*/
export const getTokenBalance = async (
walletAddress: string,
token: Token
): Promise<TokenAmount | null> => {
const tokenAmounts = await getTokenBalances(walletAddress, [token])
return tokenAmounts.length ? tokenAmounts[0] : null
}
/**
* Returns the balances for a list tokens a wallet holds across all aggregated chains.
* @param walletAddress - A wallet address.
* @param tokens - A list of Token (or TokenExtended) objects.
* @returns A list of objects containing the tokens and the amounts on different chains.
* @throws {BaseError} Throws a ValidationError if parameters are invalid.
*/
export async function getTokenBalances(
walletAddress: string,
tokens: Token[]
): Promise<TokenAmount[]>
export async function getTokenBalances(
walletAddress: string,
tokens: TokenExtended[]
): Promise<TokenAmountExtended[]> {
// split by chain
const tokensByChain = tokens.reduce(
(tokens, token) => {
if (!tokens[token.chainId]) {
tokens[token.chainId] = []
}
tokens[token.chainId].push(token)
return tokens
},
{} as { [chainId: number]: Token[] | TokenExtended[] }
)
const tokenAmountsByChain = await getTokenBalancesByChain(
walletAddress,
tokensByChain
)
return Object.values(tokenAmountsByChain).flat()
}
/**
* This method queries the balances of tokens for a specific list of chains for a given wallet.
* @param walletAddress - A wallet address.
* @param tokensByChain - A list of token objects organized by chain ids.
* @returns A list of objects containing the tokens and the amounts on different chains organized by the chosen chains.
* @throws {BaseError} Throws a ValidationError if parameters are invalid.
*/
export async function getTokenBalancesByChain(
walletAddress: string,
tokensByChain: { [chainId: number]: Token[] }
): Promise<{ [chainId: number]: TokenAmount[] }>
export async function getTokenBalancesByChain(
walletAddress: string,
tokensByChain: { [chainId: number]: TokenExtended[] }
): Promise<{ [chainId: number]: TokenAmountExtended[] }> {
if (!walletAddress) {
throw new ValidationError('Missing walletAddress.')
}
const tokenList = Object.values(tokensByChain).flat()
const invalidTokens = tokenList.filter((token) => !isToken(token))
if (invalidTokens.length) {
throw new ValidationError('Invalid tokens passed.')
}
const provider = config
.get()
.providers.find((provider) => provider.isAddress(walletAddress))
if (!provider) {
throw new Error(`SDK Token Provider for ${walletAddress} is not found.`)
}
const tokenAmountsByChain: {
[chainId: number]: TokenAmount[] | TokenAmountExtended[]
} = {}
const tokenAmountsSettled = await Promise.allSettled(
Object.keys(tokensByChain).map(async (chainIdStr) => {
const chainId = Number.parseInt(chainIdStr, 10)
const chain = await config.getChainById(chainId)
if (provider.type === chain.chainType) {
const tokenAmounts = await provider.getBalance(
walletAddress,
tokensByChain[chainId]
)
tokenAmountsByChain[chainId] = tokenAmounts
} else {
// if the provider is not the same as the chain type,
// return the tokens as is
tokenAmountsByChain[chainId] = tokensByChain[chainId]
}
})
)
if (config.get().debug) {
for (const result of tokenAmountsSettled) {
if (result.status === 'rejected') {
console.warn("Couldn't fetch token balance.", result.reason)
}
}
}
return tokenAmountsByChain
}
/**
* Returns the balances of tokens a wallet holds across EVM chains.
* @param walletAddress - A wallet address.
* @param options - Optional request options.
* @returns An object containing the tokens and the amounts organized by chain ids.
* @throws {BaseError} Throws a ValidationError if parameters are invalid.
*/
export const getWalletBalances = async (
walletAddress: string,
options?: RequestOptions
): Promise<Record<number, WalletTokenExtended[]>> => {
if (!walletAddress) {
throw new ValidationError('Missing walletAddress.')
}
const response = await request<GetWalletBalanceExtendedResponse>(
`${config.get().apiUrl}/wallets/${walletAddress}/balances?extended=true`,
{
signal: options?.signal,
}
)
return (response?.balances || {}) as Record<number, WalletTokenExtended[]>
}