Skip to main content
This page is the how-to for app and agent developers: send transactions that pay gas fees in ERC20 tokens instead of CELO. For background and wallet support, see the overview. For token and adapter addresses, see Fee currency contracts. For the protocol rules, see the fee abstraction specification.

Allowlisted fee currencies

The protocol maintains a governable allowlist of smart contract addresses that can be used as fee currencies. These contracts implement an extension of the ERC20 interface with additional functions for debiting and crediting transaction fees (see Adding Fee Currencies). To fetch the current allowlist, call getCurrencies() on the FeeCurrencyDirectory contract, or use celocli:

Adapters for non-18-decimal tokens

Allowlisted addresses may be adapters rather than full ERC20 tokens. Adapters are used when a token has decimals other than 18 (e.g., USDC and USDT use 6 decimals). The Celo blockchain calculates gas pricing in 18 decimals, so adapters normalize the value.
  • For transfers: use the token address as usual.
  • For feeCurrency: use the adapter address.
  • For balanceOf: querying via the adapter returns the balance as if the token had 18 decimals — useful for checking whether an account can cover gas without converting units.
To get the underlying token address for an adapter, call adaptedToken() on the adapter contract. Newer adapters — including the USDT and USA₮ ones — expose this as getAdaptedToken() instead, so try both if the first call reverts. For more on gas pricing, see Transaction Fees.

Adapter addresses

The current adapter and token addresses for each network are listed on the Fee currency contracts page, which is generated from the on-chain FeeCurrencyDirectory allowlist.

Using fee abstraction with Celo CLI

Transfer 1 USDC using USDC as the fee currency via celocli:
When using USDC, USDT, or USAâ‚®, use the adapter address (not the token address) as --gasCurrency. All three tokens use 6 decimals, so pass --value in units of 10^6 (e.g. 1000000 = 1 USDT/USDC/USAâ‚®). Transfer 1 USDT using USDT as the fee currency:
A key passed with --privateKey stays in your shell history, and other users on the machine can see it in the process list while the command runs. Use a dedicated account that holds only what you plan to send.

Using fee abstraction with viem

We recommend viem, which serializes CIP-64 natively when you pass a Celo chain from viem/chains. Ethers.js and web3.js do not support the field on their own; use the Celo Ethers.js wrapper or the web3 transaction-types plugin. Whichever library you use, the wallet that signs the transaction must also support CIP-64; see wallet support. To run the scripts below, install viem with npm install viem, save each script as an .mjs file (they use import and top-level await), and run it with node. They target Celo mainnet and use USDC. For USDT or USA₮, replace the token and adapter addresses with that token’s pair from Fee currency contracts; both tokens have 6 decimals, so the math stays the same.

Estimate the gas fee

Before sending, estimate the transaction fee so the UI can reserve that amount and prevent users from trying to transfer more than their available balance.
The gas price returned from the RPC is always expressed in 18 decimals, regardless of the fee currency.
Use the adapter address (for USDC/USDT/USAâ‚®) or token address (for USDm, EURm, BRLm) as the feeCurrency value when estimating.

Prepare the transaction

Set feeCurrency to the adapter address (USDC/USDT/USAâ‚®) or token address (USDm, EURm, BRLm). Use transaction type 123 (0x7b), which is CIP-64 compliant.

Send the transaction

This script sends real USDC on Celo mainnet. Use a dedicated account that holds only what you plan to send, keep its key in an environment variable or a secret store, and never commit or log it. To try the script on Celo Sepolia first, replace celo with celoSepolia and use the Celo Sepolia USDC token and adapter from Fee currency contracts.
The script below sends 1 USDC in total, fee included: it estimates the fee as in Estimate the gas fee, converts it from 18 to 6 decimals, and transfers 1 USDC minus the fee. The network takes the fee from the sender’s USDC balance before it runs the transfer, so the balance must cover the transferred amount plus the fee at the time of inclusion. That fee can rise above the estimate if the base fee goes up in between, so keep a small margin above 1 USDC. The network also checks that the balance covers the gas limit times the maximum fee per gas, which is higher than the estimate. Set PRIVATE_KEY to the sender’s private key, a 0x-prefixed hex string.

Troubleshooting

Where to set the fee currency:

Resources