UNPKG

viem

Version:

TypeScript Interface for Ethereum

231 lines (222 loc) • 7.44 kB
import type { Address } from 'abitype' import type { Account } from '../../accounts/types.js' import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import { erc20Abi } from '../../constants/abis.js' import type { BaseErrorType } from '../../errors/base.js' import type { Tokens } from '../../tokens/defineToken.js' import type { Chain } from '../../types/chain.js' import type { Log } from '../../types/log.js' import { parseEventLogs } from '../../utils/abi/parseEventLogs.js' import { estimateContractGas } from '../public/estimateContractGas.js' import { type SimulateContractReturnType, simulateContract, } from '../public/simulateContract.js' import type { WriteContractReturnType } from '../wallet/writeContract.js' import { writeContract } from '../wallet/writeContract.js' import type { writeContractSync } from '../wallet/writeContractSync.js' import { type AmountInput, defineCall, pickWriteParameters, resolveToken, type TokenParameter, toBaseUnits, type WriteParameters, } from './internal.js' /** * Transfers ERC-20 tokens to another address. * * Pass `from` to transfer on behalf of another address using an allowance * (calls `transferFrom`); otherwise transfers from the caller (calls * `transfer`). * * @example * ```ts * import { createClient, http } from 'viem' * import { mainnet } from 'viem/chains' * import { privateKeyToAccount } from 'viem/accounts' * import { token } from 'viem/actions' * * const client = createClient({ * account: privateKeyToAccount('0x...'), * chain: mainnet, * transport: http(), * }) * * const hash = await token.transfer(client, { * amount: 100000000n, * to: '0x...', * token: '0x...', * }) * ``` * * @example * ```ts * // Transfer on behalf of another address (via an allowance). * const hash = await token.transfer(client, { * amount: 100000000n, * from: '0x...', * to: '0x...', * token: '0x...', * }) * ``` * * @param client - Client. * @param parameters - Parameters. * @returns The transaction hash. */ export async function transfer< chain extends Chain | undefined, account extends Account | undefined, tokens extends Tokens | undefined = undefined, >( client: Client<Transport, chain, account, undefined, undefined, tokens>, parameters: transfer.Parameters<chain, account, tokens>, ): Promise<transfer.ReturnValue> { return transfer.inner(writeContract, client, parameters) } export namespace transfer { export type Args< chain extends Chain | undefined = Chain | undefined, tokens extends Tokens | undefined = Tokens | undefined, > = { /** Amount to transfer in base units, or as a formatted helper. */ amount: AmountInput /** Address to transfer tokens from (uses an allowance via `transferFrom`). */ from?: Address | undefined /** Address to transfer tokens to. */ to: Address } & TokenParameter<chain, tokens> export type Parameters< chain extends Chain | undefined = Chain | undefined, account extends Account | undefined = Account | undefined, tokens extends Tokens | undefined = Tokens | undefined, > = WriteParameters<chain, account> & Args<chain, tokens> export type ReturnValue = WriteContractReturnType // TODO: exhaustive error type export type ErrorType = BaseErrorType /** @internal */ export async function inner< action extends typeof writeContract | typeof writeContractSync, chain extends Chain | undefined, account extends Account | undefined, tokens extends Tokens | undefined = undefined, >( action: action, client: Client<Transport, chain, account, undefined, undefined, tokens>, parameters: transfer.Parameters<chain, account, tokens>, ): Promise<ReturnType<action>> { return (await action(client, { ...parameters, ...transfer.call(client, parameters as never), } as never)) as never } /** * Defines a call to the `transfer` (or `transferFrom`, when `from` is given) * function. * * Can be passed as a parameter to `estimateContractGas`, `simulateContract`, * `sendCalls`, `sendTransaction` (`calls`), or `multicall`. The token is * selected by `token`, which is either a token symbol (resolved from the * client's `tokens` array) or a contract `address`; `amount.decimals` * is inferred from declared client tokens when omitted. * * @param client - Client. * @param parameters - Parameters. * @returns The call. */ export function call< chain extends Chain | undefined, account extends Account | undefined, tokens extends Tokens | undefined = undefined, >( client: Client<Transport, chain, account, undefined, undefined, tokens>, parameters: Args<chain, tokens>, ) { return defineCall(getCall(client, parameters as transfer.Args)) } /** * Estimates the gas required to transfer ERC-20 tokens. `amount.decimals` is * inferred from declared client tokens when omitted. * * @param client - Client. * @param parameters - Parameters. * @returns The gas estimate. */ export async function estimateGas< chain extends Chain | undefined, account extends Account | undefined, tokens extends Tokens | undefined = undefined, >( client: Client<Transport, chain, account, undefined, undefined, tokens>, parameters: transfer.Parameters<chain, account, tokens>, ): Promise<bigint> { return estimateContractGas(client, { ...pickWriteParameters(parameters as never), ...transfer.call(client, parameters as never), } as never) } /** * Simulates a transfer of ERC-20 tokens. `amount.decimals` is inferred from * declared client tokens when omitted. * * @param client - Client. * @param parameters - Parameters. * @returns The simulation result and write request. */ export async function simulate< chain extends Chain | undefined, account extends Account | undefined, tokens extends Tokens | undefined = undefined, >( client: Client<Transport, chain, account, undefined, undefined, tokens>, parameters: transfer.Parameters<chain, account, tokens>, ): Promise< SimulateContractReturnType<typeof erc20Abi, 'transfer' | 'transferFrom'> > { return simulateContract(client, { ...pickWriteParameters(parameters as never), ...transfer.call(client, parameters as never), } as never) as never } /** * Extracts the `Transfer` event from logs. * * @param logs - The logs. * @returns The `Transfer` event. */ export function extractEvent(logs: Log[]) { const [log] = parseEventLogs({ abi: erc20Abi, logs, eventName: 'Transfer', strict: true, }) if (!log) throw new Error('`Transfer` event not found.') return log } } /** Builds the underlying `transfer`/`transferFrom` contract call. @internal */ function getCall( client: Client<Transport, Chain | undefined>, parameters: transfer.Args, ) { const { amount, from, to, token } = parameters const { address, decimals } = resolveToken(client, { token }) const value = toBaseUnits(amount, decimals) if (from) return { abi: erc20Abi, address, args: [from, to, value], functionName: 'transferFrom', } as const return { abi: erc20Abi, address, args: [to, value], functionName: 'transfer', } as const }