DotnsRegistrarController
Inherits: Initializable, UUPSUpgradeable, OwnableUpgradeable, ERC165Upgradeable, ReentrancyGuardTransient, IDotnsRegistrarController
Title: dotNS Registrar Controller
Allocates top-level labels using a commit reveal scheme.
Orchestrates allocation, PoP validation, pricing enforcement, forward registry
wiring, default reverse resolution, and immutable store writing.
Tokenisation: the minted ERC721 tokenId is uint256(node), where
node = namehash(tldNode, labelhash). The registry stores a sentinel owner
(address(0)) for tokenised nodes and derives ownership from the ERC721 registrar for
authorisation.
Note: security-contact: admin@parity.io
Constants
MAX_ALLOWED_COMMITMENT_AGE
Upper bound for commitment validity to cap storage griefing risk.
uint256 public constant MAX_ALLOWED_COMMITMENT_AGE = 7 days
State Variables
minCommitmentAge
Minimum age a commitment must reach before reveal.
uint256 public minCommitmentAge
maxCommitmentAge
Maximum age after which a commitment expires.
uint256 public maxCommitmentAge
commitments
Stores Mapping of commitment hashes to timestamp committed.
mapping(bytes32 hash => uint256 timestamp) public commitments
committedPricingVersion
Cost-model version stamped on a commitment at commit time.
Recorded from the registry's current version when commit runs, so the reveal can bind
a registration to the version that was current then. A caller cannot commit against an
arbitrary earlier, cheaper version: the reveal rejects a pricingVersion that differs
from this stamp.
mapping(bytes32 hash => uint256 version) public committedPricingVersion
protocolRegistry
Protocol-level address registry for all dotNS contracts.
IDotnsProtocolRegistry public protocolRegistry
__gap
Reserved storage space to allow for layout changes in the future.
uint256[50] private __gap
Functions
constructor
Note: oz-upgrades-unsafe-allow: constructor
constructor() ;
initialize
Initialises the registrar controller.
Callable once through the UUPS proxy; direct calls on the implementation revert
with @custom:reverts InvalidInitialization, and any nested call outside an active
initialiser scope reverts with @custom:reverts NotInitializing. Validates the
commitment window bounds: minAge must be strictly positive (otherwise
Notes:
-
reverts: MinCommitmentAgeZero) so a reveal cannot land in the same block as its commit;
maxAgemust exceedminAge(otherwise -
reverts: MaxCommitmentAgeTooLow) and must stay within
MAX_ALLOWED_COMMITMENT_AGE(otherwise @custom:reverts MaxCommitmentAgeTooHigh) before wiring the protocol registry.
function initialize(
address initialOwner,
IDotnsProtocolRegistry registry,
uint256 minAge,
uint256 maxAge
)
external
initializer;
Parameters
| Name | Type | Description |
|---|---|---|
initialOwner | address | Address that owns the contract once initialised. |
registry | IDotnsProtocolRegistry | |
minAge | uint256 | |
maxAge | uint256 |
available
Returns whether a label is available for registration.
Validates the canonical DNS-label shape (otherwise @custom:reverts InvalidLabel) and rejects labels below the minimum-length policy with
Note: reverts: LabelTooShort before checking ERC721 availability on the registrar.
function available(string calldata label) public view override returns (bool);
makeCommitment
Computes the commitment hash for a registration.
Uses abi.encode so the variable-width label is length-prefixed and the boundary
between label and the fixed-width owner, secret, reserved, maxPrice, and
pricingVersion fields is unambiguous, binding the commitment to the exact tuple. The
price ceiling and cost-model version are part of that tuple, so neither can be altered
between commit and reveal.
function makeCommitment(Registration calldata registration)
public
pure
override
returns (bytes32 commitment);
commit
Submits a commitment for a future registration.
Idempotent over expiry: re-committing an unexpired hash reverts with
Note:
reverts: UnexpiredCommitmentExists (front-running guard); a hash whose stored
timestamp has passed maxCommitmentAge overwrites the slot so storage cannot be
permanently griefed. The expiry boundary is inclusive on the commit side
(committedAt + maxCommitmentAge <= block.timestamp overwrites) and exclusive on the
reveal side (register rejects at the same instant with @custom:reverts
CommitmentTooOld), so the slot is overwritable from exactly the timestamp at which
reveal begins rejecting it. Stamps the cost model's current version on the commitment, so
the reveal binds to the version live now and rejects a pricingVersion bound to an earlier
one with @custom:reverts PricingVersionMismatch. Emits @custom:emits NameCommitted on
success.
function commit(bytes32 commitment) external override;
_currentPricingVersion
Reads the cost model's current version through the protocol registry.
Resolved at commit time so the stamp binds the version live then, not at reveal.
function _currentPricingVersion() internal view returns (uint256 pricingVersion);
Returns
| Name | Type | Description |
|---|---|---|
pricingVersion | uint256 | The current cost-model version. |
register
Registers a name after the commitment delay.
Validates the label shape (otherwise @custom:reverts InvalidLabel), rejects labels
below the minimum length policy (@custom:reverts LabelTooShort), and ERC721 availability
(otherwise @custom:reverts NameNotAvailable), then consumes the prior commitment, which
fails with @custom:reverts CommitmentNotFound when no commitment exists for the supplied
registration, @custom:reverts CommitmentTooNew before minCommitmentAge, and
Note:
reverts: CommitmentTooOld past maxCommitmentAge, and finally resolves the
configured escrow address from the protocol registry (otherwise
function register(Registration calldata registration) external payable override nonReentrant;
registerReserved
Registers a granted name after the commitment delay, at zero base cost.
Grant-backed issuance path: skips the PoP price check and charges no deposit, but
reuses the same commit-reveal pipeline so the same anti-front-running guarantees apply.
Authority is either a substrate Root dispatch or a grant naming registration.owner on the
name whitelist registered under DotnsConstants.NAME_WHITELIST; anything else
Notes:
-
reverts: NameNotGranted, and an unset registry key
-
function: IDotnsReverseResolver.claimReverseRecord, which checks ownership. Validates the label shape (otherwise @custom:reverts InvalidLabel) and ERC721 availability (otherwise @custom:reverts NameNotAvailable), then consumes the prior commitment, which fails with @custom:reverts CommitmentNotFound, @custom:reverts CommitmentTooNew, or
function registerReserved(Registration calldata registration) external override nonReentrant;
supportsInterface
Returns true if this contract implements the interface defined by
interfaceId. See the corresponding
https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
to learn more about how these ids are created.
This function call must use less than 30 000 gas.
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC165Upgradeable, IERC165)
returns (bool);
_validatedLabelNode
Validates label shape and derives (labelhash, node).
Delegates hashing to @custom:contract LabelUtils so the assembly sequence lives in
exactly one place across the codebase. Error ownership stays on this interface: shape
violations revert with InvalidLabel(); labels below the minimum length revert with
LabelTooShort(label) so off-chain consumers can distinguish "shape-valid but below
the policy minimum" from "shape-valid but already minted".
function _validatedLabelNode(string calldata label)
internal
view
returns (bytes32 labelhash, bytes32 node);
_requireAvailableLabel
function _requireAvailableLabel(string calldata label)
internal
view
returns (IDotnsRegistrar registrar, bytes32 labelhash, bytes32 node);
_consumeCommitment
function _consumeCommitment(Registration calldata registration) internal;
_completeRegistration
Completes a commit-reveal registration: mints (or skips when reclaiming), wires forward registry, optionally sets the reverse record, and writes the owner's Store.
On a fresh mint the triad of mint + forward-registry + store-write is delegated to @custom:function RegistrationUtils.registerAndStore, the single canonical implementation shared across every dotNS registration flow. On a reclaim the mint step is skipped (the escrow has already moved custody) and only the registry wiring and store write run. Reverse-record setting and the priced-registration event stay here because they are commit-reveal-specific policy.
function _completeRegistration(
Registration calldata registration,
bytes32 labelhash,
bytes32 node,
uint256 baseCost,
bool setReverseRecord,
IDotnsReverseResolver reverse,
bool isReclaim
)
internal;
_escrow
Returns the configured name escrow from the protocol registry.
function _escrow() internal view returns (address escrow);
_clearStaleReservation
Clears the base-name reservation that the previous holder of a reclaimed name still owns.
Shared by both registration paths so their reclaim clean-up cannot drift apart. The
previous holder may own a reservation on the name's base name, written by a sibling
controller (e.g. the PoP queue head stamp). Once the name moves on, that slot is
garbage, and left alone it blocks the base name for everyone else until it expires.
Only a slot owned by the previous holder is cleared; a slot held by anyone else is
still theirs and stays. Must run before escrow.reclaim, which deletes the position
the previous holder is read from. Reads no msg.sender, so it is safe under a Root
origin: PopRules sees this controller as its caller either way.
function _clearStaleReservation(
IPopRules rules,
address escrow,
uint256 tokenId,
string calldata label
)
internal;
Parameters
| Name | Type | Description |
|---|---|---|
rules | IPopRules | Configured PopRules. |
escrow | address | Configured name escrow. |
tokenId | uint256 | Token being reclaimed. |
label | string | Label being registered. |
_reclaimFromEscrow
Takes a name out of escrow custody and repoints the forward registry at its new holder.
Shared by both registration paths so the ordering constraint lives in one place: the
registry write must follow escrow.reclaim, because setOwner checks ownerOf and
would otherwise see the escrow rather than newOwner. Without the rewrite the prior
owner's resolver pointer follows the name to its new holder.
function _reclaimFromEscrow(
address escrow,
uint256 tokenId,
bytes32 node,
address newOwner
)
internal;
Parameters
| Name | Type | Description |
|---|---|---|
escrow | address | Configured name escrow. |
tokenId | uint256 | Token being reclaimed, equal to uint256(node). |
node | bytes32 | Namehash of the label, as the forward registry keys it. |
newOwner | address | Address taking custody of the name. |
_seedReleasePosition
Opens the escrow release position that every registration path must leave behind.
The single construction site for a position, so "every minted name is releasable" is
one rule rather than one per path. amount is the refundable deposit and may be zero:
DotnsNameEscrow treats recipient as the presence sentinel, so a zero-amount
position is still a position. Public registrations lock the price as the owner's
deposit, whoever paid it. Grant-backed reserved registrations seed zero because they
charge nothing at all; their position carries no value and exists only so release,
redeem and reclaim stay reachable.
function _seedReleasePosition(
address escrow,
uint256 tokenId,
address recipient,
uint256 amount
)
internal;
Parameters
| Name | Type | Description |
|---|---|---|
escrow | address | Configured name escrow. |
tokenId | uint256 | Token the position is keyed to. |
recipient | address | Address the deposit (when non-zero) and the lifecycle are keyed to. |
amount | uint256 | Refundable deposit to lock, forwarded as msg.value. |
version
Returns the release this network declares it runs, read live from the protocol registry so every dotNS contract reports one synchronised value.
Mirror of IDotnsProtocolRegistry.protocolVersion, kept under the historical
version() selector for ABI compatibility. It reports the network's declaration,
not this contract's build; per-contract identity is the codehash declared on the
registry.
function version() external view virtual returns (string memory versionString);
Returns
| Name | Type | Description |
|---|---|---|
versionString | string | Declared release as bare semver, empty when never declared. |
_nameWhitelist
Returns the configured name whitelist from the protocol registry.
function _nameWhitelist() internal view returns (IDotnsNameWhitelist whitelist);
_authorizeUpgrade
Function that should revert when msg.sender is not authorized to upgrade the contract.
Called by
{upgradeToAndCall}.
Normally, this function will use an xref:access.adoc[access control] modifier such as
{Ownable-onlyOwner}.
function _authorizeUpgrade(address) internal onlyOwner {}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner;