fee-abstraction
Skill8 starsChanged 2 months agoApache-2.0View on GitHub
Pay gas fees with ERC-20 tokens on Celo. Covers supported tokens, implementation, and wallet compatibility.
- Reads credentials
In this repository15 other files it gives its agents.
- 8004Skill
- bridgingSkill
- celo-composerSkill
- celo-defiSkill
- celo-rpcSkill
- celo-stablecoinsSkill
- contract-verificationSkill
- evm-foundrySkill
- evm-hardhatSkill
- evm-wallet-integrationSkill
- minipay-integrationSkill
- thirdwebSkill
- viemSkill
- wagmiSkill
- x402Skill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 206
Wallet Dependency: Only works with Celo-native wallets (MiniPay, Valora) or custom dApp implementations
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Fee Abstraction on Celo
This skill covers Celo's native fee abstraction feature that allows gas fees to be paid in ERC-20 tokens.
When to Use
- Paying gas fees with stablecoins (USDC, USDT, cUSD)
- Building user-friendly dApps where users don't need CELO
- MiniPay integrations
- Any Celo transaction where users prefer stablecoin gas
Overview
Celo's native fee abstraction allows gas fees to be paid in ERC-20 tokens without Account Abstraction, Paymasters, or Relay Services. Wallets simply add a feeCurrency field to transaction objects.
Source: https://docs.celo.org/developer/fee-currency
Supported Fee Currencies
Mainnet (Chain ID: 42220)
| Token | Token Address | Adapter Address | Use in feeCurrency |
| USDC | 0xcebA9300f2b948710d2653dD7B07f33A8B32118C | 0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B | Adapter |
| USDT | 0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e | 0x0e2a3e05bc9a16f5292a6170456a710cb89c6f72 | Adapter |
| cUSD | 0x765DE816845861e75A25fCA122bb6898B8B1282a | - | Token |
| cEUR | 0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73 | - | Token |
| cREAL | 0xe8537a3d056DA446677B9E9d6c5dB704EaAb4787 | - | Token |
Celo Sepolia Testnet (Chain ID: 11142220)
| Token | Token Address | Adapter Address | Use in feeCurrency |
| USDC | 0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B | 0x4822e58de6f5e485eF90df51C41CE01721331dC0 | Adapter |
Important: Use adapter addresses for 6-decimal tokens (USDC, USDT). Use token addresses directly for 18-decimal tokens (cUSD, cEUR, cREAL).
Wallet Support
| Wallet | Fee Abstraction Support | Notes |
| MiniPay | Full | Native support, recommended for mobile |
| Valora | Full | Native Celo wallet |
| MetaMask | No | Uses Ethereum tx format without feeCurrency field |
| Coinbase Wallet | No | Standard EVM format |
| Other EVM Wallets | No | Require custom dApp implementation |
MetaMask and standard EVM wallets don't support fee abstraction because they use Ethereum-compatible transaction formats that don't include the
feeCurrencyfield.
Library Support
| Library | feeCurrency Support |
| viem | Supported |
| ethers.js | Not supported |
| web3.js | Not supported |
viem is required for fee abstraction in dApps.
Basic Implementation
Send Transaction with Fee Currency
import { createWalletClient, custom, parseEther } from "viem";
import { celo } from "viem/chains";
// Use adapter address for USDC (6 decimals)
const USDC_ADAPTER = "0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B";
const walletClient = createWalletClient({
chain: celo,
transport: custom(window.ethereum),
});
const [address] = await walletClient.getAddresses();
const hash = await walletClient.sendTransaction({
account: address,
to: "0xRecipientAddress",
value: parseEther("0.01"),
feeCurrency: USDC_ADAPTER, // Pay gas in USDC
});
console.log("Transaction hash:", hash);Estimate Gas Price in Fee Currency
import { createPublicClient, http } from "viem";
import { celo } from "viem/chains";
const USDC_ADAPTER = "0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B";
const publicClient = createPublicClient({
chain: celo,
transport: http("https://forno.celo.org"),
});
// Get gas price in USDC
const priceHex = await publicClient.request({
method: "eth_gasPrice",
params: [USDC_ADAPTER],
});
const gasPrice = BigInt(priceHex);
console.log("Gas price in USDC:", gasPrice);Serialize Transaction (CIP-64)
import { serializeTransaction } from "viem/celo";
import { parseGwei, parseEther } from "viem";
const USDC_ADAPTER = "0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B";
const serialized = serializeTransaction({
chainId: 42220,
gas: 21000n,
feeCurrency: USDC_ADAPTER,
maxFeePerGas: parseGwei("20"),
maxPriorityFeePerGas: parseGwei("2"),
nonce: 1,
to: "0xRecipientAddress",
value: parseEther("0.01"),
});Key Concepts
Transaction Type
Fee currency transactions use CIP-64 type 0x7b (123 decimal). This is a Celo-specific transaction type.
Gas Overhead
Paying the network fee in a stablecoin costs meaningfully more gas than paying in CELO, and the overhead is not a constant — it depends on the token, because 6-decimal tokens route through an adapter.
Measured over 2,600 consecutive Celo mainnet blocks (ending block 77,155,261), comparing gasUsed of ERC-20 transfer calls paid in CELO against identical transfers that set feeCurrency:
| Fee currency | Paid in CELO | Paid in the token | Overhead | Samples (token / CELO) |
| USDm (18-dec, no adapter) | 39,799 | 80,499 | ≈ +40,700 | 1 / 1 |
| USDT (6-dec, adapter) | 43,801 | 115,501 | ≈ +71,700 | 6,970 / 3,143 |
| USDC (6-dec, adapter) | 45,047 | 159,759 | ≈ +114,712 | 7 / 100 |
Medians; distributions are tight (USDT p25 = p75 = 115,501).
Budget ~2.5–3.5× the gas of an equivalent CELO-paid transaction, not a flat number — set gas limits from the fee-currency figures or estimation will fail. The USDT row is measured across ~10,000 transactions; USDm and USDC fee-currency samples are small because almost nobody pays fees in them (6,970 of 6,978 fee-currency transfers in the window were USDT), so treat those two rows as indicative.
Adapters vs Token Addresses
- 6-decimal tokens (USDC, USDT): Must use adapter address
- 18-decimal tokens (cUSD, cEUR, cREAL): Use token address directly
Adapters normalize decimals since Celo's gas calculations use 18 decimals internally.
Querying Available Currencies
Using celocli:
celocli network:whitelist --node https://forno.celo.orgUsing FeeCurrencyDirectory contract:
import { createPublicClient, http } from "viem";
import { celo } from "viem/chains";
const FEE_CURRENCY_DIRECTORY = "0x9212Fb72ae65367A7c887eC4Ad9bE310BAC611BF";
const publicClient = createPublicClient({
chain: celo,
transport: http("https://forno.celo.org"),
});
const currencies = await publicClient.readContract({
address: FEE_CURRENCY_DIRECTORY,
abi: [{
name: "getCurrencies",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [{ type: "address[]" }],
}],
functionName: "getCurrencies",
});
console.log("Allowed fee currencies:", currencies);Limitations
- Wallet Dependency: Only works with Celo-native wallets (MiniPay, Valora) or custom dApp implementations
- Library Dependency: Requires viem (ethers.js/web3.js don't support feeCurrency)
- Gas Overhead: ~50k additional gas for non-CELO currencies
- Balance Requirements: User must have sufficient balance in the chosen fee currency
Advanced: Custom Fee Currency Selector UI
Build a UI that lets users choose their gas payment token:
import { useState } from "react";
import { useAccount, useBalance } from "wagmi";
import { celo } from "viem/chains";
const FEE_CURRENCIES = [
{ symbol: "CELO", address: null, adapter: null },
{
symbol: "USDC",
address: "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
adapter: "0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B",
},
{
symbol: "USDT",
address: "0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e",
adapter: "0x0e2a3e05bc9a16f5292a6170456a710cb89c6f72",
},
{
symbol: "cUSD",
address: "0x765DE816845861e75A25fCA122bb6898B8B1282a",
adapter: null,
},
];
interface FeeCurrency {
symbol: string;
address: string | null;
adapter: string | null;
}
function FeeCurrencySelector({
onSelect,
}: {
onSelect: (currency: FeeCurrency) => void;
}) {
const { address } = useAccount();
const [selected, setSelected] = useState(0);
const handleChange = (index: number) => {
setSelected(index);
onSelect(FEE_CURRENCIES[index]);
};
return (
<div>
<label>Pay gas with:</label>
<select
value={selected}
onChange={(e) => handleChange(Number(e.target.value))}
>
{FEE_CURRENCIES.map((currency, i) => (
<option key={currency.symbol} value={i}>
{currency.symbol}
</option>
))}
</select>
</div>
);
}
// Usage in transaction
function useFeeCurrencyTransaction() {
const [feeCurrency, setFeeCurrency] = useState<FeeCurrency>(FEE_CURRENCIES[0]);
const getFeeCurrencyAddress = () => {
if (!feeCurrency.address) return undefined; // CELO native
return feeCurrency.adapter || feeCurrency.address;
};
return { feeCurrency, setFeeCurrency, getFeeCurrencyAddress };
}Advanced: Server-Side Transaction Building
Build fee currency transactions server-side for gasless or sponsored transactions:
import { createWalletClient, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { celo } from "viem/chains";
const USDC_ADAPTER = "0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B";
async function buildSponsoredTransaction(
recipientAddress: `0x${string}`,
amount: bigint
) {
// Sponsor account pays gas in USDC
const sponsorAccount = privateKeyToAccount(
process.env.SPONSOR_PRIVATE_KEY as `0x${string}`
);
const walletClient = createWalletClient({
account: sponsorAccount,
chain: celo,
transport: http("https://forno.celo.org"),
});
const hash = await walletClient.sendTransaction({
to: recipientAddress,
value: amount,
feeCurrency: USDC_ADAPTER,
});
return hash;
}
// Example: sponsor a user's transaction
async function sponsorUserTransfer() {
const hash = await buildSponsoredTransaction(
"0xUserAddress",
parseEther("1")
);
console.log("Sponsored transaction:", hash);
}Additional Resources
- fee-currencies.md - Complete token addresses and adapters
- wallet-support.md - Detailed wallet compatibility guide
Source of truth: Celopedia
These skills are focused, task-level references. For anything broader — verified contract addresses, ecosystem and protocol data, listing requirements, grants, governance, migration guides, or cross-cutting Celo questions — Celopedia is the canonical source and is kept current. Where this skill and Celopedia disagree, Celopedia wins.
npx skills add celo-org/celopedia-skillsDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

