The FIFSRegistrar (First-In-First-Served Registrar) is a simple registrar contract that allocates subdomains to the first person to claim them. It provides a straightforward way to distribute subdomains under a parent node.
Overview
FIFSRegistrar provides:
- Simple first-come-first-served subdomain registration
- Re-registration capability for existing owners
- No expiration - registrations are permanent until transferred
- Minimal registration logic with low gas costs
Import
import '@ensdomains/ens-contracts/contracts/registry/FIFSRegistrar.sol';
Constructor
constructor(ENS ensAddr, bytes32 node) public
Creates a new FIFS registrar.
The address of the ENS registry
The node that this registrar administers (e.g., the hash of ‘myname.eth’)
State Variables
ens
Reference to the ENS registry contract.
rootNode
The parent node that this registrar has authority over.
Modifiers
only_owner
modifier only_owner(bytes32 label)
Ensures that only unregistered subdomains (owner is address(0x0)) or the current owner can register/update a subdomain.
The hash of the label to check
Functions
register
function register(bytes32 label, address owner) public only_owner(label)
Registers a name or changes the owner of an existing registration.
The hash of the label to register (e.g., keccak256(‘subdomain’))
The address of the new owner
This function can only be called if:
- The subdomain is currently unregistered (owner is
address(0x0)), OR
- The caller is the current owner of the subdomain
How It Works
- The FIFSRegistrar is deployed with a reference to the ENS registry and a parent node it controls
- The deployer must transfer ownership of the parent node to the FIFSRegistrar contract
- Users can call
register() with a label hash and owner address
- The registrar checks if the subdomain is available or if the caller is the current owner
- If authorized, it creates/updates the subdomain using
ens.setSubnodeOwner()
Usage Examples
Deployment
Registering Subdomains
Transfer Subdomain
With Resolver
import '@ensdomains/ens-contracts/contracts/registry/FIFSRegistrar.sol';
import '@ensdomains/ens-contracts/contracts/registry/ENS.sol';
// Assume we own 'myapp.eth' and want to let users register subdomains
ENS ens = ENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e);
bytes32 myAppNode = 0x1234...; // namehash('myapp.eth')
// Deploy the registrar
FIFSRegistrar registrar = new FIFSRegistrar(ens, myAppNode);
// Transfer ownership of 'myapp.eth' to the registrar
ens.setOwner(myAppNode, address(registrar));
// Now anyone can register subdomains under myapp.eth
import '@ensdomains/ens-contracts/contracts/registry/FIFSRegistrar.sol';
contract SubdomainClaimer {
FIFSRegistrar public registrar;
constructor(FIFSRegistrar _registrar) {
registrar = _registrar;
}
function claimSubdomain(string memory label) public {
bytes32 labelHash = keccak256(bytes(label));
// Register subdomain with caller as owner
registrar.register(labelHash, msg.sender);
// User now owns subdomain.myapp.eth
}
function claimForAddress(
string memory label,
address recipient
) public {
bytes32 labelHash = keccak256(bytes(label));
// Register subdomain with specific address as owner
registrar.register(labelHash, recipient);
}
}
import '@ensdomains/ens-contracts/contracts/registry/FIFSRegistrar.sol';
contract SubdomainTransfer {
FIFSRegistrar public registrar;
constructor(FIFSRegistrar _registrar) {
registrar = _registrar;
}
function transferSubdomain(
string memory label,
address newOwner
) public {
bytes32 labelHash = keccak256(bytes(label));
// Only current owner can call this successfully
// The only_owner modifier will check ownership
registrar.register(labelHash, newOwner);
// Subdomain ownership is now transferred to newOwner
}
}
import '@ensdomains/ens-contracts/contracts/registry/FIFSRegistrar.sol';
import '@ensdomains/ens-contracts/contracts/registry/ENS.sol';
contract SubdomainWithResolver {
FIFSRegistrar public registrar;
ENS public ens;
address public defaultResolver;
constructor(
FIFSRegistrar _registrar,
ENS _ens,
address _resolver
) {
registrar = _registrar;
ens = _ens;
defaultResolver = _resolver;
}
function registerWithResolver(string memory label) public {
bytes32 labelHash = keccak256(bytes(label));
// First register the subdomain
registrar.register(labelHash, msg.sender);
// Calculate the full subnode hash
bytes32 subnode = keccak256(
abi.encodePacked(registrar.rootNode(), labelHash)
);
// Set the resolver for the subdomain
// Caller must be owner, which they are from the previous step
ens.setResolver(subnode, defaultResolver);
}
}
Use Cases
Application Subdomains
Allow users of your dApp to claim personalized subdomains:
alice.myapp.eth
bob.myapp.eth
dao.myapp.eth
Organization Structure
Create subdomains for different parts of an organization:
engineering.company.eth
marketing.company.eth
sales.company.eth
Simple Name Distribution
Provide a straightforward way to distribute names without complex auction mechanics or pricing.
Limitations
- No built-in expiration mechanism - names are permanent unless transferred
- No pricing mechanism - registration is free (only gas costs)
- No name length restrictions - single character names can be claimed
- Vulnerable to frontrunning - miners or MEV searchers can see and claim names first
- No reclaim mechanism for the parent node owner
Comparison with TestRegistrar
Unlike the FIFSRegistrar:
- TestRegistrar includes automatic expiration (4 weeks)
- TestRegistrar allows re-registration after expiration by anyone
- TestRegistrar is designed specifically for test networks
Choose FIFSRegistrar for permanent subdomain allocation, or TestRegistrar for temporary test environments.