Skip to main content

registrationUsername

Centralized Verification

This function uses Core.sol's validateAndConsumeNonce() for signature verification. Completes the commit-reveal scheme started by preRegistrationUsername.

Function Type: external
Function Signature: registrationUsername(address,string,uint256,address,uint256,bytes,uint256,uint256,bytes)

Completes username registration by revealing the username and lock number used in pre-registration. This function validates the reveal against the stored commitment, processes the registration fee payment, and grants 366 days of ownership.

Requirements:

  • Valid pre-registration with matching hash must exist
  • Pre-registration must belong to the same user
  • Pre-registration must not be expired (within 30-minute window)
  • Username must be available and valid format
  • User must pay registration fee (100x EVVM reward or market-based)

Parameters

ParameterTypeDescription
useraddressThe address of the registrant (must match pre-registration address).
usernamestringThe desired username being registered (revealed from commit phase).
lockNumberuint256The secret number used during pre-registration. Proves ownership of commitment.
originExecutoraddressOptional tx.origin restriction (use address(0) for unrestricted).
nonceuint256User's centralized Core nonce for this operation.
signaturebytesEIP-191 signature authorizing this operation (verified by Core.sol).
priorityFeeEvvmuint256Optional fee paid to executor, added to registration fee payment.
nonceEvvmuint256User's Core nonce for the payment operation (registration fee + priority fee).
signatureEvvmbytesUser's signature authorizing the payment (registration fee + priority fee).
Signature Requirements

NameService Signature (signature):

Payment Signature (signatureEvvm):

  • Covers total payment: getPriceOfRegistration(username) + priorityFeeEvvm
  • Uses CoreHashUtils.hashDataForPay()
  • Reference: Payment Signature Structure

Execution Flow

1. Signature Verification (Core.sol)

core.validateAndConsumeNonce(
user,
Hash.hashDataForRegistrationUsername(username, lockNumber),
originExecutor,
nonce,
true, // Async
signature
);

What Core.sol validates:

  • ✅ Signature matches user address
  • ✅ Nonce is valid and available
  • tx.origin matches originExecutor (if specified)
  • ✅ Marks nonce as consumed

2. Username Validation

// Admin bypass validation
if (admin.current != user && !IdentityValidation.isValidUsername(username)) {
revert Error.InvalidUsername();
}

if (!isUsernameAvailable(username)) {
revert Error.UsernameAlreadyRegistered();
}

Validation Rules:

  • Alphanumeric characters only (a-z, 0-9, underscore)
  • Length: 3-32 characters
  • No leading/trailing underscores
  • Not already registered (admin can override)

3. Registration Fee Payment

uint256 registrationCost = getPriceOfRegistration(username);

requestPay(
user,
registrationCost,
priorityFeeEvvm,
nonceEvvm,
signatureEvvm
);

Internally calls:

core.pay(
user, // Payer
address(this), // NameService receives
"", // No identity
principalToken, // PT
registrationCost + priorityFeeEvvm, // Total
priorityFeeEvvm, // Priority fee
address(this), // Executor
nonceEvvm, // Payment nonce
true, // Async
signatureEvvm // Payment sig
);

Registration Cost Calculation:

function getPriceOfRegistration(string memory username) public view returns (uint256) {
return identityDetails[username].offerMaxSlots > 0
? seePriceToRenew(username) // Market-based (if offers exist)
: core.getRewardAmount() * 100; // Standard rate
}

4. Pre-Registration Validation

string memory key = string.concat(
"@",
AdvancedStrings.bytes32ToString(hashUsername(username, lockNumber))
);

// Validate commitment
if (
identityDetails[key].owner != user ||
identityDetails[key].expirationDate > block.timestamp
) {
revert Error.PreRegistrationNotValid();
}

Validates:

  • ✅ Pre-registration exists
  • ✅ Matches same user address
  • ✅ Waiting period has passed (30 minutes)
  • ✅ Not expired

5. Username Registration

identityDetails[username] = IdentityBaseMetadata({
owner: user,
expirationDate: block.timestamp + 366 days,
customMetadataMaxSlots: 0,
offerMaxSlots: 0,
flagNotAUsername: 0x00 // Marked as username
});

Grants:

  • 366 days of ownership
  • Metadata capabilities
  • Marketplace participation

