Overview
TheLockedMigrationController handles migration of locked .eth second-level domain (2LD) names from the ENS v1 NameWrapper to the v2 registry system. Locked names are those with the CANNOT_UNWRAP fuse set, indicating permanent commitment to the wrapped state.
Contract Location:
contracts/src/migration/LockedMigrationController.solInherits from WrapperReceiver to handle ERC1155 token transfers and migration logic.Architecture
Inheritance Structure
Key Components
WrapperReceiver (Base Contract)
WrapperReceiver (Base Contract)
Provides the core migration functionality:
- ERC1155 receiver implementation
- Fuse to role translation logic
- Subregistry deployment via VerifiableFactory
- Migration data validation
- Batch migration support
VerifiableFactory
VerifiableFactory
Deploys deterministic subregistry contracts:
- CREATE2 deployment for predictable addresses
- Verification of deployment bytecode
- Initialization with migration parameters
ETH Registry
ETH Registry
Target registry for v2 names:
- Permissioned registration system
- Role-based access control
- Hierarchical subregistry support
Contract Interface
Constructor
ethRegistry: The v2 ETH Registry contractnameWrapper: The v1 NameWrapper contractverifiableFactory: Factory for deploying WrapperRegistry subregistrieswrapperRegistryImpl: Implementation contract for WrapperRegistry proxies
Immutable State
Migration Process
Step-by-Step Flow
Pre-Migration: Reserve Names
Names must be marked as
RESERVED in the v2 ETH Registry before migration. This prevents registration through other flows.Controller Receives Token
The
onERC1155Received() hook is triggered, starting the migration process.Validate Migration Data
Controller validates:
- Token ID matches computed namehash
- Name is locked (
CANNOT_UNWRAPset) - Owner is not zero address
- Name is not expired
Fuse Translation
The migration translates v1 fuses to v2 roles through the_generateRoleBitmapsFromFuses() function:
Token Roles (Parent Registry Permissions)
Subregistry Roles (Child Registry Permissions)
Fuses Frozen: When
CANNOT_BURN_FUSES is set, admin roles are not granted, making permissions permanent.Resolver Handling
The migration intelligently handles resolver configuration:Locked Resolver (CANNOT_SET_RESOLVER set)
Locked Resolver (CANNOT_SET_RESOLVER set)
- Current v1 resolver address is read from the registry
- Resolver address is transferred to v2 registration
- Ensures continuity of name resolution
Unlocked Resolver (CANNOT_SET_RESOLVER not set)
Unlocked Resolver (CANNOT_SET_RESOLVER not set)
- v1 resolver is cleared to prevent confusion
- v2 owner can set their own resolver
- Grants
ROLE_SET_RESOLVERandROLE_SET_RESOLVER_ADMIN
Subregistry Deployment
CREATE2 Deployment
Subregistries are deployed deterministically using CREATE2:Benefits of Deterministic Deployment
Predictable Addresses
Subregistry addresses can be computed off-chain before deployment.
Verifiable Bytecode
VerifiableFactory ensures deployed bytecode matches expected implementation.
No Duplicate Deployments
CREATE2 prevents deploying the same subregistry twice.
Cross-Chain Consistency
Same salt produces same address across different chains.
Security Guarantees
Validation Checks
Caller Authorization
Caller Authorization
Lock Verification
Lock Verification
Namehash Validation
Namehash Validation
Owner Validation
Owner Validation
Expiry Check
Expiry Check
Batch Migration
Migrate multiple locked names in a single transaction:Migration Data Structure
IWrapperRegistry.Data
Field Details
label
label
Type:
stringThe first label of the name being migrated. For “myname.eth”, this is “myname”.Constraints:- Length must be 1-255 bytes
- Must match the token ID being transferred
owner
owner
Type:
addressThe owner of the name in the v2 system.Constraints:- Cannot be zero address
- Receives all granted roles
resolver
resolver
Type:
addressResolver address for the name.Behavior:- If
CANNOT_SET_RESOLVERis set: v1 resolver is used regardless of this value - If
CANNOT_SET_RESOLVERis not set: This value is used as the resolver
salt
salt
Type:
uint256Salt value for CREATE2 deployment of the WrapperRegistry subregistry.Usage:- Enables deterministic subregistry addresses
- Should be unique per name to avoid deployment conflicts
- Can be zero for simple deployments
Error Reference
LockedMigrationController Errors
Transfer not initiated by the NameWrapper contract.Solution: Only transfer through
NameWrapper.safeTransferFrom()Name doesn’t have
CANNOT_UNWRAP fuse set.Solution: Use UnlockedMigrationController insteadToken ID doesn’t match computed namehash from label.Solution: Verify label is correct for the token being migrated
Owner address is zero address.Solution: Provide valid owner address
Migration data is too small or malformed.Solution: Ensure data is properly ABI-encoded
Example: Complete Migration
Best Practices
Verify Lock Status
Always verify
CANNOT_UNWRAP is set before using locked migration.Choose Salt Carefully
Use unique, deterministic salt values for predictable subregistry addresses.
Pre-Reserve Names
Ensure names are reserved in v2 registry before migration.
Test on Testnet
Test migration process on testnet before migrating mainnet names.
Verify Roles
Check resulting role bitmap matches expected permissions.
Batch When Possible
Use batch transfers for multiple names to save gas.
Related Documentation
Migration Overview
Understand the complete migration system
Unlocked Migration
Learn about unlocked name migration
Access Control
Understand v2 role-based permissions
Wrapper Registry
Learn about the WrapperRegistry subregistry