import { erc20Abi, getContract, type Address, type PublicActions, type WalletClient } from 'viem'; type EnsureTokenApprovalDependencies = { client: WalletClient & PublicActions; }; type EnsureTokenApprovalParams = { tokenAddress: Address; spenderAddress: Address; amount: bigint; }; type EnsureTokenApprovalReturnType = { spenderAddress: Address; approved: boolean; transactionHash?: `0x${string}`; previousAllowance: bigint; newAllowance: bigint; wasAlreadyApproved: boolean; }; /** * Ensures that the token is approved for the spender to spend the amount. * If the token is already approved for the amount, this function does nothing. * If the token is not approved for the amount, this function approves the token for the spender to spend the amount. * * @param dependencies - The dependencies for the function. * @param params - The parameters for the function. * @returns A promise that resolves with the approval result details. */ export async function ensureTokenApproval( dependencies: EnsureTokenApprovalDependencies, params: EnsureTokenApprovalParams, ): Promise { const { client } = dependencies; const { tokenAddress, spenderAddress, amount } = params; if (!client.account) { throw new Error('Wallet client account not found for approval'); } const tokenContract = getContract({ abi: erc20Abi, address: tokenAddress, client, }); const currentAllowance = await tokenContract.read.allowance([client.account.address, spenderAddress]); if (currentAllowance >= amount) { return { spenderAddress, approved: true, previousAllowance: currentAllowance, newAllowance: currentAllowance, wasAlreadyApproved: true, }; } const approveTxHash = await tokenContract.write.approve([spenderAddress, amount], { account: client.account, chain: client.chain, }); await client.waitForTransactionReceipt({ hash: approveTxHash }); return { spenderAddress, approved: true, transactionHash: approveTxHash, previousAllowance: currentAllowance, newAllowance: amount, wasAlreadyApproved: false, }; }