6. Staker Rewards (if executor is staker)

if (core.isAddressStaker(msg.sender)) {
makeCaPay(msg.sender, (50 * core.getRewardAmount()) + priorityFeeEvvm);
}

Reward Breakdown:

  • Base reward: 50x core.getRewardAmount()
  • Priority fee: Full priorityFeeEvvm amount
  • Highest NameService reward (reflects registration importance)

7. Cleanup

delete identityDetails[key];  // Remove pre-registration

Frees storage and gas refund.

Complete Example

Scenario: Complete registration of "alice" after pre-registration

Step 1: Recall Pre-Registration Details

string memory username = "alice";
uint256 lockNumber = 987654321; // Used in pre-registration

// Verify commitment matches
bytes32 expectedHash = keccak256(abi.encodePacked(username, lockNumber));
// Must match pre-registration hash

Step 2: Calculate Registration Cost

uint256 registrationCost = nameService.getPriceOfRegistration("alice");
// If no offers: 100x reward (e.g., 100 PT)
// If offers exist: Market-based pricing

Step 3: Generate Signatures

// Operation signature
bytes32 hashPayload = NameServiceHashUtils.hashDataForRegistrationUsername(
username,
lockNumber
);

// Payment signature (covers total)
bytes32 paymentHash = CoreHashUtils.hashDataForPay(
nameServiceAddress, // Receiver
principalToken,
registrationCost + priorityFee,
priorityFee
);

Step 4: Submit Registration

nameService.registrationUsername(
user, // 0x742d...
"alice", // Revealed username
987654321, // Revealed lock number
address(0), // Unrestricted
nonce, // 43
signature, // Operation sig
1000000000000000000, // 1 PT priority fee
nonceEvvm, // 44
signatureEvvm // Payment sig (covers 101 PT)
);

Step 5: Username Registered

// Now available for use
address owner = nameService.getOwnerOfIdentity("alice"); // user address
uint256 expires = nameService.getExpireDateOfIdentity("alice"); // now + 366 days

Pricing Examples

Standard Rate (No Offers)

// No existing offers on username
registrationCost = 100 * core.getRewardAmount();
// Example: 100 * 1 PT = 100 PT

Market-Based (With Offers)

// Username has offers, uses seePriceToRenew()
// Price based on:
// - Time to expiration
// - Offer amounts
// - Market demand
registrationCost = seePriceToRenew("alice");
// Example: 150 PT (higher demand)

Important Notes

Time Constraints

  • Minimum wait: 30 minutes after pre-registration
  • Maximum wait: No hard limit, but pre-registration expires eventually
  • Best practice: Register within 24 hours of pre-registration

Lock Number Security

// NEVER share lock number before registration
lockNumber = 987654321; // Keep this secret!

// After registration, lock number can be public
// (but no harm in keeping it private)

Username Validation

// Valid usernames
"alice" ✅ // lowercase letters
"alice123" ✅ // letters + numbers
"alice_bob" ✅ // underscores allowed

// Invalid usernames
"Alice" ❌ // uppercase not allowed
"alice-bob" ❌ // hyphens not allowed
"al" ❌ // too short (< 3 chars)
"_alice" ❌ // leading underscore
"alice_" ❌ // trailing underscore

Admin Override

// Admin can bypass validation
if (admin.current == user) {
// Skip validation
// Allows admin to register any format
}

Gas Costs

Estimated Gas Usage:

  • Base operation: ~100,000 gas
  • Core verification: ~5,000 gas
  • Username validation: ~5,000 gas
  • Payment processing: ~40,000 gas
  • Storage (username): ~40,000 gas
  • Cleanup (pre-reg): ~5,000 gas (refund)
  • Total: ~195,000 gas

Actual cost varies with:

  • Username length
  • Market pricing calculations
  • Staker reward distribution

Error Handling

Common revert reasons:

// From Core.sol
"Invalid signature" // Operation signature failed
"Nonce already used" // Nonce consumed
"Invalid executor" // tx.origin mismatch

// From validation
"InvalidUsername" // Format validation failed
"UsernameAlreadyRegistered" // Username taken

// From pre-registration check
"PreRegistrationNotValid" // Commitment doesn't match
// OR wrong owner
// OR expired

// From payment
"Insufficient balance" // User lacks PT
"Payment signature invalid" // Payment sig failed