Skip to content

feat: price dotNS names on a scarcity curve - #208

Open
sphamjoli wants to merge 8 commits into
masterfrom
spha/scarcity-pricing
Open

feat: price dotNS names on a scarcity curve#208
sphamjoli wants to merge 8 commits into
masterfrom
spha/scarcity-pricing

Conversation

@sphamjoli

@sphamjoli sphamjoli commented Aug 7, 2026

Copy link
Copy Markdown
Member

Description

Prices every name by its length on a single geometric curve instead of a flat fee. A name's price doubles for each character below nine and flattens to the base fee from nine upward, so short, scarce names cost more. A trailing number never changes the price.

Three bands share the curve. Names of nine characters or more are open to anyone at the base fee. Names of six to eight characters are premium, and only a verified person may register them, paying the curve for the length. Names of five characters or fewer stay reserved to governance.

Personhood buys access to the premium band: a verified person pays the same curve as anyone else, with no discount inside it. Each wallet gets one free name from the gateway, of any length, on the unpriced lane. Moving a name re-prices it at its own length, so a scarce name cannot be handed cheaply to a party who could never have registered it.

Registering under your own key locks a refundable deposit on the curve. Fees, paid when someone else pays for a name or on a transfer, are non-refundable and gather in one protocol fee pot that governance can send to a burn or the treasury. The pot no longer backs refunds; a refund draws only on its own reserve.

The formula

$$ \text{price}(n) = \begin{cases} D \cdot 2^{,9-n} & 1 \le n \le 8 \ D & n \ge 9 \end{cases} $$

$n$ is the number of letters in the name, its base length: the total length minus a trailing two-digit number, which does not change the price. $D$ is the base fee.

Below nine letters, dropping one letter doubles the price, because a name one letter shorter is rarer. At nine letters and above the price is flat at $D$, because names that long are plentiful. The exponent $9 - n$ is how many doublings you are above the base fee, so a one-letter name sits eight doublings, $2^8 = 256$ times $D$, above it.

Every length, at the launch fee $D = 10$ DOT

Letters ($n$) Multiplier Price Band
9 or more $2^{0}=1$ 10 DOT open to anyone
8 $2^{1}=2$ 20 DOT premium, verified only
7 $2^{2}=4$ 40 DOT premium, verified only
6 $2^{3}=8$ 80 DOT premium, verified only
5 $2^{4}=16$ 160 DOT reserved to governance
4 $2^{5}=32$ 320 DOT reserved to governance
3 $2^{6}=64$ 640 DOT reserved to governance
2 $2^{7}=128$ 1,280 DOT reserved to governance
1 $2^{8}=256$ 2,560 DOT reserved to governance

In practice:

  • A wallet claims its one free name through the gateway, any length: it pays nothing.
  • A second name on that wallet goes on the public curve: a six-character name is 80 DOT.
  • Someone with no personhood registers a ten-character name: 10 DOT, held as a refundable deposit they get back on release.
  • That person tries for a six-character name: rejected, because the premium band is gated on personhood.
  • A holder passes a six-character name to a wallet with no personhood: the move re-prices at 80 DOT, the name's own price, not the ten-DOT floor.

Type

  • Bug fix
  • Feature
  • Breaking change
  • Documentation
  • Chore
  • Refactor
  • Security

Scope

  • Registration
  • Resolver
  • Store
  • Proof of Personhood
  • Deployment scripts
  • Tests

Related Issues

None.

Fixes

None.

Checklist

Code

  • Follows project style
  • forge build passes
  • forge test passes
  • No new compiler warnings

Testing

  • New tests added for changed behavior
  • Fuzz tests added where applicable
  • Invariant tests verified

Security

  • No new selfdestruct or delegatecall
  • Access control reviewed
  • No storage layout conflicts (for upgradeable contracts)

Documentation

  • NatSpec updated on changed interfaces
  • README updated if needed

Breaking Changes

  • No breaking changes
  • Breaking changes documented below

Breaking changes: the escrow fee pot is renamed from the insurance fund to protocol fees. The insuranceFund() view becomes protocolFees(), depositInsurance becomes depositProtocolFee, and the withdrawal shortfall event is removed. Consumers reading the fee balance must move to the new names.

How to test

forge test --mt test_price_follows_scarcity_curve

Notes

The fee pot's governance routing to a burn or treasury, the one-free-name-per-person cap, and a bounded band on the base fee are the target model described in the README and land in follow-up work.

Replace flat pricing with a geometric curve on base length: the price
doubles for each character below nine and flattens to the base fee from
nine upward. Every caller pays the curve; personhood gates access to the
premium band rather than discounting it. Transfers re-price at the name's
own length.

Rename the escrow insurance fund to protocol fees, a non-refundable pot
that only accumulates, and remove the dead refund-shortfall draw so
refunds draw solely on the per-asset reserve.
@sphamjoli
sphamjoli requested a review from waytwotall as a code owner August 7, 2026 05:43
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

CI Summary

Check Result
4naly3er Analysis Found 41 issues: 5 medium, 10 low, 15 gas, 11 informational - View Report
Slither Analysis Found 155 issues: 3 high, 38 medium, 54 low, 60 informational - View Report
Contract Tests (Unit + Fuzz) All tests passed (435 total) - View Report
Contract Tests (Invariant) All tests passed (39 total) - View Report
Gas Report 9 contracts analyzed - View Report
Coverage 70.2% line coverage - View Report
Documentation Passed - 57 pages generated - View Docs
Format & Lint Passed - Code formatted correctly
Deploy Contracts Reproduces DEPLOYMENTS.md; resume verified
PR Title PR Title Valid
Labels Unknown
Secret Scan Passed - No secrets detected

4naly3er Analysis

Medium (5)

ID Finding Instances
M-1 block.number means different things on different L2s 5
M-2 Centralization Risk for trusted owners 24
M-3 _safeMint() should be used rather than _mint() wherever possible 1
M-4 Using transferFrom on ERC721 tokens 1
M-5 Direct supportsInterface() calls may cause caller to revert 9

Low (10)

ID Finding Instances
L-1 Use a 2-step ownership transfer pattern 3
L-2 External call recipient may consume all transaction gas 10
L-3 Fallback lacking payable 1
L-4 Initializers could be front-run 40
L-5 Signature use at deadlines should be allowed 5
L-6 Use Ownable2Step.transferOwnership instead of `Ownable.transferOwner 1
L-7 Unsafe ERC20 operation(s) 1
L-8 Unsafe solidity low-level call can cause gas grief attack 1
L-9 Upgradeable contract is missing a __gap[50] storage variable to allo 108
L-10 Upgradeable contract not initialized 163

Gas (15)

ID Finding Instances
GAS-1 Use ERC721A instead ERC721 1
GAS-2 Don't use _msgSender() if not supporting EIP-2771 1
GAS-3 a = a + b is more gas effective than a += b for state variables (e 13
GAS-4 Using bools for storage incurs overhead 3
GAS-5 Cache array length outside of loop 3
GAS-6 For Operations that will not overflow, you could use unchecked 292
GAS-7 Use Custom Errors instead of Revert Strings to save Gas 7
GAS-8 Avoid contract existence checks by using low level calls 1
GAS-9 Functions guaranteed to revert when called by normal users can be mark 46
GAS-10 ++i costs less gas compared to i++ or i += 1 (same for --i vs 9
GAS-11 Using private rather than public for constants, saves gas 7
GAS-12 Use shift right/left instead of division/multiplication if possible 1
GAS-13 Splitting require() statements that use && saves gas 6
GAS-14 Increments/decrements can be unchecked in for-loops 26
GAS-15 Use != 0 instead of > 0 for unsigned integer comparison 17

Informational (11)

ID Finding Instances
NC-1 constants should be defined rather than using magic numbers 21
NC-2 Control structures do not follow the Solidity Style Guide 95
NC-3 Critical Changes Should Use Two-step Procedure 3
NC-4 Dangerous while(true) loop 1
NC-5 Consider disabling renounceOwnership() 2
NC-6 Functions should not be longer than 50 lines 333
NC-7 Use a modifier instead of a require/if statement for a special `ms 19
NC-8 addresss shouldn't be hard-coded 1
NC-9 Take advantage of Custom Error's return value property 1
NC-10 Avoid the use of sensitive terms 21
NC-11 Variables need not be initialized to zero 13

