A CLI for managing Basenames on Base (L2)
- Full Registration Flow — Register basenames directly from the command line with availability checks, price quotes, and anti-frontrunning protection
- Ledger Hardware Wallet Support — Sign transactions securely with your Ledger device instead of storing private keys
- Smart Contract Naming — Name your smart contracts with subnames under your basename, with full forward and reverse resolution support
- Name Discovery — List all basenames owned by any address via ENSNode indexing (see note on indexing gaps)
- Record Verification — Verify that all onchain records are correctly configured
- ENSNode Integration — Fast, fully indexed ENS queries with automatic onchain fallback
- Complete Record Management — Edit text records, addresses, resolvers, ABIs, and content hashes
- Install dependencies:
cd basenames-cli
npm install- Build the CLI:
npm run build- Link it globally (so you can use
basenamesfrom anywhere):
npm linkNow you can use basenames from any directory:
basenames resolve mynameIf you don't want to link globally, you can use it directly:
# From the basenames-cli directory
npm run dev -- resolve myname
# Or using tsx directly
npx tsx src/index.ts resolve myname
# Or using the built version
node dist/index.js resolve mynameTo remove the global link:
npm unlink -g @basenames/cliBasenames CLI supports both Base Mainnet and Base Sepolia testnet.
| Network | Parent Domain | Chain ID |
|---|---|---|
| Base Mainnet | base.eth |
8453 |
| Base Sepolia | basetest.eth |
84532 |
Use the --network flag (or -n) to switch between networks:
# Use Base Sepolia testnet (default)
basenames resolve myname
# Use Base Mainnet
basenames resolve myname --network base
basenames resolve myname -n base# Private key for write operations (required unless using --ledger)
export BASENAMES_PRIVATE_KEY="0x..."
# Network-specific RPC URLs (recommended)
export BASE_RPC_URL_BASE="https://base.drpc.org"Priority order for RPC URLs:
- Network-specific URL (
BASE_RPC_URL_BASEorBASE_RPC_URL_BASESEPOLIA) - Generic URL (
BASE_RPC_URL) - Default from config (network-specific default)
All write commands support Ledger hardware wallet signing via the --ledger flag. This is the recommended approach for security-sensitive operations.
- Connect your Ledger device via USB
- Unlock it (enter PIN)
- Open the Ethereum app
- Enable "Blind signing" in Ethereum app settings (Settings > Blind signing > Enable)
# Register with Ledger
basenames register --ledger myname
# Use a specific account index (default: 0)
basenames register --ledger --account-index 2 myname
# Edit records with Ledger
basenames edit txt --ledger myname description "My basename"
basenames edit primary --ledger myname
# Name a contract with Ledger
basenames name --ledger 0x1234...5678 vault --parent myname.basetest.eth| Command | Ledger Support |
|---|---|
register |
✅ --ledger |
edit txt |
✅ --ledger |
edit address |
✅ --ledger |
edit primary |
✅ --ledger |
name |
✅ --ledger |
- Private keys never leave the device — Your keys are stored securely on the Ledger hardware
- Physical confirmation — Every transaction must be approved on the device screen
- No environment variables — No need to store private keys in
BASENAMES_PRIVATE_KEY
Register a new basename with a complete flow: availability check, price calculation, commitment, and registration.
# Register a basename for 1 year
basenames register coolname
# Register with Ledger hardware wallet
basenames register --ledger coolname
# Register for multiple years
basenames register coolname --years 2
# Register on mainnet
basenames register coolname -n baseCheck if a basename is available for registration with price quote.
basenames available coolname
# ✓ coolname.basetest.eth is available!
# Price: 0.001 ETH/yearList all basenames owned by an address.
basenames list 0x1234567890123456789012345678901234567890Note on ENSNode Indexing: The
listcommand relies on ENSNode's indexed data. Some basenames may not appear in the list if they haven't been indexed yet by ENSNode, even though they exist on-chain. This is a known indexing gap that has been communicated to the ENSNode team. You can still query individual names that aren't indexed usingbasenames profile <name>orbasenames resolve <name>, which use direct on-chain queries.
Verify that all records for a basename are correctly set onchain.
basenames verify myname
# Verifying myname.basetest.eth
# ============================
# ✓ Resolver is set
# ✓ Address record matches owner
# ✓ Primary name is set
# ✓ All records verified!Name your smart contracts on Base with ENS subnames. This enables both forward resolution (name → address) and reverse resolution (address → name).
Assign a basename to a smart contract you own.
# Name a contract under your basename
basenames name <contract-address> <label> --parent <your-basename>
# Example: Name contract as "vault.myname.basetest.eth"
basenames name 0x1234...5678 vault --parent myname.basetest.eth
# Use Ledger for signing
basenames name --ledger 0x1234...5678 vault --parent myname.basetest.eth
# Skip reverse resolution (forward only)
basenames name 0x1234...5678 vault --parent myname.basetest.eth --no-reverseWhat this does:
- Verifies you own the parent domain
- Creates the subname via the ENS Registry
- Sets forward resolution (name → contract address) with L2 coin type
- Sets reverse resolution (contract address → name) if the contract supports it
For reverse resolution to work, your contract must implement ERC173 (Ownable):
function owner() external view returns (address);The owner() function must return an address you control. This is how the L2 Reverse Registrar verifies you're authorized to set the contract's primary name.
Common patterns that work:
- OpenZeppelin's
Ownable - Custom
owner()implementations - Any ERC173-compliant contract
If your contract doesn't have owner(): Forward resolution will still work, but reverse resolution will be skipped with a warning.
A test contract and deployment script are included for testing the naming flow:
# Deploy an Ownable test contract to Base Sepolia
npx tsx scripts/deploy-ownable-contract.ts
# The script will output the deployed address and next steps
# ✅ Ownable contract deployed successfully!
# Address: 0x1234...5678
#
# 📝 Next step - name this contract:
# basenames name 0x1234...5678 mycontract --parent yourname.basetest.ethPrerequisites for the deploy script:
- Foundry installed
BASENAMES_PRIVATE_KEYset in.env.local- Base Sepolia ETH in your wallet
Resolve a basename to an address or vice versa.
# Resolve basename to address
basenames resolve vitalik
# 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
# Resolve address to basename (reverse lookup)
basenames resolve 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
# vitalik.basetest.eth
# Get a specific TXT record
basenames resolve myname --txt com.github
# myusernameDisplay a complete Basename profile with all records.
basenames profile myname
# Basenames Profile
# =================
#
# Name: myname.basetest.eth
# Address: 0x1234567890123456789012345678901234567890
# Owner: 0x1234567890123456789012345678901234567890
# Resolver: 0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBA
# Primary: ✓ Set
# Expires: 2/5/2026
#
# Text Records:
# Avatar: https://example.com/avatar.png
# Bio: Hello, I'm on Base!
# Twitter: myhandleGet the current resolver address for a basename.
basenames resolver myname
# 0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBAGenerate a namehash for a basename.
basenames namehash mynameGenerate a labelhash for a label.
basenames labelhash mynameList deployed Basenames contracts for all networks.
basenames deploymentsEdit commands require either BASENAMES_PRIVATE_KEY environment variable or --ledger flag.
# Option 1: Environment variable
export BASENAMES_PRIVATE_KEY="0x..."
# Option 2: Ledger hardware wallet (recommended)
basenames edit txt --ledger myname description "Hello"
⚠️ Security Warning: Never commit your private key or add it permanently to shell config files! Consider using--ledgerinstead.
Set or clear a text record.
basenames edit txt myname com.github myusername
basenames edit txt myname com.github null # clear
# With Ledger
basenames edit txt --ledger myname com.github myusernameStandard text keys: avatar, description, display, email, url, com.github, com.twitter, com.discord, com.warpcast, org.telegram
Set or clear an address record.
basenames edit address myname ETH 0x1234...
# With Ledger
basenames edit address --ledger myname ETH 0x1234...Set the primary basename for your address.
basenames edit primary myname
# With Ledger
basenames edit primary --ledger mynameSet the resolver for a basename.
basenames edit resolver myname 0x6533...Set or clear an ABI record.
basenames edit abi myname ./contract-abi.jsonSet or clear a content hash.
basenames edit contenthash myname ipfs://QmRAQB6Y...| Contract | Address |
|---|---|
| Registry | 0x1493b2567056c2181630115660963E13A8E32735 |
| L2Resolver | 0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBA |
| RegistrarController | 0x49aE3cC2e3AA768B1e5654f5D3C6002144A59581 |
| BaseRegistrar | 0xa0c70ec36c010b55e3c434d6c6ebeec50c705794 |
| ReverseRegistrar | 0x876eF94ce0773052a2f81921E70FF25a5e76841f |
| Contract | Address |
|---|---|
| Registry | 0xb94704422c2a1e396835a571837aa5ae53285a95 |
| L2Resolver | 0xC6d566A56A1aFf6508b41f6c90ff131615583BCD |
| RegistrarController | 0x4cCb0BB02FCABA27e82a56646E81d8c5bC4119a5 |
| BaseRegistrar | 0x03c4738Ee98aE44591e1A4A4F3CaB6641d95DD9a |
| ReverseRegistrar | 0x79ea96012eea67a83431f1701b3dff7e37f9e282 |
The list command relies entirely on ENSNode's indexed data. Some basenames may not appear in the list if they haven't been indexed yet by ENSNode, even though they exist on-chain and are fully functional.
What this means:
- Names that aren't indexed won't appear in
basenames list <address> - You can still query individual names using
basenames profile <name>orbasenames resolve <name> - All other commands work normally (they use direct on-chain queries)
Status: This indexing gap has been communicated to the ENSNode team. The issue affects a subset of basenames and appears to be related to how ENSNode processes registration events from certain contracts or during specific time periods.
Workaround: Use basenames profile <name> to query individual names that don't appear in the list.
MIT
