name: celonames description: Activates for any task involving celo.eth name registration, price checks, renewals, transfers, editing records, primary name setup, or reading name records. Use this skill even if the user just says "register a name on Celo" or "check if a .celo.eth name is available".
Celo Names
Celo Names is an ENS-based naming system on Celo L2. Names are subnames of celo.eth, formatted as {label}.celo.eth (e.g., alice.celo.eth). Each name is an ERC-721 NFT on Celo. Cross-chain resolution from Ethereum L1 works via CCIP-Read.
Network
| Chain | Celo Mainnet (42220) |
| RPC | https://forno.celo.org |
Contracts
| Contract | Address |
|---|---|
| L2Registrar (paid registration) | 0x9Eb22700eFa1558eb2e0E522eB1DECC8025C3127 |
| L2Registry (NFT + records) | 0x4d7912779679AFdC592CBd4674b32Fcb189395F7 |
| ReverseRegistrar (primary name) | 0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb |
| L2SelfRegistrar (free claim) | 0x063E9F0bA0061F6C3c6169674c81f43BE21fe8cc |
| RegistrarStorage | 0xaAF67A46b99bE9a183580Cd86236cd0c6f2a85cb |
| L1Resolver (Ethereum) | 0xC6FC912C5DACb6BF0a24Bad113493c900fEA254a |
Payment Tokens
| Token | Address | Decimals |
|---|---|---|
| CELO (native) | 0x0000000000000000000000000000000000000000 |
18 |
| USDC | 0xcebA9300f2b948710d2653dD7B07f33A8B32118C |
6 |
| USDT | 0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e |
6 |
| cUSD | 0x765DE816845861e75A25fCA122bb6898B8B1282a |
18 |
Pricing
| Length | USD/Year |
|---|---|
| 3 chars | $400 |
| 4 chars | $160 |
| 5 chars | $50 |
| 6+ chars | $5 |
Duration: 1-10,000 years. 10% goes to ENS treasury.
ABI - L2Registrar
The registrar handles paid name registration, renewal, and price checks.
// Check if a label is available for registration
function available(string label) view returns (bool)
// Get price in native CELO (pass zero address as token)
function rentPrice(string label, uint64 durationInYears) view returns (uint256)
// Get price in a specific ERC-20 token
function rentPrice(string label, uint64 durationInYears, address paymentToken) view returns (uint256)
// Register with native CELO - send price as msg.value
function register(string label, uint64 durationInYears, address owner, bytes[] resolverData) payable
// Register with ERC-20 token via permit
function registerERC20(string label, uint64 durationInYears, address owner, bytes[] resolverData, address paymentToken, (uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) permit)
// Renew with native CELO - send price as msg.value
function renew(string label, uint64 durationInYears) payable
// Renew with ERC-20 token via permit
function renewERC20(string label, uint64 durationInYears, address paymentToken, (uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) permit)
ABI - L2Registry
The registry is the NFT contract that holds names and resolver records. Token ID = uint256(namehash("label.celo.eth")).
// ERC-721
function ownerOf(uint256 tokenId) view returns (address)
function balanceOf(address owner) view returns (uint256)
function safeTransferFrom(address from, address to, uint256 tokenId)
function totalSupply() view returns (uint256)
// Name expiry (returns unix timestamp)
function expiries(bytes32 node) view returns (uint256)
// Resolver records
function addr(bytes32 node) view returns (address)
function addr(bytes32 node, uint256 coinType) view returns (bytes)
function text(bytes32 node, string key) view returns (string)
function contenthash(bytes32 node) view returns (bytes)
// Set resolver records (only name owner)
function setAddr(bytes32 node, address a)
function setAddr(bytes32 node, uint256 coinType, bytes a)
function setText(bytes32 node, string key, string value)
function setContenthash(bytes32 node, bytes hash)
// Batch multiple record updates
function multicall(bytes[] data) returns (bytes[])
function multicallWithNodeCheck(bytes32 nodehash, bytes[] data) returns (bytes[])
ABI - ReverseRegistrar
Sets which name displays as your "primary name" when someone looks up your address.
// Set your primary name (caller's address)
function setName(string name) returns (bytes32)
// Set primary name for another address (requires authorization)
function setNameForAddr(address addr, address owner, address resolver, string name) returns (bytes32)
// Get the reverse node for an address
function node(address addr) pure returns (bytes32)
Registration Flow
1. available("alice") → true/false
2. rentPrice("alice", 1) → price in wei (native CELO)
3. register("alice", 1, owner, []) with value = price
4. (optional) setName("alice.celo.eth") → set as primary name
Pass only the label to the registrar -- alice, not alice.celo.eth.
Editing resolver records (text + address)
To update text and/or address records for an existing name, call the record setters on L2Registry for the node (namehash("label.celo.eth")). The caller must be the name owner (ERC-721 owner for uint256(node)).
Use multicall(data[]) to bundle multiple updates into one transaction.
1. node = namehash("alice.celo.eth")
2. owner = ownerOf(uint256(node)) → must equal msg.sender
3. data = [
abi.encodeCall(setAddr, (node, 0xYourEvmAddress)),
abi.encodeCall(setText, (node, "email", "me@domain.com")),
abi.encodeCall(setText, (node, "url", "https://example.com"))
]
4. multicall(data)
Notes:
- EVM address record:
setAddr(bytes32 node, address a)updates the standard EVMaddr()record. - Multi-chain address records: use
setAddr(bytes32 node, uint256 coinType, bytes a)for non-EVM coin types (the record is returned byaddr(node, coinType)). - Deleting text records: set the value to
""(empty string). For addresses, set toaddress(0)(EVM) or0x(bytes). - When batching updates, ensure every encoded call uses the same
node.
Computing namehash
The node (namehash) for alice.celo.eth is computed recursively:
namehash("") = 0x0000000000000000000000000000000000000000000000000000000000000000
namehash("eth") = keccak256(namehash("") + keccak256("eth"))
namehash("celo.eth") = keccak256(namehash("eth") + keccak256("celo"))
namehash("alice.celo.eth") = keccak256(namehash("celo.eth") + keccak256("alice"))
Common Mistakes
| Mistake | Fix |
|---|---|
Forgetting value in register() |
Always pass value: price for native CELO payments |
| Using full name as label | Pass only the label: alice, not alice.celo.eth |
Wrong durationInYears type |
It's uint64, not uint256 (in V2 registrar) |
| Expired name still shows owner | Check expiries(node) against block.timestamp |
| Not setting primary name | Registration alone doesn't set reverse resolution |
Examples
For implementation examples with working code, read the appropriate reference file:
- Cast (Foundry CLI): Read
references/cast-examples.mdfor command-line examples usingcast callandcast send - Viem (TypeScript): Read
references/viem-examples.mdfor TypeScript examples using viem
Environment Variables
PRIVATE_KEY=0x... # Wallet private key (never commit)
CELO_RPC=https://forno.celo.org