Skip to content

feat(dashpay): any number of simultaneous contested name requests - #959

Merged
QuantumExplorer merged 2 commits into
developfrom
feat/multi-contested-requests
Aug 10, 2026
Merged

feat(dashpay): any number of simultaneous contested name requests#959
QuantumExplorer merged 2 commits into
developfrom
feat/multi-contested-requests

Conversation

@QuantumExplorer

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Testnet QA: requesting "bite-me" while "greg" was still in its vote was refused with "wait for that vote to finish". The limit was never a protocol rule — each label runs its own independent vote poll with its own 0.2 DASH vote-resolution fund. It existed only because the app's reconciliation bookmark (DWContestedNameStatusService) was single-slot, and #946's review fixed the overwrite hazard with a one-at-a-time guard rather than a multi-label store.

What was done

The bookmark store is now a per-(network, wallet) dictionary — canonical label → {submittedAt, votingEnd} — and the guard is removed end to end:

  • DWContestedNameStatusService: pendingLabels (oldest-first) and per-label pendingVotingEndTime(label:for:); recordSubmission upserts (recovery re-records can't reorder); recordVotingEndTime / clearPending are per-label; finalizeWon clears only the won label, so other contests keep reconciling. pendingLabel / pendingVotingEndTime survive as OLDEST-entry conveniences — the setup-flow consumers (JoinDashPay banner, CreateUsername voting screen, profile sheet, menus) only ever deal with the first username and needed no changes. The retired single-slot bookmark migrates into the dictionary on first read (same wallet scope, unambiguous attribution); the unscoped legacy bookmark is still discarded, and resetForWipe covers the new key prefix.
  • DWCurrentUserIdentityInfo: the username-surface filter (Edit Profile, profile sheet, invitation links, payment memo) and the recovered-username pick now exclude EVERY in-flight label; identity recovery rebuilds one bookmark per still-voting contested label instead of only contested.min().
  • DWIdentityRegistrationCoordinator: checkPendingContestResolution loops all in-flight labels; each resolves independently (a per-label fetch failure skips only that label for the pass), and won/lost clears only its own bookmark.
  • Marketplace: the contestInProgress guard, error case, sheet branch, and its string are gone.

How Has This Been Tested?

Clean dashpay arm64 simulator build. On the testnet QA simulator (which carries a live in-flight "greg" contest whose old-format bookmark exercises the migration): verifying that a second contested request now submits, both labels show under My Names → "In network vote", and neither leaks into the username surfaces. Results to follow on this PR. (Unit-test target pre-existing broken.)

Breaking Changes

None.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

The one-contested-request-at-a-time refusal existed only because the
reconciliation bookmark was single-slot; the protocol itself runs one
independent vote poll (and vote-resolution fund) per label. The store
is now a per-(network, wallet) dictionary — canonical label →
{submittedAt, votingEnd} — and the guard is gone:

- DWContestedNameStatusService: pendingLabels / per-label votingEndTime,
  recordSubmission upserts, recordVotingEndTime and clearPending are
  per-label, finalizeWon clears only the won label. pendingLabel /
  pendingVotingEndTime remain as OLDEST-entry conveniences for the
  setup-flow surfaces (which only ever deal with the first username),
  so those consumers are unchanged. The retired single-slot bookmark
  migrates into the dictionary on first read; the unscoped legacy
  bookmark is still discarded. resetForWipe covers the new key prefix.
- DWCurrentUserIdentityInfo: the username-surface filter and the
  recovered-username pick exclude EVERY in-flight label; identity
  recovery rebuilds a bookmark per still-voting contested label
  instead of only the lexicographic minimum.
- DWIdentityRegistrationCoordinator: checkPendingContestResolution
  resolves every in-flight label independently — a per-label failure
  skips only that label; won/lost clears only its own bookmark.
- UsernameMarketplaceService: contestInProgress guard and error case
  removed; RegisterNameSheet's wait-for-the-other-vote branch removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@QuantumExplorer, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 30 seconds

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7857db08-488e-4e9a-843f-48fbeca9aac5

📥 Commits

Reviewing files that changed from the base of the PR and between 3277529 and dd33d9d.

📒 Files selected for processing (6)
  • DashWallet/Sources/Infrastructure/SwiftDashSDK/Identity/DWContestedNameStatusService.swift
  • DashWallet/Sources/Infrastructure/SwiftDashSDK/Identity/DWCurrentUserIdentityInfo.swift
  • DashWallet/Sources/Infrastructure/SwiftDashSDK/Identity/DWIdentityRegistrationCoordinator.swift
  • DashWallet/Sources/Infrastructure/SwiftDashSDK/UsernameMarketplaceService.swift
  • DashWallet/Sources/UI/Explore Dash/UsernameMarketplaceScreen.swift
  • DashWallet/en.lproj/Localizable.strings

Comment @coderabbitai help to get the list of available commands.

After the PIN the marketplace sat silent for the seconds a Platform
transition takes — the action sheets dismiss themselves as the action
starts, so nothing said work was happening until the success banner.
perform() now takes a progressText and drives a blocking spinner
overlay on the marketplace screen ("Submitting your username
request…", "Listing for sale…", "Completing your purchase…", …), shown
until the action succeeds or fails. The PIN prompt presents above it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@QuantumExplorer

Copy link
Copy Markdown
Member Author

Added in 2nd commit: a blocking activity overlay during trade transitions — after the PIN, the screen now shows a spinner with what's happening ("Submitting your username request…", "Listing for sale…", etc.) instead of sitting silent until the success banner. perform() gained a progressText: and all six trade actions pass one.

@QuantumExplorer
QuantumExplorer merged commit 62f9da7 into develop Aug 10, 2026
2 checks passed
@QuantumExplorer

Copy link
Copy Markdown
Member Author

Verified on the testnet QA simulator: four contested requests in flight simultaneously (greg, qu1et, samu, ye1p), each with its own bookmark and deadline under My Names → In network vote; the pre-rework single-slot "greg" bookmark migrated cleanly, and no still-voting label leaks into the username surfaces.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant