ENS (Ethereum Name Service) — Skill
What this skill does
Enables Gundwane to:
- Resolve ENS names to Ethereum addresses (forward resolution)
- Resolve addresses to ENS names (reverse resolution)
- Look up ENS profiles (avatar, social records, text records)
- Help users register, renew, and manage .eth names on Ethereum mainnet
When to use
- User mentions any
.ethname: "send to vitalik.eth", "look up nick.eth", "who is luc.eth" - Displaying wallet addresses to the user — show the ENS primary name alongside the address
- User asks "what's my ENS?", "do I have an ENS name?", "set my ENS"
- User asks to register a new .eth name, renew an existing one, or update records
- User sends to or receives from an
.ethaddress
ENS Name Detection
Any token matching *.eth in user input is likely an ENS name. Examples:
- "send 0.1 ETH to vitalik.eth" → resolve
vitalik.eth - "what's the address for nick.eth?" → resolve
nick.eth - "register myname.eth" → check availability for
myname
Always resolve before using. Never pass a .eth name directly to LI.FI or transaction tools — resolve to a 0x address first.
Resolution
Forward Resolution (Name → Address)
Use curl to resolve an ENS name to its Ethereum address. Try in priority order.
Approach 1: ENS Subgraph (The Graph)
Best for detailed data (expiry, registrant, resolver). Requires GRAPH_API_KEY env var.
curl -s -X POST \
--url "https://gateway.thegraph.com/api/$GRAPH_API_KEY/subgraphs/id/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH" \
--header 'Content-Type: application/json' \
--data '{"query":"{ domains(where: { name: \"vitalik.eth\" }) { name resolvedAddress { id } expiryDate registration { registrant { id } expiryDate } } }"}'
Response: data.domains[0].resolvedAddress.id = the 0x address.
Approach 2: web3.bio API (free, no key needed)
Good for quick resolution + profile data in one call.
curl -s "https://api.web3.bio/profile/vitalik.eth"
Returns JSON with address, identity, displayName, avatar, description, and linked social profiles. Use the address field for the resolved 0x address.
Approach 3: Node.js with viem (fallback)
If APIs are down and node is available (viem is in the project deps):
node --input-type=module -e "
import { createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';
import { normalize } from 'viem/ens';
const c = createPublicClient({ chain: mainnet, transport: http('https://eth.llamarpc.com') });
const addr = await c.getEnsAddress({ name: normalize('REPLACE_NAME') });
console.log(JSON.stringify({ address: addr }));
"
Replace REPLACE_NAME with the actual ENS name.
Priority: Approach 1 → 2 → 3. Use whichever is available and fastest.
Reverse Resolution (Address → Name)
Given a 0x address, find the primary ENS name.
Via ENS Subgraph
curl -s -X POST \
--url "https://gateway.thegraph.com/api/$GRAPH_API_KEY/subgraphs/id/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH" \
--header 'Content-Type: application/json' \
--data '{"query":"{ domains(where: { resolvedAddress: \"0xd8da6bf26964af9d7eed9e03e53415d37aa96045\" }) { name } }"}'
Note: address must be lowercase in the query.
Via web3.bio
curl -s "https://api.web3.bio/profile/0xd8da6bf26964af9d7eed9e03e53415d37aa96045"
Returns ENS name and profile if a primary name is set.
Via viem (fallback)
node --input-type=module -e "
import { createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';
const c = createPublicClient({ chain: mainnet, transport: http('https://eth.llamarpc.com') });
const name = await c.getEnsName({ address: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045' });
console.log(JSON.stringify({ name }));
"
Profile Lookup
Get ENS profile details: avatar, description, social links, text records.
curl -s "https://api.web3.bio/profile/nick.eth"
Common text record keys (for reference):
com.twitter— Twitter/X handlecom.github— GitHub usernameurl— Websiteemail— Email addressavatar— Avatar URL or NFT referencedescription— Bio/descriptioncom.discord— Discord handle
ENS Avatar URL
Direct avatar image:
https://metadata.ens.domains/mainnet/avatar/{name}
Example: https://metadata.ens.domains/mainnet/avatar/nick.eth
Use this URL when displaying a user's ENS avatar in messages.
Display Rules
When showing addresses
- After getting a user's wallet via
defi_get_wallet, optionally check for a reverse ENS name. - If user has a primary ENS name, display it:
fabri.eth (0xabc...def) - In portfolio views, prefer the ENS name when available.
- Don't resolve on every message — cache the result for the session.
When resolving for transactions
- Always confirm the resolved address before executing:
vitalik.eth → 0xd8dA...6045 Send 0.1 ETH to this address? - Never blindly trust resolution — ENS records can change. Always show the
0xaddress.
In transaction summaries
- Use both:
0.1 ETH → vitalik.eth (0xd8d...6045) on Base
Registration
.eth Name Registration
Registration happens on Ethereum mainnet only. Requires ETH for the name price + gas. If the user's ETH is on L2, flag that they need to bridge first.
Pricing:
- 5+ characters: $5/year in ETH
- 4 characters: $160/year in ETH
- 3 characters: $640/year in ETH
Contracts (Mainnet):
| Contract | Address |
|---|---|
| ENS Registry | 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e |
| ETH Registrar Controller | 0x253553366Da8546fC250F225fe3d25d0C782303b |
| Public Resolver | 0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63 |
| Reverse Registrar | 0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb |
| Name Wrapper | 0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401 |
| Universal Resolver | 0xce01f8eee7E479C928F8919abD53E553a36CeF67 |
Check Availability
Via the ENS subgraph:
curl -s -X POST \
--url "https://gateway.thegraph.com/api/$GRAPH_API_KEY/subgraphs/id/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH" \
--header 'Content-Type: application/json' \
--data '{"query":"{ registrations(where: { labelName: \"myname\" }) { labelName expiryDate } }"}'
If no result or expiryDate is in the past (+ 90 day grace period), the name is available.
Or link the user to check directly: https://ens.app/myname.eth
Registration Flow
Registration uses a 2-step commit/reveal process (prevents front-running):
- Check availability (subgraph query above).
- Check price: ~$5/year for 5+ char names. Current ETH price determines the exact cost.
- Present summary:
Register myname.eth: • Cost: ~0.002 ETH ($5) for 1 year • Chain: Ethereum mainnet • 2-step process (~2 min total) Register? - Step 1 — Commit: Call
commit(bytes32)on the ETH Registrar Controller viadefi_send_transaction(chainId: 1). The commitment hash must be computed from the name, owner address, duration, and a random secret. - Wait 60 seconds (tell the user: "Commitment submitted. Registration completes in ~1 minute.").
- Step 2 — Register: Call
register(name, owner, duration, secret, resolver, data, reverseRecord, fuses)with the name price asvalue. - Confirm:
myname.eth registered! Yours for 1 year (expires Feb 2027). [View tx](...) - Store in strategy and suggest setting a primary name.
Simpler alternative: Direct the user to the ENS Manager App for registration: https://ens.app/myname.eth — this handles the full flow with a nice UI. Recommend this for first-time registrations.
Renewal
Simpler than registration — single transaction, no commit step.
When user says "renew myname.eth":
- Look up current expiry via subgraph or strategy.
- Get renewal price (same as registration pricing).
- Present summary:
Renew myname.eth: • Current expiry: Feb 8, 2027 • Cost: ~0.002 ETH ($5) for 1 year • New expiry: Feb 8, 2028 - On approval: Call
renew(string name, uint256 duration)on the ETH Registrar Controller viadefi_send_transaction(chainId: 1) with the renewal price asvalue. Duration in seconds (1 year = 31536000). - Update expiry in strategy.
Grace period: Names have a 90-day grace period after expiry. Only the original owner can renew during this window. After grace period, name goes to public auction with a temporary premium that decreases over 21 days.
Setting Records
Set Primary Name (Reverse Record)
When user says "set my ENS primary name" or "make myname.eth my primary":
- Call
setName(string name)on the Reverse Registrar (0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb) viadefi_send_transactionon mainnet (chainId: 1). - This makes the user's address resolve to
myname.ethin reverse lookups. - The user must own the name and it must point to their address.
Set Text Records
Update social/text records via the Public Resolver (0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63):
- Function:
setText(bytes32 node, string key, string value) - The
nodeis the namehash of the full name. - Common keys:
com.twitter,com.github,url,email,avatar,description
For complex record updates, recommend the ENS Manager App: https://ens.app/myname.eth
Expiry Monitoring
Store registered ENS names in the user's strategy for heartbeat monitoring:
{
"ensNames": [
{
"name": "fabri.eth",
"expiry": "2027-02-08T00:00:00Z",
"isPrimary": true
}
]
}
During heartbeats, check ensNames from each user's strategy:
- 30 days before expiry: "Your name fabri.eth expires in 30 days. Want to renew?"
- 7 days before expiry: "fabri.eth expires in 7 days. Renew now to keep it."
- Expired (in grace period): "fabri.eth expired! You have 90 days to renew before it's released."
Data Storage — Strategy JSON
ENS data is stored per-user in strategy JSON (via defi_set_strategy):
{
"ensNames": [
{
"name": "fabri.eth",
"expiry": "2027-02-08T00:00:00Z",
"isPrimary": true
}
],
"ensPreferences": {
"showEnsInPortfolio": true,
"expiryAlertDays": 30
}
}
Read via defi_get_strategy, write via defi_set_strategy. Automatically per-user.
Narrative data (e.g., "resolved vitalik.eth for a 0.1 ETH transfer") goes to per-user daily memory.
Rules
- Always resolve before transacting. Never pass
.ethnames to LI.FI or transaction tools. Resolve to a0xaddress first. - Confirm resolved address. Always show the user the resolved
0xaddress before sending funds. ENS records can change. - Mainnet only for registration/renewal. .eth names live on Ethereum mainnet. Flag if user needs to bridge ETH for gas + fees.
- No permission for lookups. ENS resolution and profile lookups are read operations — do them silently.
- Cache within session. If you resolve a name in a conversation, reuse the result. Don't re-resolve every message.
- Handle failures gracefully. If resolution fails (name doesn't exist, API down), tell the user clearly. Never guess an address.
- Monitor expiry during heartbeats. Check
ensNamesin user strategies. Alert before names expire. - Per-user isolation. ENS data lives in the user's strategy JSON. Never cross-read.
- Suggest ENS once. If user doesn't have an ENS name and frequently uses their raw address, mention ENS once. Don't push it.
- ENSv2 awareness. ENS is migrating to a new L2-based system (ENSv2). Current mainnet registration still works. Be aware this may change.