The dotHYPEController is the logic layer for domain registration. It manages who can mint, how much they pay, and how names are allocated across phases.
All domain registrations and renewals are routed through the controller; it’s the decision-making contract that sits between users and the registry.
This is where pricing, access rules, signature verification, and allowlist enforcement live.
The Controller handles:
Enforcing registration pricing (based on name length and duration)
Processing payments in $HYPE (based on USD-pegged prices)
Verifying signed requests to prevent front-running (via EIP-712)
Checking Merkle proofs for allowlisted mints
Managing name reservations (for pre-approved addresses)
Think of it as the programmable access layer for the dotHYPE registry.
dotHYPE uses a simple but powerful pricing structure:
Price is based on name length (e.g., 3-char names cost more than 5+)
All prices are denominated in USD
Payment is made in $HYPE, converted using an onchain oracle
Registration and renewal are priced separately
Example (public mint):
3 characters: $100/year
4 characters: $40/year
5+ characters: $15/year
Pricing can be updated over time by the protocol owner, but logic is hardcoded for length-based tiers.
To fetch the price:
function getPrice(string calldata name, uint256 duration) public view returns (uint256);
Most mints use EIP-712 structured data to ensure fair access. This prevents bots from front-running open registrations.
function registerWithSignature(...) external payable returns (uint256);
This flow:
Verifies a signed message from an authorized backend signer
Confirms the nonce hasn't been used
Calculates price and duration
Registers the name through the Registry
Signatures are used in public mint, premium auction, and certain reserved phases.
For whitelist and partner-only phases, we use Merkle trees to verify whether a user can register a name:
function registerWithMerkleProof(...) external payable returns (uint256);
Each address on the allowlist can register one name. Proofs are verified onchain. The Merkle root is updated periodically by the owner.
Some names are explicitly reserved for specific addresses—partners, integrations, or founders.
function registerReservedName(...) external payable returns (uint256);
Only the address that a name is reserved for can register it. No one else can front-run or bypass this.
Admin can assign or remove reservations:
function reserveName(...) external onlyOwner;
function removeReservation(...) external onlyOwner;
Renewals are permissionless. Anyone can renew any name by paying the correct amount.
function renew(string calldata name, uint256 duration) external payable returns (uint256);
Duration is in seconds
Pricing uses the renewal tier (not the first-year price)
The Controller:
Calculates price in $USD
Converts it to $HYPE using a live price oracle
Verifies the user paid enough
Refunds any overpayment
Sends funds to the treasury or designated recipient
All payments are native token compatible—no need for approval flows.
The Controller can be configured by the protocol owner (via multisig):
setAnnualPrice()
setAnnualRenewalPrice()
setRegistry()
setSigner()
setPaymentRecipient()
setPriceOracle()
These functions adjust pricing, set key addresses, and manage access without changing the contract itself.
Events emitted for downstream analytics:
DomainRegistered
DomainRenewed
ReservedNameRegistered
MerkleProofRegistration
SignerUpdated
, PaymentRecipientUpdated
, PriceOracleUpdated
These can be tracked via Subgraph or block explorer for indexers and dashboards.
If you’re building a dApp or CLI for minting:
Use getPrice()
to fetch cost before submitting
Use registerWithSignature()
for standard public minting
Use registerWithMerkleProof()
for allowlisted flows
You don’t need to interact with the Registry directly—the Controller routes all writes.
Want to mint for users via backend signature? Check our SDK guide or integration examples.
To see how a registered name becomes usable across dApps, dashboards, and wallets, continue to the Resolver Contract →