View full report | View logs

Slither Analysis

High (3)

Check Description Location
arbitrary-send-eth Multicall3.aggregate3Value(Multicall3.Call3Value[]) (contracts/utils/Multicall3. contracts/utils/Multicall3.sol:157
arbitrary-send-eth DotnsRegistrarController._settleEscrow(address,uint256,address,bool,uint256) (co contracts/registrars/DotnsRegistrarController.sol:248
uninitialized-state DotnsNameEscrow._entriesByRecipient (contracts/escrow/DotnsNameEscrow.sol#84) is contracts/escrow/DotnsNameEscrow.sol:84

Medium (38)

Check Description Location
incorrect-equality DotnsRegistrarController.commit(bytes32) (contracts/registrars/DotnsRegistrarCon contracts/registrars/DotnsRegistrarController.sol:139
reentrancy-no-eth Reentrancy in DotnsPopController.reserveBaseName(IDotnsPopController.BaseReserva contracts/registrars/DotnsPopController.sol:213
reentrancy-no-eth Reentrancy in DotnsPopController._releasePopRulesSlot(bytes32) (contracts/regist contracts/registrars/DotnsPopController.sol:862
reentrancy-no-eth Reentrancy in DotnsPopController.registerBaseName(IDotnsPopController.FullRegist contracts/registrars/DotnsPopController.sol:299
reentrancy-no-eth Reentrancy in DotnsPopController.registerBaseName(IDotnsPopController.FullRegist contracts/registrars/DotnsPopController.sol:299
reentrancy-no-eth Reentrancy in DotnsPopController._claimLabelStoreFor(address) (contracts/registr contracts/registrars/DotnsPopController.sol:400
reentrancy-no-eth Reentrancy in DotnsPopController.reserveBaseNameOnly(IDotnsPopController.BaseNam contracts/registrars/DotnsPopController.sol:242
reentrancy-no-eth Reentrancy in DotnsPopController.reserveBaseName(IDotnsPopController.BaseReserva contracts/registrars/DotnsPopController.sol:213
reentrancy-no-eth Reentrancy in DotnsPopController.reserveBaseNameOnly(IDotnsPopController.BaseNam contracts/registrars/DotnsPopController.sol:242
uninitialized-local StringUtils.stripDots(string).outputIndex (contracts/utils/StringUtils.sol#91) i contracts/utils/StringUtils.sol:91
+28 more

Low (54)

Check Description Location
shadowing-local IDotnsPopResolver.setChatKey(bytes32,bytes).chatKey (contracts/resolvers/IDotnsP contracts/resolvers/IDotnsPopResolver.sol:53
shadowing-local IDotnsRegistrarController.isWhiteListed(address).isWhiteListed (contracts/regist contracts/registrars/IDotnsRegistrarController.sol:184
shadowing-local IDotnsPopResolver.chatKey(bytes32).chatKey (contracts/resolvers/IDotnsPopResolve contracts/resolvers/IDotnsPopResolver.sol:71
events-maths DotnsRegistrarController.initialize(IDotnsProtocolRegistry,uint256,uint256) (con contracts/registrars/DotnsRegistrarController.sol:95
missing-zero-check RootGatewayDispatcher.constructor(address).target_ (contracts/registrars/RootGat contracts/registrars/RootGatewayDispatcher.sol:51
calls-loop Multicall3.aggregate3Value(Multicall3.Call3Value[]) (contracts/utils/Multicall3. contracts/utils/Multicall3.sol:157
calls-loop DotnsPopController._settlePendingLabel(IStoreFactory,address,address,string) (co contracts/registrars/DotnsPopController.sol:423
calls-loop DotnsPopController._writeRecord(address,bytes32,string) (contracts/registrars/Do contracts/registrars/DotnsPopController.sol:652
calls-loop DotnsPopController._writeRecord(address,bytes32,string) (contracts/registrars/Do contracts/registrars/DotnsPopController.sol:652
calls-loop Multicall3.tryAggregate(bool,Multicall3.Call[]) (contracts/utils/Multicall3.sol# contracts/utils/Multicall3.sol:67
+44 more

Informational (60)

Check Description Location
assembly LabelUtils.namehashUnder(bytes32,bytes32) (contracts/utils/LabelUtils.sol#52-63) contracts/utils/LabelUtils.sol:52
assembly RootGatewayDispatcher.fallback() (contracts/registrars/RootGatewayDispatcher.sol contracts/registrars/RootGatewayDispatcher.sol:64
assembly LabelUtils.labelhashMemory(string) (contracts/utils/LabelUtils.sol#40-44) uses a contracts/utils/LabelUtils.sol:40
assembly DotnsPopController._dispatchTyped(bytes4,bytes) (contracts/registrars/DotnsPopCo contracts/registrars/DotnsPopController.sol:894
assembly Multicall3.aggregate3(Multicall3.Call3[]) (contracts/utils/Multicall3.sol#126-15 contracts/utils/Multicall3.sol:126
assembly LabelUtils.labelhash(string) (contracts/utils/LabelUtils.sol#25-32) uses assembl contracts/utils/LabelUtils.sol:25
assembly Multicall3.aggregate3Value(Multicall3.Call3Value[]) (contracts/utils/Multicall3. contracts/utils/Multicall3.sol:157
assembly DotnsRegistry._parentNamehash(string) (contracts/registry/DotnsRegistry.sol#217- contracts/registry/DotnsRegistry.sol:217
costly-loop DotnsNameEscrow._removeRefundEntry(uint256,address) (contracts/escrow/DotnsNameE contracts/escrow/DotnsNameEscrow.sol:553
costly-loop DotnsNameEscrow._removeRefundEntry(uint256,address) (contracts/escrow/DotnsNameE contracts/escrow/DotnsNameEscrow.sol:553
+50 more

View full report | View logs

Contract Tests (Unit + Fuzz)

BasicDotnsIntegrationReverts (test/intergration/BasicDotns.reverts.t.sol)

Test Result Error
test_parent_can_reassign_existing_subdomain PASS
test_revert_non_owner_cannot_create_subdomain_under_someone_elses_name PASS
test_revert_poplite_cannot_register_popfull_required PASS
test_revert_unapproved_cannot_set_contenthash PASS

BasicDotnsIntegration (test/intergration/BasicDotns.t.sol)

Test Result Error
test_nostatus_end_to_end PASS
test_popfull_end_to_end PASS
test_poplite_end_to_end PASS
test_third_party_reserved_registration_preserves_existing_reverse PASS

DeployCreate3FactoryTest (test/unit/deploy/DeployCreate3Factory.t.sol)

Test Result Error
test_revertsWhenDeployerNonceNotZero PASS

DeterministicDeploymentTest (test/unit/deploy/DeterministicDeployment.t.sol)

Test Result Error
test_addressesIdenticalAcrossDeployers PASS
test_addressesStableAcrossSequentialRuns PASS
test_adoptRevertsWhenFactoryHasNoCode PASS
test_coreDeploymentAddressesStayTheSameAcrossChainIds PASS
test_create3FactoryResolvesFromProtocolRegistry PASS
test_ensureReusesConfiguredFactory PASS
test_predictionsMatchCreate3Deployments PASS
test_predictionsMatchForNonProxyDeploys PASS
test_reDeployAdoptsAnExistingContract PASS
test_reDeployAdoptsProxyWithoutReinitialising PASS
test_reusedFactoryMakesAddressesDeployerIndependent PASS

DotnsContentResolverTests (test/unit/resolver/DotnsContentResolver.t.sol)

Test Result Error
test_operator_can_modify_records PASS
test_set_contenthash PASS
test_set_text PASS

DotnsNameEscrowTest (test/unit/escrow/DotnsNameEscrow.t.sol)

Test Result Error
test_cross_payer_downgrade_charges_only_owner_price PASS
test_cross_payer_pays_the_curve_into_fees PASS
test_cross_payer_verified_sponsors_nostatus_pays_only_D PASS
test_deposit_records_position PASS
test_downgrade_transfer_pays_name_price PASS
test_funded_position_follows_NFT_through_multiple_transfers PASS
test_funded_position_rebinds_to_new_holder_on_transfer PASS
test_reclaim_transfers_custody_to_new_owner PASS
test_release_and_withdraw_subject_to_cooldown_after_transfer PASS
test_release_transfers_token_to_escrow PASS
test_released_tokens_pagination PASS
test_revert_deposit_already_funded PASS
test_revert_deposit_not_controller PASS
test_revert_double_release PASS
test_revert_ghost_nft_transfer_into_escrow PASS
test_revert_reclaim_before_withdraw PASS
test_revert_reclaim_not_controller PASS
test_revert_release_escrow_not_approved PASS
test_revert_release_not_holder PASS
test_revert_withdraw_before_cooldown PASS
test_revert_withdraw_not_recipient PASS
test_same_tier_NoStatus_transfer_rebinds_position_to_new_holder PASS
test_self_registration_seeds_funded_position PASS
test_solvency_after_force_sent_funds PASS
test_transfer_charges_friction_and_rebinds_position PASS
test_transfer_no_rebind_when_to_equals_position_recipient PASS
test_update_cooldown PASS
test_withdraw_sends_refund_after_cooldown PASS

DotnsNameEscrowFuzzTest (test/fuzz/escrow/DotnsNameEscrowFuzz.t.sol)

Test Result Error
testFuzz_deposit_amount PASS
testFuzz_withdraw_timing PASS

DotnsNameEscrowRefundsTest (test/unit/escrow/DotnsNameEscrowRefunds.t.sol)

Test Result Error
test_claimRefund_emitsRefundClaimed PASS
test_claimRefund_revertsBeforeCooldown PASS
test_claimRefund_revertsOnTransferFailure PASS
test_claimRefund_revertsWhenCallerIsNotRecipient PASS
test_claimRefund_transfersAndDeletes PASS
test_claimRefundsBatch_aggregatesAndTransfers PASS
test_claimRefundsBatch_atomicOnLockedEntry PASS
test_claimRefundsBatch_revertsOnEmpty PASS
test_creditRefund_allocatesMonotonicEntryIds PASS
test_creditRefund_emitsRefundCredited PASS
test_creditRefund_independentCooldowns PASS
test_creditRefund_revertsOnZeroAmount PASS
test_creditRefund_revertsOnZeroRecipient PASS
test_pendingRefundIds_paginates PASS
test_removeRefundEntry_swapPopMiddlePreservesIndices PASS

DotnsPopControllerTests (test/unit/registrar/DotnsPopController.t.sol)

Test Result Error
testFuzz_bytes_overloads_reject_non_root_origin PASS
test_advanceExpiredHead_last_expire_releases_popRules_slot PASS
test_advanceExpiredHead_promotes_waiter_and_resyncs_popRules PASS
test_both_controllers_can_mint_on_shared_registrar PASS
test_claimLabelStoreFor_reverts_for_non_gateway PASS
test_claimLabelStore_at_exact_expiry_boundary_belongs_to_user PASS
test_claimLabelStore_deploys_store_and_writes_label_and_chat_key PASS
test_claimLabelStore_emits_settled_and_name_registered PASS
test_claimLabelStore_msg_sender_keyed_other_users_stash_untouched PASS
test_claimLabelStore_with_empty_chat_key_skips_resolver_write PASS
test_claim_releases_popRules_slot PASS
test_claim_then_reEnqueue_on_same_stem_resets_cleanly PASS
test_controller_authorised_but_not_gateway_cannot_enter_pop_flow PASS
test_enqueueReservation_same_user_second_call_replaces_first PASS
test_enqueue_becomesHead_writes_popRules_reservation PASS
test_entry_point_format_rejections PASS
test_expirePendingClaim_clears_entry_after_reservation_duration PASS
test_expireReservation_is_permissionless PASS
test_expireReservation_on_empty_queue_is_noop PASS
test_gatewayReserve_pending_claim_lapses_after_minimum_duration PASS
test_gatewayReserve_stashes_pending_claim_when_user_has_no_label_store PASS
test_gatewayReserve_warm_user_after_claim_writes_directly_without_stashing PASS
test_gateway_can_settle_label_store_for_user PASS
test_gateway_reserved_name_allows_holder_to_register_via_public PASS
test_gateway_reserved_name_rejects_public_register_by_other_user PASS
test_head_expires_clears_slot_for_next_reserver PASS
test_multiWaiter_standaloneGuard_rejects_non_head_user PASS
test_non_owner_cannot_create_subname_under_pop_minted_name PASS
test_owner_of_pop_minted_name_can_create_subname PASS
test_pendingClaimUsers_enumeration_mirrors_stash_and_settle PASS
test_pendingClaimUsers_pagination_boundary_cases PASS
test_pendingClaimUsers_returns_empty_when_offset_past_count PASS
test_pendingClaims_returns_empty_array_for_fresh_user PASS
test_pop_reservation_of_already_public_minted_name_fails_on_claim PASS
test_public_register_after_pop_full_mint_reverts_at_registrar PASS
test_public_stranger_can_mint_after_claim_clears_reservation PASS
test_public_stranger_can_mint_after_reservation_expires PASS
test_reEnqueue_after_own_expiry_promotes_same_user_to_head PASS
test_registerBaseName_claim_by_store_less_full_person_piles_then_settles PASS
test_registerBaseName_claim_emits_claim_event_and_not_standalone PASS
test_registerBaseName_claim_inherits_chat_key_from_lite_node PASS
test_registerBaseName_claim_path_bypasses_standalone_holder_guard PASS
test_registerBaseName_claim_wipes_entire_queue PASS
test_registerBaseName_guard_blocks_stranger_and_preserves_claim PASS
test_registerBaseName_popFull_user_on_popFull_label_succeeds PASS
test_registerBaseName_reverts_for_governance_length_name PASS
test_registerBaseName_reverts_when_origin_is_not_root PASS
test_registerBaseName_standalone_auto_relinquishes_users_other_reservation PASS
test_registerBaseName_standalone_emits_standalone_event_and_not_claim PASS
test_registerBaseName_standalone_succeeds_when_head_is_expired PASS
test_registerBaseName_standalone_succeeds_when_queue_empty PASS
test_registerBaseName_standalone_with_lite_link_silently_relinquishes PASS
test_registerBaseName_zero_length_label_reverts PASS
test_relinquishReservation_promotes_next_waiter_when_head_leaves PASS
test_relinquishReservation_reverts_when_caller_has_no_reservation PASS
test_relinquish_last_releases_popRules_slot PASS
test_reserveBaseNameOnly_does_not_mint_lite_or_base_name PASS
test_reserveBaseNameOnly_reverts_for_non_gateway PASS
test_reserveBaseNameOnly_reverts_for_reserved_or_suffixed_labels PASS
test_reserveBaseNameOnly_same_user_can_replace_prior_reservation PASS
test_reserveBaseName_accepts_65_byte_chat_key PASS
test_reserveBaseName_enqueues_when_reserved_label_provided PASS
test_reserveBaseName_lite_and_base_legs_both_succeed_in_one_call PASS
test_reserveBaseName_mints_and_wires_registry_and_resolver PASS
test_reserveBaseName_reserved_label_classification_reverts PASS
test_reserveBaseName_reverts_for_digit_suffixed_reserved_base_label PASS
test_reserveBaseName_reverts_when_origin_is_not_root PASS
test_reserveLiteName_bytes_reverts_on_malformed_payload PASS
test_reserveLiteName_piles_second_pending_claim_when_caller_has_no_store PASS
test_reserveLiteName_reverts_for_non_lite_format PASS
test_reserveLiteName_reverts_when_flattened_label_is_governance_reserved PASS
test_reserveLiteName_reverts_when_origin_is_not_root PASS
test_reserveLiteName_reverts_when_suffix_is_not_exactly_two_digits PASS
test_reserveLiteName_succeeds_for_long_stem PASS
test_reserveLiteName_succeeds_regardless_of_base_reservation PASS
test_revert_claimLabelStore_when_caller_has_no_pending_claim PASS
test_revert_claimLabelStore_when_pending_claim_has_lapsed PASS
test_revert_expirePendingClaim_when_entry_is_still_live PASS
test_revert_expirePendingClaim_when_user_has_no_pending_claim PASS
test_revert_setReservationDuration_below_minimum PASS
test_same_stem_lite_and_base_occupy_distinct_registrar_tokens PASS
test_second_pop_lite_mint_of_same_label_reverts_at_registrar PASS
test_setReservationDuration_reverts_for_non_owner PASS
test_setReservationDuration_shortening_retroactively_expires_live_entries PASS
test_split_gateway_flow_mints_lite_then_reserves_base PASS
test_user_claimLabelStore_fallback_still_settles_after_gateway_mint PASS

DotnsPopControllerFuzz (test/fuzz/registrar/DotnsPopControllerFuzz.t.sol)

Test Result Error
testFuzz_claimLabelStore_settles_label_and_chat_key_exactly PASS
testFuzz_gatewayReserve_cold_user_stashes_label_and_chat_key_exactly PASS
testFuzz_isReservedForClaim_tracks_duration_boundary PASS
testFuzz_pendingClaim_expiry_boundary_admits_or_lapses PASS
testFuzz_public_register_respects_popRules_reservation PASS
testFuzz_registerBaseName_overloads_equivalent PASS
testFuzz_reserveBaseName_accepts_any_two_digit_suffix PASS
testFuzz_reserveBaseName_overloads_equivalent PASS
testFuzz_reserveBaseName_persists_chat_key_exact_bytes PASS
testFuzz_reserveLiteName_overloads_equivalent PASS

DotnsPopResolverTests (test/unit/resolver/DotnsPopResolver.t.sol)

Test Result Error
test_rotating_pop_controller_changes_authorised_writer PASS
test_setChatKey_accepts_exactly_65_bytes PASS
test_setChatKey_accepts_zero_node_as_passthrough PASS
test_setChatKey_auth_check_precedes_length_check_on_bad_payload PASS
test_setChatKey_auth_check_runs_before_length_check PASS
test_setChatKey_reverts_for_64_byte_payload PASS
test_setChatKey_reverts_for_66_byte_payload PASS
test_setChatKey_reverts_for_empty_payload PASS
test_setChatKey_reverts_for_large_griefing_payload PASS
test_setChatKey_reverts_for_one_byte_payload PASS
test_setChatKey_reverts_for_unauthorised_caller PASS
test_setChatKey_writes_and_emits PASS
test_setLiteLink_accepts_zero_inputs_as_passthrough PASS
test_setLiteLink_chain_returns_to_original_without_drift PASS
test_setLiteLink_cross_chain_no_drift PASS
test_setLiteLink_idempotent_relink_keeps_both_indices PASS
test_setLiteLink_long_chain_invariant_holds_at_every_step PASS
test_setLiteLink_old_lite_reads_zero_after_relink PASS
test_setLiteLink_quadrangle_clears_both_stale_inverses PASS
test_setLiteLink_reverts_for_unauthorised_caller PASS
test_setLiteLink_same_full_node_relink_clears_old_reverse PASS
test_setLiteLink_same_lite_relink_clears_old_forward PASS
test_setLiteLink_with_zero_lite_is_passthrough PASS
test_setLiteLink_writes_and_emits PASS

DotnsProtocolRegistryTldTests (test/unit/registry/DotnsProtocolRegistry.t.sol)

Test Result Error
test_same_label_derives_distinct_node_and_token_id_per_tld PASS

DotnsProtocolRegistryFuzzTest (test/fuzz/registry/DotnsProtocolRegistryFuzz.t.sol)

Test Result Error
testFuzz_isRegisteredAddress_matches_ground_truth PASS
testFuzz_set_same_pair_is_no_op PASS
testFuzz_zero_address_never_registered PASS
test_initialise_reverts_on_empty_tld PASS
test_initialise_reverts_on_multi_label_tld PASS

DotnsRegistrarTests (test/unit/registrar/DotnsRegistrar.t.sol)

Test Result Error
test_add_controller PASS
test_add_controller_emits_event PASS
test_add_controller_reverts_for_non_owner PASS
test_approvals_work PASS
test_available_before_after_register PASS
test_available_when_token_held_by_escrow_returns_true PASS
test_exists_reports_minted_state PASS
test_initialize_cannot_be_called_twice PASS
test_label_of_returns_empty_for_nonexistent_token PASS
test_label_of_returns_empty_when_owner_has_no_label_store PASS
test_label_of_returns_stripped_label PASS
test_quote_transfer_fee_reverts_for_zero_recipient PASS
test_quote_transfer_fee_reverts_when_escrow_unconfigured PASS
test_quote_transfer_fee_zero_for_escrow_recipient PASS
test_quote_transfer_fee_zero_for_self_transfer PASS
test_quote_transfer_fee_zero_when_no_label_recorded PASS
test_register_emits_name_registered PASS
test_register_mints_to_owner PASS
test_register_reverts_for_non_controller PASS
test_register_reverts_when_name_not_available PASS
test_register_with_empty_label_skips_store_write PASS
test_register_writes_label_into_owner_label_store PASS
test_remove_controller PASS
test_remove_controller_emits_event PASS
test_remove_controller_reverts_for_non_owner PASS
test_self_transfer_skips_fee_charge PASS
test_supports_ierc721_interface PASS
test_transfer_reverts_when_fee_required_but_no_value_attached PASS
test_upgrade_rejects_non_owner PASS
test_version_string PASS

DotnsRegistrarControllerTest (test/unit/registrar/DotnsRegistrarController.t.sol)

Test Result Error
test_available_reverts_for_dotted_label PASS
test_available_reverts_for_empty_label PASS
test_available_state_transitions PASS
test_commit_allows_recommit_after_expiry PASS
test_commit_allows_recommit_at_exact_expiry_boundary PASS
test_commit_sets_timestamp PASS
test_initialize_reverts_when_max_above_ceiling PASS
test_initialize_reverts_when_max_not_greater_than_min PASS
test_initialize_reverts_when_min_commitment_age_is_zero PASS
test_mint_does_not_trigger_store_write PASS
test_non_owner_cannot_grant_role PASS
test_non_owner_cannot_revoke_role PASS
test_owner_can_grant_and_revoke_whitelist_operator PASS
test_register_does_not_overwrite_third_party_reverse_record PASS
test_register_popfull_wires_all_records PASS
test_register_poplite_reserves_base_name PASS
test_register_reverts_at_exact_expiry_boundary PASS
test_register_reverts_for_dotted_label PASS
test_registerreserved_revertnon_owner PASS
test_registerreserved_writes_to_store PASS
test_removed_from_whitelist_cannot_register_reserved PASS
test_revert_cross_payer_sponsoring_unverified_owner PASS
test_safe_transfer_writes_to_store PASS
test_setrole_reverts_for_unsupported_role PASS
test_setrole_reverts_for_zero_address PASS
test_supports_idotnsrolemanager_interface PASS
test_transfer_back_skips_locked_entry PASS
test_transfer_clears_primary_reverse_name_when_current_name_is_moved PASS
test_transfer_round_trip_to_original_depositor_rebinds_position_back PASS
test_transfer_skips_store_deploy_when_label_empty PASS
test_transfer_via_approved_operator_writes_to_store PASS
test_transfer_writes_label_and_creates_store PASS
test_transfer_zero_fee_rebinds_position_to_new_holder PASS
test_whitelist_operator_can_whitelist_address PASS
test_whitelistaddress_reverts_without_owner_or_operator PASS
test_whitelisted_can_register_reserved PASS

DotnsRegistrarControllerFuzzTest (test/fuzz/registrar/DotnsRegistrarControllerFuzz.t.sol)

Test Result Error
testFuzz_NoStatus_transfer_rebinds_position_to_new_holder PASS
testFuzz_non_owner_cannot_setrole PASS
testFuzz_owner_setrole_matches_hasrole PASS
testFuzz_register_pushes_overpayment_back_to_eoa_payer PASS
testFuzz_register_refunds_overpayment_inline PASS
testFuzz_register_refunds_overpayment_to_payer_not_owner PASS
testFuzz_third_party_registration_does_not_overwrite_owner_reverse PASS
testFuzz_transfer_clears_sender_primary_reverse PASS
testFuzz_transfer_writes_label_to_recipient_store PASS

DotnsRegistrarControllerLifecycleTest (test/unit/registrar/DotnsRegistrarControllerLifecycle.t.sol)

Test Result Error
test_register_creates_funded_position_for_self_registration PASS
test_register_cross_payer_charges_max_not_sum_of_price_and_reach PASS
test_register_cross_payer_routes_owner_price_to_protocol_fees PASS
test_register_overpayment_falls_back_to_ledger_on_rejecting_contract PASS
test_register_overpayment_pushed_directly_to_accepting_contract PASS
test_register_overpayment_pushed_directly_to_eoa_payer PASS
test_register_overpayment_reentrant_attacker_falls_back_to_ledger PASS
test_register_reclaim_state_consistent_during_safe_transfer_callback PASS
test_register_reclaim_succeeds_for_new_poplite_owner PASS
test_register_second_reserved_name_preserves_prior_primary_reverse_record PASS
test_revert_register_cross_payer_when_msg_value_below_max_of_price_and_reach PASS
test_revert_register_on_reentry_from_onerc721received PASS
test_revert_registerreserved_for_already_seeded_label PASS

DotnsRegistryTests (test/unit/registry/DotnsRegistry.t.sol)

Test Result Error
test_new_parent_can_reassign_after_erc721_transfer PASS
test_node_owner_can_clear_resolver_to_zero PASS
test_node_owner_creates_nested_subnode_with_canonical_parent_path PASS
test_node_owner_creates_subnode_emits_event_and_returns_expected_subnode PASS
test_node_owner_sets_resolver_emits_event_and_persists PASS
test_non_parent_cannot_call_setSubnodeResolver PASS
test_parent_can_set_resolver_on_subnode_via_setSubnodeResolver PASS
test_parent_reassigns_existing_subnode_owner PASS
test_parent_reassigns_subnode_to_self_then_sets_resolver PASS
test_protocol_registry_bound_at_init PASS
test_reassignment_emits_new_owner_event PASS
test_reassignment_resets_resolver_to_default PASS
test_registrar_controller_sets_owner_emits_event_and_sets_resolver PASS
test_revert_non_parent_cannot_reassign_subnode PASS
test_revert_subnode_owner_with_dotted_sublabel PASS
test_revert_subnode_owner_with_empty_sublabel PASS
test_revert_subnode_owner_with_parent_label_mismatch PASS
test_revert_subnode_owner_with_uppercase_parent_label PASS
test_revert_subnode_owner_with_uppercase_sublabel PASS
test_root_record_is_not_initialized PASS
test_same_sublabel_under_different_parents_owned_by_same_address PASS
test_setSubnodeResolver_reverts_on_nonexistent_subnode PASS
test_subnode_owner_can_still_set_resolver_directly PASS
test_subnode_owner_creates_nested_subnode_under_owned_parent PASS

DotnsRegistryFuzzTest (test/fuzz/registry/DotnsRegistryFuzz.t.sol)

Test Result Error
testFuzz_non_parent_non_owner_cannot_reassign PASS
testFuzz_parent_can_reassign_subnode_to_any_owner PASS
testFuzz_reassignment_resets_resolver_to_default PASS

DotnsResolverTests (test/unit/resolver/DotnsResolver.t.sol)

Test Result Error
test_setaddress_emits_event_and_persists PASS
test_setaddress_overwrites_previous_value PASS

DotnsReverseResolverTests (test/unit/resolver/DotnsReverseResolver.t.sol)

Test Result Error
test_claim_reverse_record_after_receiving_transfer PASS
test_claim_reverse_record_emits_event PASS
test_claim_reverse_record_overwrites_existing_primary PASS
test_claim_reverse_record_sets_for_current_owner PASS
test_nameof_fails_closed_for_unminted_label PASS
test_nameof_fails_closed_when_caller_no_longer_owns_stored_name PASS
test_nameof_fails_closed_when_stored_lacks_tld_suffix PASS
test_nameof_returns_empty_when_unset PASS
test_protocol_registry_bound_at_init PASS
test_register_preserves_existing_reverse_record PASS
test_register_sets_reverse_record_for_owner PASS
test_revert_claim_reverse_record_when_caller_does_not_own PASS

LabelStoreTests (test/unit/store/LabelStore.t.sol)

Test Result Error
test_caller_becoming_unregistered_rejects_subsequent_write PASS
test_getLabels_caps_at_available PASS
test_getLabels_returns_empty_when_offset_past_end PASS
test_implementation_cannot_be_initialised_directly PASS
test_initialize_binds_owner_and_registry PASS
test_initialize_reverts_on_second_call PASS
test_initialize_reverts_on_zero_registry PASS
test_initialize_reverts_on_zero_user PASS
test_storeLabel_reverts_on_second_write_same_labelhash PASS
test_storeLabel_reverts_when_caller_not_registered PASS
test_storeLabel_reverts_when_labelhash_zero PASS
test_storeLabel_writes_locks_and_enumerates PASS

LabelStoreFuzzTest (test/fuzz/store/LabelStoreFuzz.t.sol)

Test Result Error
testFuzz_getLabels_pagination_consistent PASS
testFuzz_storeLabel_succeeds_for_arbitrary_inputs PASS

NoStatusDepositLifecycle (test/intergration/NoStatusDepositLifecycle.t.sol)

Test Result Error
test_NoStatus_register_then_transfer_then_holder_claims_refund PASS

PopRulesFuzzTest (test/fuzz/pop/PopFuzz.t.sol)

Test Result Error
testFuzz_expired_reservation_rolls_forward_to_next_lite_registrant PASS
testFuzz_governance_names_always_revert PASS
testFuzz_mixed_case_names_are_rejected PASS
testFuzz_nostatus_user_cannot_access_popfull PASS
testFuzz_popfull_user_can_access_nostatus PASS
testFuzz_popfull_user_can_access_poplite PASS
testFuzz_price_matches_curve PASS
testFuzz_price_without_check_returns_price PASS
testFuzz_reservation_blocks_other_users PASS

PopLifecycleFlow (test/intergration/PopLifecycleFlow.t.sol)

Test Result Error
test_cold_gateway_reserve_then_user_settles_pending_claim PASS
test_lapsed_pending_claim_is_swept_without_deploying_store PASS
test_lite_via_gateway_then_full_via_public_after_upgrade PASS
test_pop_full_name_is_first_class_erc721_name PASS
test_recover_full_username_from_lite_label PASS
test_reserve_expire_reserve_cycle_for_same_user PASS
test_transfer_of_token_with_live_pending_claim_does_not_move_claim PASS

PopRulesTests (test/unit/pop/PopRules.t.sol)

Test Result Error
test_base_reservation_blocks_others PASS
test_classify_governance PASS
test_classify_nostatus PASS
test_classify_nostatus_no_digits PASS
test_classify_popfull PASS
test_classify_poplite PASS
test_classify_reverts_for_one_digit_suffix PASS
test_classify_reverts_for_three_digit_suffix PASS
test_open_band_is_flat_at_base_fee PASS
test_popfull_user_can_access_poplite_name PASS
test_poplite_user_can_access_nostatus_name PASS
test_price_follows_scarcity_curve PASS
test_price_with_check_revert_full_needed PASS
test_price_with_check_revert_governance PASS
test_price_without_check_returns_price_for_reserved PASS
test_releaseBaseName_expired_slot_cleared_by_any_controller PASS
test_releaseBaseName_reverts_for_non_controller PASS
test_releaseBaseName_reverts_for_non_reserving_controller PASS
test_releaseBaseName_succeeds_for_reserving_controller PASS
test_reserveBaseNameForPop_refreshes_expiry_for_same_owner PASS
test_reserveBaseNameForPop_reverts_for_non_controller PASS
test_reserveBaseNameForPop_reverts_when_slot_held_by_other_user PASS
test_trailing_digits_do_not_change_price PASS
test_transfer_reprices_at_own_length PASS
test_verified_person_pays_the_curve_for_premium PASS
test_writeReservation_preserves_original_controller_on_same_owner_refresh PASS

RootGatewayDispatcherTests (test/unit/registrar/RootGatewayDispatcher.t.sol)

Test Result Error
test_controller_authorises_call_from_dispatcher_address PASS
test_controller_rejects_unknown_msg_sender_when_not_root PASS
test_dispatcher_bubbles_controller_revert_data PASS
test_dispatcher_forwards_to_controller_when_root PASS
test_dispatcher_rejects_value_transfers PASS
test_dispatcher_reverts_when_caller_is_not_root PASS
test_dispatcher_target_is_immutable_and_points_to_controller PASS
test_rotating_pop_gateway_key_revokes_old_dispatcher PASS

StoreFactoryTests (test/unit/store/StoreFactory.t.sol)

Test Result Error
test_beacon_owner_is_factory_for_both_beacons PASS
test_claimUserStore_frontrun_is_harmless PASS
test_claimUserStore_owner_is_caller PASS
test_claimUserStore_reverts_on_double_claim PASS
test_constructor_deploys_both_beacons_and_implementations PASS
test_constructor_reverts_on_zero_registry PASS
test_deployLabelStoreFor_reverts_for_unregistered_non_owner PASS
test_deployLabelStoreFor_reverts_on_double_deploy PASS
test_deployLabelStoreFor_reverts_on_zero_user PASS
test_deployLabelStoreFor_succeeds_for_owner PASS
test_deployLabelStoreFor_succeeds_for_registered_protocol PASS
test_getLabelStores_enumerates_in_deployment_order PASS
test_getUserStores_enumerates_in_claim_order PASS
test_upgradeLabelStoreImplementation_propagates_to_live_proxies PASS
test_upgradeLabelStoreImplementation_reverts_for_non_owner PASS
test_upgradeLabelStoreImplementation_reverts_on_zero_impl PASS

StoreIntegrationTest (test/intergration/StoreIntegration.t.sol)

Test Result Error
test_beacon_upgrade_preserves_label_store_state PASS
test_beacon_upgrade_preserves_user_store_state PASS
test_double_transfer_back_does_not_revert_on_existing_lock PASS
test_erc721_transfer_syncs_label_to_recipient_store PASS
test_main_controller_registration_writes_label_store PASS
test_pop_controller_registration_writes_label_store PASS
test_registration_reuses_factory_owner_predeployed_store PASS
test_user_claim_round_trip_with_history PASS

StoreStressTest (test/stress/store/StoreStress.t.sol)

Test Result Error
test_label_store_many_labels PASS
test_pagination_extreme_bounds PASS
test_user_store_deep_history PASS
test_user_store_large_value_round_trip PASS
test_user_store_many_keys PASS

UserStoreTests (test/unit/store/UserStore.t.sol)

Test Result Error
test_getHistory_pagination_bounds PASS
test_getKeys_pagination PASS
test_implementation_cannot_be_initialised_directly PASS
test_initialize_binds_owner PASS
test_initialize_reverts_on_second_call PASS
test_initialize_reverts_on_zero_user PASS
test_setValue_does_not_duplicate_key_list_entries PASS
test_setValue_empty_bytes_after_nonempty_snapshots_prior_value PASS
test_setValue_first_write_leaves_history_empty PASS
test_setValue_fresh_key_with_empty_bytes_writes_no_history PASS
test_setValue_reverts_for_non_owner PASS
test_setValue_reverts_for_zero_key PASS
test_setValue_second_write_snapshots_prev_into_history PASS

UserStoreFuzzTest (test/fuzz/store/UserStoreFuzz.t.sol)

Test Result Error
testFuzz_getHistory_pagination_consistent PASS
testFuzz_history_length_equals_prior_nonempty_writes PASS
testFuzz_setValue_accepts_arbitrary_inputs PASS

WhitelistOperatorFlow (test/intergration/WhitelistOperatorFlow.t.sol)

Test Result Error
test_operator_can_seed_whitelist_for_reserved_registration PASS
test_operator_role_revocation_blocks_further_whitelist_writes PASS

View full report | View logs

Contract Tests (Invariant)

DotnsNameEscrowInvariantTest (test/invariant/escrow/DotnsNameEscrowInvariant.t.sol)

Test Result Error
invariant_claimed_positions_have_zero_amount PASS
invariant_no_funds_in_controller PASS
invariant_position_recipient_mirrors_current_nft_holder PASS
invariant_protocol_fees_match_tracked_inflows PASS
invariant_released_count_consistent PASS
invariant_released_tokens_in_escrow_custody PASS
invariant_reserves_match_positions PASS
invariant_solvency PASS
invariant_withdrawn_tokens_are_in_escrow_custody_and_available PASS

DotnsPopControllerInvariant (test/invariant/registrar/DotnsPopControllerInvariant.t.sol)

Test Result Error
invariant_every_minted_tokenId_has_nonempty_label PASS
invariant_fullClaim_liteLink_are_inverse PASS
invariant_no_stale_fullClaim PASS
invariant_no_stale_liteLink PASS
invariant_no_stuck_lapsed_pending_claims PASS
invariant_one_reservation_per_account_consistent PASS
invariant_pendingClaimUserCount_matches_enumeration_length PASS
invariant_pendingClaimUsers_mirrors_pendingClaims_mapping PASS
invariant_pending_claim_and_label_store_are_mutually_exclusive PASS
invariant_popRules_head_matches_queue_head_or_zero PASS
invariant_queue_length_bounded PASS

DotnsRegistrarControllerInvariantTest (test/invariant/registrar/DotnsRegistrarControllerInvariant.t.sol)

Test Result Error
invariant_consumed_commitments_deleted PASS
invariant_current_owners_have_label_in_store PASS
invariant_no_stuck_funds PASS
invariant_ownership_consistency PASS
invariant_registered_names_unavailable PASS
invariant_registration_count_consistent PASS
invariant_reserved_names_have_reverse_resolution PASS
invariant_store_entries_locked PASS
invariant_transfer_recipients_have_store_entries PASS
invariant_value_conservation PASS

DotnsRegistrarControllerRoleInvariantTest (test/invariant/registrar/DotnsRegistrarControllerRoleInvariant.t.sol)

Test Result Error
invariant_whitelist_operator_role_matches_ghost_state PASS

DotnsRegistryInvariantTest (test/invariant/registry/DotnsRegistryInvariant.t.sol)

Test Result Error
invariant_parent_can_always_reassign_subnodes PASS
invariant_subnode_owner_authorized PASS
invariant_subnodes_always_exist PASS

StoreInvariantTest (test/invariant/store/StoreInvariant.t.sol)

Test Result Error
invariant_at_most_one_store_of_each_type_per_user PASS
invariant_enumeration_matches_counters PASS
invariant_factory_owns_both_beacons PASS
invariant_locked_label_text_never_changes PASS
invariant_locked_labels_never_unlock PASS

View full report | View logs

Gas Report

DotnsProtocolRegistry

Function Master Current Diff
set 10,637 10,552 -85
initialize 3,287 3,250 -37
get 1,776 1,769 -7
tldNode 2,022 2,020 -2
isRegisteredAddress 1,901 1,900 -1
tld 2,298 2,297 -1

DotnsRegistrar

Function Master Current Diff
quoteTransferFee 68,276 69,140 +864
register 236,765 237,049 +284
addController 10,140 10,066 -74
initialize 561 555 -6
transferFrom 498,592 498,597 +5
exists 761 764 +3
safeTransferFrom 53,515 53,516 +1

DotnsRegistrarController

Function Master Current Diff
register 653,120 672,055 +18,935
setRole 9,993 9,908 -85
initialize 339 335 -4

DotnsRegistry

Function Master Current Diff
initialize 336 332 -4
setOwner 37,040 37,038 -2

DotnsReverseResolver

Function Master Current Diff
nameOf 13,090 13,071 -19
initialize 336 332 -4

PopRules

Function Master Current Diff
priceWithoutCheck 16,944 18,790 +1,846
transferFloor 10,248 12,010 +1,762
price 6,113 7,757 +1,644
priceWithCheck 18,509 19,423 +914
classifyName 5,895 5,976 +81
isBaseName 4,811 4,779 -32
MAX_RESERVATION_TIME 351 329 -22
getBaseNameReservation 9,485 9,463 -22
isBaseNameReserved 9,050 9,028 -22
releaseBaseName 31,570 31,548 -22
releaseReservationForReclaim 18,944 18,922 -22
startingPrice 2,717 2,695 -22
stripDigits 8,331 8,347 +16
reserveBaseNameForPop 57,006 56,996 -10

View full report | View logs

Coverage
File Lines Statements Branches Functions
contracts/utils/Multicall3.sol 0.0% 0.0% 0.0% 0.0%
scripts/deploy/DeployCore.s.sol 0.0% 0.0% 100.0% 0.0%
scripts/deploy/DeployPolicy.s.sol 0.0% 0.0% 100.0% 0.0%
scripts/deploy/DeployPopSystem.s.sol 0.0% 0.0% 100.0% 0.0%
scripts/deploy/DeployRecords.s.sol 0.0% 0.0% 100.0% 0.0%
scripts/deploy/DeploymentNetwork.sol 0.0% 0.0% 0.0% 0.0%
scripts/deploy/DotnsDeployer.s.sol 0.0% 0.0% 0.0% 0.0%
scripts/deploy/WireDeployments.s.sol 0.0% 0.0% 0.0% 0.0%
scripts/deploy/DeployCreate3Factory.s.sol 42.9% 33.3% 50.0% 100.0%
contracts/resolvers/DotnsResolver.sol 63.6% 56.2% 50.0% 66.7%
contracts/utils/LabelUtils.sol 65.4% 69.0% 100.0% 100.0%
scripts/deploy/BaseDeployer.s.sol 72.2% 65.8% 25.0% 81.8%
contracts/resolvers/DotnsPopResolver.sol 72.2% 73.3% 33.3% 75.0%
contracts/registrars/RootGatewayDispatcher.sol 75.0% 71.4% 66.7% 100.0%
contracts/resolvers/DotnsContentResolver.sol 75.8% 72.0% 50.0% 75.0%
contracts/utils/StringUtils.sol 76.8% 73.6% 33.3% 69.2%
contracts/resolvers/DotnsReverseResolver.sol 78.6% 81.8% 88.9% 70.0%
contracts/registry/DotnsProtocolRegistry.sol 85.2% 90.9% 33.3% 77.8%
contracts/escrow/DotnsNameEscrow.sol 88.1% 86.6% 18.0% 88.6%
contracts/registry/DotnsRegistry.sol 89.5% 89.5% 26.8% 89.5%
contracts/access/DotnsRoleManager.sol 90.0% 86.7% 50.0% 100.0%
contracts/store/UserStore.sol 90.9% 90.4% 30.0% 93.3%
contracts/registrars/DotnsRegistrar.sol 91.7% 94.9% 45.7% 88.9%
contracts/registrars/DotnsPopController.sol 92.2% 92.3% 53.9% 90.0%
contracts/registrars/DotnsRegistrarController.sol 92.2% 92.9% 36.4% 90.9%
contracts/store/LabelStore.sol 92.5% 94.2% 25.0% 93.8%
contracts/pop/PopRules.sol 94.4% 94.9% 44.4% 90.6%
contracts/store/StoreFactory.sol 95.0% 96.4% 25.0% 93.3%
contracts/deploy/Create3Factory.sol 100.0% 80.0% 0.0% 100.0%
contracts/utils/RegistrationUtils.sol 100.0% 100.0% 100.0% 100.0%
contracts/utils/StoreUtils.sol 100.0% 100.0% 100.0% 100.0%

View full report | View logs

Deploy Contracts

Deployed addresses vs DEPLOYMENTS.md

Expected is the committed manifest; actual is this CI deployment of the same pipeline.

Contract Expected Actual Match
Create3Factory 0x8533c79E058c5a6489CAFeCA86dc600E029D75f5 0x8533c79E058c5a6489CAFeCA86dc600E029D75f5 match
DotnsContentResolver 0x7F74D7CD50f5a834270E2ad395a01b01891AB37d 0x7F74D7CD50f5a834270E2ad395a01b01891AB37d match
DotnsNameEscrow 0x4881Afb78e7C908cAe818168B926229D93376520 0x4881Afb78e7C908cAe818168B926229D93376520 match
DotnsPopController 0xCC932348606cc1f3318cADeC5A5Cd2CA447f8a4b 0xCC932348606cc1f3318cADeC5A5Cd2CA447f8a4b match
DotnsPopResolver 0xDaC984884EcA8Fc44011f1D6C49B27828390A72B 0xDaC984884EcA8Fc44011f1D6C49B27828390A72B match
DotnsProtocolRegistry 0xD19e3D0C97CF501125a04A97405e3e6592fa846E 0xD19e3D0C97CF501125a04A97405e3e6592fa846E match
DotnsRegistrar 0x4f06E818Ba3d987704fd91cf3d868E4b019106Ab 0x4f06E818Ba3d987704fd91cf3d868E4b019106Ab match
DotnsRegistrarController 0xBdaA01bD1bA67d709F2b1fF286Da0d854977EA30 0xBdaA01bD1bA67d709F2b1fF286Da0d854977EA30 match
DotnsRegistry 0xf34054fd76BbF85f216cf9908226D5f0A72E50CA 0xf34054fd76BbF85f216cf9908226D5f0A72E50CA match
DotnsResolver 0xbd1165E549DF96F083c0A16f61590927bC187009 0xbd1165E549DF96F083c0A16f61590927bC187009 match
DotnsReverseResolver 0xee3883d7eB60Ee9BCD7F3bcD8f2f05302A9Cc035 0xee3883d7eB60Ee9BCD7F3bcD8f2f05302A9Cc035 match
LabelStoreBeacon 0xb57Ebc2e7085616d4906D1fE49af1cE13f7dffeF 0xb57Ebc2e7085616d4906D1fE49af1cE13f7dffeF match
Multicall3 0xB4468000abD87D3c56cbFBd153161223D7b109e5 0xB4468000abD87D3c56cbFBd153161223D7b109e5 match
PopRules 0x747B456bE03aec0b42bd85C51513730FBD45DA31 0x747B456bE03aec0b42bd85C51513730FBD45DA31 match
RootGatewayDispatcher 0xa889CCA3Fb4B07b98a11cc54C10f13dDA20bc3db 0xa889CCA3Fb4B07b98a11cc54C10f13dDA20bc3db match
StoreFactory 0x709A027F446a9e2a4BB9cb9a9c754435b19e32B7 0x709A027F446a9e2a4BB9cb9a9c754435b19e32B7 match
UserStoreBeacon 0xb7C995601679840d36F37E86DB2d7dF30797eC5C 0xb7C995601679840d36F37E86DB2d7dF30797eC5C match
_seed 0x0000000000000000000000000000000000000000 0x0000000000000000000000000000000000000000 match

View full logs

Labels

smartcontracts, scope: registration, type: test, type: docs, scope: pop

…in state

The protocol-fee conservation invariant mirrored the on-chain balance delta into its own tracker, so it compared the escrow balance against an echo of itself and could never catch a miscredit. The handler now accumulates the independently computed charge and quoted transfer fee, and a new invariant asserts those tracked inflows equal the on-chain protocol fees. Dead insurance-draw scaffolding, its recorded-log parsing, and stale insurance wording are removed to match the protocol-fee model.
…in state (#210)

## Description

Follow-up to the scarcity-pricing change.

The protocol-fee conservation invariant used to read the on-chain
protocol-fee balance before and after each action and fold the
difference into its own tracker. Because both sides came from the same
source, the check compared the balance against an echo of itself and
would have passed even if a caller had been charged the wrong amount.
The handler now accumulates the fee it independently expects (the charge
on a cross-paid registration, the quoted fee on a payable transfer), so
the invariant proves the escrow credited exactly what the caller was
asked to pay. A second invariant asserts that the tracked inflows sum to
the on-chain protocol-fee balance, which holds because protocol fees
only ever accrue.

## Type

- [ ] Bug fix
- [ ] Feature
- [ ] Breaking change
- [ ] Documentation
- [ ] Chore
- [ ] Refactor
- [ ] Security

## Scope

- [ ] Registration
- [ ] Resolver
- [ ] Store
- [ ] Proof of Personhood
- [ ] Deployment scripts
- [x] Tests

## Related Issues

Stacked on #208.

## Fixes

## Checklist

### Code

- [x] Follows project style
- [x] `forge build` passes
- [x] `forge test` passes
- [x] No new compiler warnings

### Testing

- [ ] New tests added for changed behavior
- [ ] Fuzz tests added where applicable
- [x] Invariant tests verified

### Security

- [x] No new `selfdestruct` or `delegatecall`
- [x] Access control reviewed
- [x] No storage layout conflicts (for upgradeable contracts)

### Documentation

- [x] NatSpec updated on changed interfaces
- [x] README updated if needed

### Breaking Changes

- [x] No breaking changes
- [ ] Breaking changes documented below

**Breaking changes:**

## How to test

```bash
forge test --mc DotnsNameEscrowInvariantTest
```

## Notes

Stacks on #208; review and merge that first. The new pricing-invariant
suite is tracked separately in #209.
The base-fee setter rejects a value large enough to overflow the scarcity multiplier, so short-name pricing and transfers cannot be knocked offline by an oversized fee. Cross-payer registration charges the name's own curve price directly; the floor comparison that could never change the amount is removed. The transfer-fee event fires only when a fee is taken, so a zero-fee position rebind emits nothing. The escrow transfer-fee parameter and its registrar call sites read transferFee to match the value they carry, and the transfer-floor and fee-parameter documentation is completed.
The free name is claimed per wallet through the personhood gateway, which waives the price. The gateway lane accepts a name of any length and applies no public band gate; it only refuses the governance-reserved stems of five characters or fewer.
@re-gius

re-gius commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

I would keep this PR on hold and have a broader discussion on this argument. This can be taken as a concrete proposal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants