计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
fee-abstraction【效率与协作】Pay gas fees with ERC-20 tokens on Celo. Covers supported tokens, implementation, and wallet compatibility.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
This skill covers Celo's native fee abstraction feature that allows gas fees to be paid in ERC-20 tokens.
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
| 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 |
| 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 | 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 | feeCurrency Support |
|---|---|
| viem | Supported |
| ethers.js | Not supported |
| web3.js | Not supported |
viem is required for fee abstraction in dApps.
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);
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);
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"),
});
Fee currency transactions use CIP-64 type 0x7b (123 decimal). This is a Celo-specific transaction type.
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 normalize decimals since Celo's gas calculations use 18 decimals internally.
Using celocli:
celocli network:whitelist --node https://forno.celo.org
Using 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);
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 };
}
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);
}
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-skills