- Configure registration with the Owner SDK.
- Build the signup flow with the Holder SDK.
- Recover interrupted requests before retrying.
- Add app approval only if your membership rules require it.
Configure registration
Claiming starts disabled. The current on-chain namespace owner signs the configuration before users can claim. A console team role alone does not grant that authority. Choose the following settings:
The receiving wallet may differ from the namespace owner’s wallet. In Public mode, ordinary owner
issue and issueBatch calls are blocked even while claims are paused. Reserved assignment is the explicit exception.
Where username fees go
The Registrar stores the fee and receiving wallet on chain. Show the exact amount before asking the user to authorize it. One successful claim transaction:- Transfers the username fee from the claimant directly to the configured treasury.
- Issues the name to the claimant.
- Stores the user’s complete receiving destination and the operation receipt.
Username price and network costs
Free registration means a zero username fee. Contract execution and storage rent still cost XLM, and initial storage rent can be higher than later actions. Review the current transaction estimate. Owner 0.12.0 and Holder 0.10.0 default to a 5 XLM network-fee limit per transaction, including storage restoration. This limit is separate from the namespace’s username price; it is not the amount charged. An estimate above the limit is rejected before wallet signing. Applications can explicitly setmaxNetworkFeeStroops after their user reviews the estimate. Native operations
also respect maxNativeFeeStroops; when both are set, the lower limit applies.
See write-client fee limits. Do not
automatically raise a limit or assume an earlier estimate remains valid.
The owner can update the fee or treasury with another on-chain settings transaction.
A pending claim using older settings cannot silently charge a different price.
Reconcile the original receipt and transaction before preparing a replacement.
If the original did not apply and can no longer apply, create a new request ID
and obtain the user’s authorization for the changed terms.
Username prices are separate from the namespace application fee and refund rules.
Open or restricted registration
App approval is automatic backend admission. The human namespace owner does not sign every signup.
App-approved registration requires a dedicated approval account and explicit lifetime and allowance limits. The total allowance is cumulative; raising it requires the owner’s authorization. Key rotation, mode changes and ownership transfer do not reset usage or the active rate-limit window.
A wallet quota does not prove one private app account or one person.
The app determines whether its account qualifies; the contract checks its approval
and the claim rules. Membership is checked when the approval is issued. Logout,
account suspension and unlinking a wallet do not recall an already signed approval
or undo a confirmed claim. Ordinary app access remains governed by the app’s login
and account rules.
Reserve usernames
- Reserve available labels with
reserveNames, before or after opening registration. A reserved, unassigned label has no holder and does not resolve. - Assign a reserved name with
assignReserved. You do not need to release it first. This owner-authorized action bypasses ordinary username fees and claim quotas; network costs apply. - Use
releaseReservationswhen you want a label to follow normal claim availability again. Assignment alone leaves its reservation in place.
User signup and receiving details
- The user authenticates with your app and deliberately links a supported wallet.
- Your app checks the namespace’s current claim capability, rules and availability.
- The user chooses a name and receiving details and reviews any fee.
- The user authorizes the transaction. The contract enforces the selected rules.
- Your app verifies the confirmed receipt and current holder before associating the name with the existing app account.
contract_claim_version() == 1; Holder’s contractWallet adapter supplies their authorization while a separate G signer submits. This requires a compatible wallet integration and does not extend the browser preparation routes automatically. See write-client configuration.
Signing in again does not claim another name or charge again. An existing holder
can link their name without making another paid claim. Keep a stable internal app
account ID: transferring a username must never give its new holder access to the
previous holder’s private account or data.
Confirmation and interruption
Persist the original public operation reference before submission. After a timeout or wallet interruption, recover that operation’s result before preparing another claim. A completed receipt describes historical success; separately verify current ownership, because the name may since have transferred or been reissued. A quote does not reserve a name. The first valid claim confirmed on chain wins. An unavailable or archived read is not proof that a name is free. Pausing new registration or an approval-service outage does not remove existing holders’ permitted contract operations.Owner SDK example
Install the matching packages:acme must be one your operator wallet owns.
feeAmount is the owner-selected number of XLM base units; one XLM is
10_000_000 base units. Use bigint and exact decimal conversion. Read back
owner.claimPolicy("acme") to display the current settings. Each settings action
is a separate transaction; preserve its hash and reconcile an uncertain result
before repeating it.
owner.pauseClaims(namespace) pauses new public claims without revoking issued names. The owner can also switch back to Manual mode; Public allocation is not an immutable fairness guarantee.
Reservation batches contain 1–23 distinct canonical labels and succeed or fail as a group.
assignReserved(namespace, label, holderG) uses the holder’s default receiving address. A custom destination needs that holder’s consent: the SDK’s simple method leaves a later setPayment to the holder. The contract API also exposes an atomic assignment with both parties’ authorization.
Claim from your application
For open registration, no approval service is needed. Keep the selected intent unchanged while the wallet reviews and submits it.status: "replayed" and
transaction: null; it sends no second claim transaction.
To use different receiving instructions, change the destination passed to
createClaimIntent before review:
Use the recipient’s actual route. An M address contains a base G account and a
64-bit ID; that ID is not a transaction memo. In this G-account example, the name holder and username-fee payer remain
the user’s G account even when the receiving destination differs.
Recover before replacing a request
What recovery verifies
Holder 0.10.0 checks Registrar attestation and deployed code after each receipt read or transaction confirmation. It requires RPC ledger context at least as recent as the result, including for anull receipt.
Missing, older or mismatched context stops recovery. Retain the original request and transaction hash. These checks depend on your configured RPC reporting state and ledgers honestly.
Later owner, policy or Resolver changes alone do not erase historical receipts. An upgraded or tainted Registrar cannot provide trusted receipt history.
A null receipt does not prove failure. The original transaction may still be
pending. A successful receipt does not prove current ownership. Read the current
holder, generation and destination through Universal Lookup before linking the
name to a private app account.
Changing a fee, destination, holder, business deadline or epoch creates a different
intent and requires a new request ID after reconciliation. Refreshing only the
native authorization credential’s nonce/signature for the identical still-valid
intent keeps the same business request ID.
Add app approval only when needed
For restricted signup, use a dedicated G eligibility account. Keep it separate from the namespace owner, treasury and claimant. The backend authenticates the app account and wallet link, then signs the exact claim intent usingsignClaimEligibility. Its trusted configuration, deployment anchors and expiry
bounds must come from independent chain reads, not the applicant’s request.
A server-side integration can keep account checks separate from contract signing:
holder.buildClaim(intent) to obtain the unsigned admission
entry without signing or submitting a transaction. After the backend supplies the
scoped native authorization entry, pass it as
holder.claim(intent, { eligibilityAuthorization, onPrepared }). The user’s wallet
still authorizes the transaction. The approver entry contains only the exact
Registrar claim intent and no fee-transfer authorization.
Bound the approval service
Configure all four limits explicitly; the SDK does not choose them for you:
The SDK also limits approval credentials to 60 future ledgers. A compromised approver can admit attacker-controlled wallets within those budgets. Its admission signature cannot debit another user’s wallet, change an existing holder’s payment route or undo a completed claim.
For a stable invitation cohort, use
buildClaimAllowlist and publish its complete
root through configureClaims. Keep the complete wallet list and distribute each
wallet’s proof. Rebuilding a root from a smaller subset replaces the invitation
list; it does not append to it. Frontend login alone never restricts an Open
contract policy.
Existing holders after signup
Payment updates and permitted transfers do not need the app approval service.acceptNameTransferWithDestination lets a recipient accept and initialize their
chosen receiving details in one transaction, preserving the name’s expiry.
Finite-term holders can renew their name without a separate username renewal fee. Renewal is independent of claim pause and app approval, but still follows the immutable term, generation and lease limits. Reservation does not prevent renewal of a live name or one still protected by applicable grace.
The owner can configure up to 90 days of renewal grace; it defaults to zero and applies only to expiries covered by the current activation cutoff. Grace prevents ordinary reissue but does not keep an expired name resolving. After grace, renewal can still succeed before reissue, except for a now-reissuable label the owner has reserved. See the exact renewal rules. renewalPreview shows the stored destination that renewal will reactivate. Zero-term names cannot renew, and policy-based owner reclaim rights remain.
See the native contract API and
native claim storage reference for exact types,
authorization, receipt and lifecycle boundaries.