IDotnsRegistrar
Inherits: IERC721
Title: Dotns Registrar
ERC721-backed ownership for DotNS names with controller-gated registration.
Intentionally minimal and policy-free. Provides ERC721 ownership for registered name token IDs and controller-gated registration; pricing, PoP enforcement, and flow-specific policy live in the controllers.
Note: security-contact: admin@parity.io
Functions
available
Returns whether a registration call may proceed for id.
Signals two distinct paths to the controller. Returns true when the owner slot is
empty (a fresh @custom:function register call may mint) OR when the current owner is
the configured escrow and the released position's redeem window has elapsed (the
controller must then route through @custom:function IDotnsNameEscrow.reclaim instead of
Notes:
-
function: register, because
registercalls_mintwhich rejects existing tokens). All other holders returnfalse. The controller distinguishes the twotruecases via @custom:function exists. Escrow custody inside the redeem window returnsfalse: that window belongs to the previous holder, who may still @custom:function IDotnsNameEscrow.redeem the name, and reclaim would revert until it elapses. Clients wanting the exact moment a released name becomes registrable should readredeemableUntilfrom -
function: IDotnsNameEscrow.getReleasePosition.
function available(uint256 id) external view returns (bool isAvailable);
register
Registers a name permanently.
Permanence is by construction: there is no expire, renew, or release path on
the registrar. Custody only moves via ERC721 transfers (which the registrar polices via
the fee-on-transfer hook) or via escrow reclaim. Restricted to authorised controllers
(otherwise @custom:reverts NotController) and rejects ids that are not available
(otherwise @custom:reverts NameNotAvailable). Emits @custom:emits NameRegistered on
success.
When the caller is the address registered under DotnsConstants.POP_CONTROLLER, the
name is marked soulbound and becomes permanently non-transferable (see
Note: function: isSoulbound). Provenance is read from the protocol registry at mint time, so no other authorised controller can mint a soulbound name and the PoP controller cannot mint an unlocked one. Public registrations from any other controller stay transferable.
function register(uint256 id, address owner, string calldata label) external;
Parameters
| Name | Type | Description |
|---|---|---|
id | uint256 | |
owner | address | |
label | string | The human-readable label string (e.g. "alice"). |
isSoulbound
Returns whether a token is soulbound (PoP-gateway minted and non-transferable).
Durable on-chain marker set once at mint by @custom:function register and never
cleared. A true result means every transfer overload reverts with
Note: reverts: NameSoulbound and @custom:function quoteTransferFee reverts likewise.
function isSoulbound(uint256 tokenId) external view returns (bool soulbound);
Parameters
| Name | Type | Description |
|---|---|---|
tokenId | uint256 | The name's token id. |
Returns
| Name | Type | Description |
|---|---|---|
soulbound | bool | True when the name was minted through the PoP gateway. |
exists
Returns whether a given token id has been minted.
function exists(uint256 tokenId) external view returns (bool tokenExists);
addController
Adds an authorised controller.
Typed against the baseline IDotnsController (not a concrete subtype) so a single
authorisation surface accepts every controller flavour (commit-reveal, PoP gateway,
future variants) without per-flavour setters. Owner-gated (otherwise
Note: reverts: OwnableUnauthorizedAccount); emits @custom:emits ControllerAdded on success.
function addController(IDotnsController controller) external;
removeController
Removes an authorised controller.
Mirrors the @custom:function addController baseline-typed signature so any registered controller can be revoked through the same entry point. Owner-gated (otherwise
Note: reverts: OwnableUnauthorizedAccount); emits @custom:emits ControllerRemoved on success.
function removeController(IDotnsController controller) external;
controllers
Returns whether controller is currently authorised to call
Note: function: register.
function controllers(IDotnsController controller) external view returns (bool authorised);
Parameters
| Name | Type | Description |
|---|---|---|
controller | IDotnsController | Candidate controller. |
Returns
| Name | Type | Description |
|---|---|---|
authorised | bool | True when controller was added via @custom:function addController and has not been removed. |
labelOf
Returns the human-readable label a token was registered with.
Canonical state source for the label string; any client that holds a node or tokenId can resolve the original label in one view call without scanning registration events. Returns the empty string when the token does not exist.
function labelOf(uint256 tokenId) external view returns (string memory label);
quoteTransferFee
Quotes the additional native fee required to transfer a token to to.
Returns the fee from @custom:function PopRules.transferFloor: the name's own price
as the maximum of (i) the reach component charged when the recipient does not meet
the label's required tier and (ii) the downgrade component charged when the
recipient tier is strictly below the sender tier. Self-transfers and
escrow-touching transfers (release into escrow, reclaim out of escrow) return
zero. A token whose sender has no stored label also returns zero because there
is no label-derived price to charge against. Soulbound names are non-transferable
and have no transfer price, so a soulbound tokenId reverts with
Notes:
-
reverts: NameSoulbound rather than returning zero. Rejects a zero
towith -
reverts: ERC721InvalidReceiver, an unminted
tokenIdwith -
reverts: ERC721NonexistentToken via the underlying
ownerOf, and requires the protocol registry to have an escrow configured (otherwise -
reverts: EscrowNotConfigured). Returns zero when the protocol registry has no
STORE_FACTORYconfigured because no label-derived price is reachable.
function quoteTransferFee(
uint256 tokenId,
address to
)
external
view
returns (uint256 requiredFee);
safeTransferFrom
The registrar's _update hook consults @custom:function PopRules.transferFloor
to compute the required transfer fee; if the caller does not forward at least that
amount as msg.value, the transfer reverts with @custom:reverts TransferFeeRequired.
A soulbound name is non-transferable and reverts with @custom:reverts NameSoulbound.
The payable modifier on every transfer overload exists so the fee can be forwarded
in the same call.
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
)
external
payable
override;
safeTransferFrom
Subject to the same fee-on-transfer gate as the four-argument overload; reverts with
Note:
reverts: TransferFeeRequired when the recipient owes a non-zero transfer fee and
the caller has not forwarded it as msg.value, and with @custom:reverts NameSoulbound when
the token is soulbound.
function safeTransferFrom(address from, address to, uint256 tokenId) external payable override;
transferFrom
Subject to the same fee-on-transfer gate as the safe overloads; reverts with
Note:
reverts: TransferFeeRequired when the recipient owes a non-zero transfer fee and
the caller has not forwarded it as msg.value, and with @custom:reverts NameSoulbound when
the token is soulbound.
function transferFrom(address from, address to, uint256 tokenId) external payable override;
Events
NameRegistered
Emitted when a name is registered.
event NameRegistered(uint256 indexed id, address indexed owner, bool soulbound);
Parameters
| Name | Type | Description |
|---|---|---|
id | uint256 | The token id (namehash node) that was minted. |
owner | address | The address that received the name. |
soulbound | bool | True when the name is soulbound: PoP-gateway minted and non-transferable. Lets indexers classify registrations without a per-token @custom:function isSoulbound read. |
ControllerAdded
Emitted when a controller is added.
Typed as the shared baseline @custom:contract IDotnsController so the commit-reveal controller and the PoP controller (and any future controller) all fit the same signature without the registrar depending on any specific controller interface.
event ControllerAdded(IDotnsController indexed controller);
ControllerRemoved
Emitted when a controller is removed.
event ControllerRemoved(IDotnsController indexed controller);
Errors
NameNotAvailable
Thrown when a name is already registered.
error NameNotAvailable(uint256 tokenId);
NotController
Thrown when the caller is not an authorised controller.
error NotController(address caller);
EscrowNotConfigured
Thrown when the protocol registry has no escrow address configured.
error EscrowNotConfigured();
TransferFeeRequired
Thrown when a standard ERC721 transfer is attempted but the recipient
tier requires a non-zero transfer fee and the caller forwarded no msg.value.
error TransferFeeRequired(uint256 tokenId, address to, uint256 requiredFee);
ProtocolRegistryRequired
Thrown when @custom:function initialize is called with the zero address as the protocol registry.
error ProtocolRegistryRequired();
UnexpectedValue
Thrown when a mint, burn, or self-transfer carries msg.value. None of those
paths forward value onward, so attached value would be permanently trapped.
error UnexpectedValue();
InvalidOwner
Thrown when @custom:function register is called with the escrow address as
owner, which would mint directly into escrow custody with no @custom:struct
ReleasePosition recorded.
error InvalidOwner();
InvalidLabel
Thrown when @custom:function register receives an empty or non-canonical label.
error InvalidLabel();
NameSoulbound
Thrown when a transfer or a transfer-fee quote targets a soulbound name.
Soulbound names are minted through the PoP gateway and are permanently
non-transferable. Raised by the _update transfer gate and by
Note: function: quoteTransferFee.
error NameSoulbound(uint256 tokenId);