Skip to main content
This guide covers deploying ENS contracts to different networks, including mainnet, testnets, and Layer 2 solutions.

Deployment Overview

ENS contracts use Hardhat Deploy for managing deployments across networks. Deployment scripts are versioned and only run once per network.

Network Configuration

The project supports multiple networks configured in hardhat.config.ts:
  • Mainnet - Ethereum mainnet
  • Sepolia - Sepolia testnet
  • Holesky - Holesky testnet
  • Optimism - Optimism L2
  • Arbitrum - Arbitrum L2
  • Localhost - Local development network

Deployment Commands

Deploy to Network

# Deploy to specific network
bun hh --network <network_name> deploy

# Examples:
bun hh --network sepolia deploy
bun hh --network mainnet deploy
bun hh --network optimism deploy
Only deployment scripts that haven’t been run on the specified network will execute.

Deploy L2 Contracts

For Layer 2 deployments:
# Deploy L2-specific contracts
bun run l2:deploy
This deploys L2ReverseRegistrar and UniversalSigValidator to L2 networks.

Deployment Scripts

Deployment scripts are organized by module in the deploy/ directory:
deploy/
├── registry/           # ENS Registry contracts
├── ethregistrar/       # .eth registrar contracts
├── resolvers/          # Resolver contracts
├── wrapper/            # NameWrapper contracts
├── dnsregistrar/       # DNS integration
├── dnssec-oracle/      # DNSSEC oracle
├── l2/                 # Layer 2 contracts
├── reverseregistrar/   # Reverse registrar
└── utils/              # Utility contracts

Deployment Testing

1

Test with Anvil fork

Run a local fork for testing:
# Start Anvil fork
anvil --fork-url https://sepolia.infura.io/v3/YOUR_KEY
2

Deploy to fork

Update hardhat.config.ts with your fork URL:
const config = {
  networks: {
    targetNetwork: {
      url: 'http://127.0.0.1:8545',
    },
  },
}
3

Run deployment

bun hh --network targetNetwork deploy
4

Test deployed contracts

bun run test:deploy

Using Tenderly Virtual TestNet

For staging environments:
  1. Create a Tenderly Virtual TestNet
  2. Update network configuration with Tenderly RPC URL
  3. Deploy using the standard deployment command

Deployment with Impersonation

When you need to deploy without direct access to owner accounts:
# Deploy with account impersonation
./scripts/deploy-with-impersonation.ts --rpc-url <url> --accounts <addr1> [addr2 ...] [--tags <tags>]

# Testnet example
./scripts/deploy-with-impersonation.ts \
  --rpc-url https://sepolia.infura.io/v3/YOUR_KEY \
  --accounts 0x0F32b753aFc8ABad9Ca6fE589F707755f4df2353

# Mainnet example
./scripts/deploy-with-impersonation.ts \
  --rpc-url https://mainnet.infura.io/v3/YOUR_KEY \
  --accounts 0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7
This script makes impersonated accounts from an external node available to Hardhat.

Environment Setup

Required Environment Variables

Create a .env file with:
# API Keys
INFURA_API_KEY=your_infura_api_key
ETHERSCAN_API_KEY=your_etherscan_api_key

# Deployment Accounts
DEPLOYER_KEY=your_deployer_private_key
OWNER_KEY=your_owner_private_key

Account Configuration

The Hardhat config uses two accounts:
  • DEPLOYER_KEY - Account for deploying contracts
  • OWNER_KEY - Account for ownership operations

Versioning Deployment Scripts

When preparing a new deployment:
1

Bump version number

Update the deploy script version to the appropriate new version. This ensures the script will run for each network.
2

Test on testnet

Deploy to testnet first:
bun hh --network sepolia deploy
3

Verify deployment

Check deployment artifacts in deployments/ directory.
4

Deploy to mainnet

After successful testing:
bun hh --network mainnet deploy

L2 Deployment Details

Unified Contract Addresses

L2 contracts (L2ReverseRegistrar) maintain the same address across networks using:
  • Safe multisig wallets
  • CREATE3 helper contract
Safe Addresses:
  • Testnet: 0x343431e9CEb7C19cC8d3eA0EE231bfF82B584910
  • Mainnet: 0x353530FE74098903728Ddb66Ecdb70f52e568eC1
This enables multi-chain signature functionality per EIP-191.

Deployment Artifacts

After deployment, artifacts are stored in:
deployments/
├── mainnet/
├── sepolia/
├── optimism/
└── arbitrum/
Each directory contains:
  • Contract deployment JSON files
  • Contract addresses
  • Constructor arguments
  • Transaction hashes

Verifying Contracts

Verify deployed contracts on Etherscan:
# Verify specific contract
bun run hh multichain-verify <contractName> <address> [constructorArgs...]

Post-Deployment Steps

1

Verify contracts on Etherscan

Ensure all contracts are verified for transparency.
2

Update documentation

Document new contract addresses and deployment details.
3

Test deployed contracts

Run integration tests against deployed contracts:
TEST_REMOTE=1 bun run test:remote
4

Commit deployment artifacts

Commit deployment files to the repository:
git add deployments/
git commit -m "Deploy contracts to <network>"

Troubleshooting

This means the script has already run for this network. To redeploy, remove the deployment artifacts from deployments/<network>/.
Ensure your deployer account has enough ETH for gas fees:
bun run hh accounts --network <network>
Check that:
  • Etherscan API key is correct
  • Contract was deployed with correct compiler settings
  • Constructor arguments match deployment

Release Flow

See the complete release process

Testing Guide

Learn how to test deployments

Build docs developers (and LLMs) love