ERC-20 helpers
Use @onmax/unerc20 when you already have an EIP-1193 provider and need portable ERC-20 calls without taking on wallet discovery, chain switching, or token-list policy.
Do not use it for native ETH/MATIC transfers, NFT transfers, allowance management, token registries, fiat pricing, or payout approvals. Those decisions belong in your app.
Nuxt server-backed composables
Use @onmax/unerc20/nuxt when a Nuxt app should keep the RPC URL on the server.
export default defineNuxtConfig({
modules: ['@onmax/unerc20/nuxt'],
unerc20: {
rpcUrl: process.env.ERC20_RPC_URL,
},
})
<script setup lang="ts">
const network = useEvmNetwork({ refreshIntervalMs: 15_000 })
const account = useErc20Account({
account: '0x1234567890abcdef1234567890abcdef12345678',
token: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
})
</script>
useEvmNetwork() and useErc20Account() call internal Nitro routes by default. Pass an explicit provider to useErc20Account() only when you intentionally want direct provider access.
Read network status
Use readEvmNetwork() when shared TypeScript code needs a single EVM summary instead of probing JSON-RPC methods one by one.
import { readEvmNetwork } from '@onmax/unerc20'
const summary = await readEvmNetwork(window.ethereum)
console.log(summary.chainId, summary.blockNumber, summary.syncing)
Read a balance
Use the read helpers when the provider already points at the chain that hosts the token.
import {
formatTokenAmountAtomic,
readErc20Balance,
readErc20Decimals,
readErc20Symbol,
} from '@onmax/unerc20'
const provider = window.ethereum
const token = '0xc2132D05D31c914a87C6611C10748AEb04B58e8F'
const account = '0x1234567890abcdef1234567890abcdef12345678'
const [symbol, decimals, atomicBalance] = await Promise.all([
readErc20Symbol(provider, token),
readErc20Decimals(provider, token),
readErc20Balance(provider, { token, account }),
])
console.log(`${symbol}: ${formatTokenAmountAtomic(atomicBalance, decimals)}`)
Expected behavior:
USDT: 1.2345
The package calls eth_call for symbol(), decimals(), and balanceOf(address).
Skip metadata calls when you already know it
Use a token object when the app has trusted metadata. readErc20Symbol() and readErc20Decimals() return the provided metadata without calling the provider.
import { readErc20Decimals, readErc20Symbol } from '@onmax/unerc20'
const usdt = {
address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
symbol: 'USDT',
decimals: 6,
}
console.log(await readErc20Symbol(window.ethereum, usdt))
console.log(await readErc20Decimals(window.ethereum, usdt))
Expected behavior:
USDT
6
Use this for app-owned token allowlists. Do not trust user-provided token metadata for transfer screens.
Parse user input once
Use parseTokenAmountToAtomic() at the form boundary, then pass the returned bigint through the rest of the flow.
import { formatTokenAmountAtomic, parseTokenAmountToAtomic } from '@onmax/unerc20'
const decimals = 6
const amount = parseTokenAmountToAtomic('1,234.50', decimals)
if (amount === null)
throw new Error('Enter a valid token amount')
console.log(amount)
console.log(formatTokenAmountAtomic(amount, decimals))
Expected behavior:
1234500000n
1234.5
Inputs with more fraction digits than the token supports return null.
import { parseTokenAmountToAtomic } from '@onmax/unerc20'
console.log(parseTokenAmountToAtomic('1.2345678', 6))
console.log(parseTokenAmountToAtomic('1.23 USDT', 6))
Expected behavior:
null
null
Submit a transfer
Use transferErc20() after your app has selected the token, account, recipient, amount, and chain.
import { parseTokenAmountToAtomic, transferErc20 } from '@onmax/unerc20'
const provider = window.ethereum
const amount = parseTokenAmountToAtomic('10', 6)
if (amount === null)
throw new Error('Invalid amount')
const hash = await transferErc20(provider, {
token: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
from: '0x1234567890abcdef1234567890abcdef12345678',
to: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd',
amount,
})
console.log(hash)
Expected behavior:
0x...
The helper sends eth_sendTransaction with ERC-20 transfer(address,uint256) calldata, value: '0x0', and optional gas.
Reuse the ABI in simulations
Use ERC20_ABI when a test, simulator, or decoder needs the same standard ERC-20 shape as the helpers.
import { expect, it } from 'vitest'
import { ERC20_ABI } from '@onmax/unerc20'
it('keeps the helper ABI small', () => {
expect(ERC20_ABI.map(entry => entry.name).sort()).toEqual([
'balanceOf',
'decimals',
'symbol',
'transfer',
])
})