A CLI for managing Basenames on Base (L2)
Features
- 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
Installation
Local Development Setup
- Install dependencies:
cd basenames-cli
npm install- Build the CLI:
npm run build
- Link it globally (so you can use
basenamesfrom anywhere):
npm link
Now you can use basenames from any directory:
basenames resolve myname
Alternative: Use Without Linking
If 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 myname
Uninstall
To remove the global link:
npm unlink -g @basenames/cli
Networks
Basenames CLI supports both Base Mainnet and Base Sepolia testnet.
| Network | Parent Domain | Chain ID |
|---|---|---|
| Base Mainnet | base.eth |
8453 |
| Base Sepolia | basetest.eth |
84532 |
Switching Networks
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
Environment Variables
# 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)
Ledger Hardware Wallet
All write commands support Ledger hardware wallet signing via the --ledger flag. This is the recommended approach for security-sensitive operations.
Setup
- 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)
Usage
# 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
Supported Commands
| Command | Ledger Support |
|---|---|
register |
✅ --ledger |
edit txt |
✅ --ledger |
edit address |
✅ --ledger |
edit primary |
✅ --ledger |
name |
✅ --ledger |
Why Use 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
Registration & Discovery
register
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 base
available
Check if a basename is available for registration with price quote.
basenames available coolname # ✓ coolname.basetest.eth is available! # Price: 0.001 ETH/year
list
List all basenames owned by an address.
basenames list 0x1234567890123456789012345678901234567890
Note 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
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!
Smart Contract Naming
Name your smart contracts on Base with ENS subnames. This enables both forward resolution (name → address) and reverse resolution (address → name).
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-reverse
What 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
Reverse Resolution Requirements
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.
Testing with a Sample Contract
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.eth
Prerequisites for the deploy script:
- Foundry installed
BASENAMES_PRIVATE_KEYset in.env.local- Base Sepolia ETH in your wallet
Read Commands
resolve
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 # myusername
profile
Display 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: myhandle
resolver
Get the current resolver address for a basename.
basenames resolver myname
# 0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBAnamehash
Generate a namehash for a basename.
basenames namehash myname
labelhash
Generate a labelhash for a label.
basenames labelhash myname
deployments
List deployed Basenames contracts for all networks.
basenames deployments
Edit Commands
Edit 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.
edit txt
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 myusername
Standard text keys: avatar, description, display, email, url, com.github, com.twitter, com.discord, com.warpcast, org.telegram
edit address
Set or clear an address record.
basenames edit address myname ETH 0x1234...
# With Ledger
basenames edit address --ledger myname ETH 0x1234...edit primary
Set the primary basename for your address.
basenames edit primary myname
# With Ledger
basenames edit primary --ledger mynameedit resolver
Set the resolver for a basename.
basenames edit resolver myname 0x6533...
edit abi
Set or clear an ABI record.
basenames edit abi myname ./contract-abi.json
edit contenthash
Set or clear a content hash.
basenames edit contenthash myname ipfs://QmRAQB6Y...
Contract Addresses
Base Sepolia (Testnet)
| Contract | Address |
|---|---|
| Registry | 0x1493b2567056c2181630115660963E13A8E32735 |
| L2Resolver | 0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBA |
| RegistrarController | 0x49aE3cC2e3AA768B1e5654f5D3C6002144A59581 |
| BaseRegistrar | 0xa0c70ec36c010b55e3c434d6c6ebeec50c705794 |
| ReverseRegistrar | 0x876eF94ce0773052a2f81921E70FF25a5e76841f |
Base Mainnet
| Contract | Address |
|---|---|
| Registry | 0xb94704422c2a1e396835a571837aa5ae53285a95 |
| L2Resolver | 0xC6d566A56A1aFf6508b41f6c90ff131615583BCD |
| RegistrarController | 0x4cCb0BB02FCABA27e82a56646E81d8c5bC4119a5 |
| BaseRegistrar | 0x03c4738Ee98aE44591e1A4A4F3CaB6641d95DD9a |
| ReverseRegistrar | 0x79ea96012eea67a83431f1701b3dff7e37f9e282 |
Known Issues & Limitations
ENSNode Indexing Gap
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.
License
MIT
