fix(platform-wallet): raise the invitation cap to cover the contested username tier - #4351
fix(platform-wallet): raise the invitation cap to cover the contested username tier#4351bfoss765 wants to merge 1 commit into
Conversation
… username tier MAX_INVITATION_DUFFS 5_000_000 -> 26_000_000 (0.05 -> 0.26 DASH). Required for existing Android functionality: the Android wallet has funded contested (premium-name) invitations at 0.25 DASH since 2024 (DASH_PAY_FEE_CONTESTED, dash-wallet Constants.java). Its legacy dashj path imposes no cap, so contested invitations have always worked pre-cutover; the moment a wallet commits the dashj->SDK cutover, create_invitation becomes the only route and every contested invitation is rejected pre-broadcast with 'invitation amount 25000000 exceeds the cap 5000000 duffs' - observed on device 2026-08-09, an endless-retry loop in the field. The 0.05 value's own doc deferred the contested tier 'until contested-name-via-invite claim exists; raise this cap when it does'. That condition is met: claim_invitation is amount-agnostic and contested-invite claims are verified working in QA. The cap remains a client-side leaked-link bound, not consensus; both the dashj and shielded invitation paths already mint 0.25 bearer links today, so 0.05 here protected nothing while breaking path parity. 0.26 = 0.25 contested fee + margin for the create/claim fees, matching the Android wallet's rounded contested guidance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe public invitation funding cap increases from 5,000,000 to 26,000,000 duffs. The documentation states that the cap covers identity creation and normal or contested DPNS names. ChangesInvitation funding
Estimated code review effort: 1 (Trivial) | ~5 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
✅ Final review complete — no blockers (commit 25534db) |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs`:
- Around line 48-60: Update the invitation amount validation in
CreateInvitationSheet and the public limit documentation in Dashpay to reflect
MAX_INVITATION_DUFFS (26,000,000 duffs), preferably exposing and reusing the
Rust limit through client APIs instead of duplicating literals. Add client-level
boundary coverage for an amount between 5,000,000 and 26,000,000 duffs, ensuring
it is accepted.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 6dc0cb5f-f942-4693-8b77-663113a20f3f
📒 Files selected for processing (1)
packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs
| /// Hard cap on the amount an invitation can lock (0.26 DASH). The voucher is a | ||
| /// bearer credential, so the blast radius of a leaked link is bounded here in | ||
| /// Rust — not just in the UI. Sized for onboarding: the invitee spends the | ||
| /// voucher on identity creation **plus** a normal DPNS name (~0.03 DASH — the | ||
| /// legacy `DASH_PAY_FEE`), so the previous 0.01 cap was actually below a usable | ||
| /// invitation and rejected its own onboarding default. The contested/premium-name | ||
| /// tier (~0.25 DASH) is deferred until contested-name-via-invite claim exists; | ||
| /// raise this cap when it does. | ||
| pub const MAX_INVITATION_DUFFS: u64 = 5_000_000; | ||
| /// Rust — not just in the UI. Sized for onboarding at BOTH username tiers: the | ||
| /// invitee spends the voucher on identity creation **plus** a DPNS name — a | ||
| /// normal name (~0.03 DASH, the legacy `DASH_PAY_FEE`) or a contested/premium | ||
| /// name (~0.25 DASH, `DASH_PAY_FEE_CONTESTED`), with the 0.01 margin covering | ||
| /// the create/claim fees. The earlier 0.05 value deferred the contested tier | ||
| /// "until contested-name-via-invite claim exists" — the claim path is | ||
| /// amount-agnostic and contested-invite claims are verified working, and the | ||
| /// Android wallet has funded contested invitations at 0.25 since 2024, so the | ||
| /// deferral is over. (The pre-merge 0.01 iteration was below even a usable | ||
| /// non-contested invitation and rejected its own onboarding default.) | ||
| pub const MAX_INVITATION_DUFFS: u64 = 26_000_000; |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Update the downstream invitation limits.
The Rust cap now accepts up to 26_000_000 duffs, but downstream contracts still use 5_000_000.
packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DashPay/CreateInvitationSheet.swift, Lines 24-26: update the local validation limit. Otherwise, the Swift flow rejects valid contested invitations before calling Rust.packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/tokens/Dashpay.kt, Lines 405-421: update the SDK documentation to prevent an incorrect public contract.
Prefer exposing the Rust limit through the client APIs instead of maintaining independent literals. Add client-level boundary coverage for an amount between the old and new caps.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs` around
lines 48 - 60, Update the invitation amount validation in CreateInvitationSheet
and the public limit documentation in Dashpay to reflect MAX_INVITATION_DUFFS
(26,000,000 duffs), preferably exposing and reusing the Rust limit through
client APIs instead of duplicating literals. Add client-level boundary coverage
for an amount between 5,000,000 and 26,000,000 duffs, ensuring it is accepted.
There was a problem hiding this comment.
The Swift cap and Kotlin KDoc mismatches are valid. The obsolete contract also remains in docs/dashpay/DIP15_INVITATIONS_SPEC.md, the Kotlin example's displayed range and test plan, and the Swift Info.plist security note; the Rust guard also lacks direct coverage for 25,000,000, 26,000,000, and 26,000,001 duffs.
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- invitation limit references ---'
rg -n -i -C 3 \
-e '5_000_000' \
-e '5000000' \
-e '0\.05[[:space:]]*DASH' \
-e 'MAX_INVITATION_DUFFS' \
-e 'invitation.*(amount|limit|range|duff)' \
packages docs
printf '%s\n' '--- candidate Swift and Kotlin files ---'
fd -t f -i \
-g 'CreateInvitationSheet.swift' \
-g 'Dashpay.kt' \
-g 'Info.plist' \
-g '*Invitation*Test*.swift' \
-g '*Invitation*Test*.kt' \
packages
printf '%s\n' '--- Rust module outline ---'
ast-grep outline packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs \
--items all --type function --view expandedLength of output: 201001
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- exact obsolete invitation-limit contracts ---'
rg -n -i -C 4 \
-e 'MAX_INVITATION_DUFFS' \
-e '5_000_000' \
-e '0\.05[[:space:]]*DASH' \
-e '5000000' \
packages/swift-sdk packages/kotlin-sdk docs/dashpay/DIP15_INVITATIONS_SPEC.md \
-g '!**/test/data/**' -g '!**/*.lock'
printf '%s\n' '--- relevant file excerpts ---'
sed -n '1,180p' \
packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DashPay/CreateInvitationSheet.swift
sed -n '25,55p' packages/swift-sdk/SwiftExampleApp/Info.plist
sed -n '380,440p' \
packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/tokens/Dashpay.kt
printf '%s\n' '--- invitation-related Swift/Kotlin test candidates ---'
find packages/swift-sdk packages/kotlin-sdk -type f \
\( -iname '*invitation*test*.swift' -o -iname '*invitation*test*.kt' \
-o -iname '*dashpay*test*.swift' -o -iname '*dashpay*test*.kt' \) \
-print | sort
printf '%s\n' '--- binding/API references for invitation limits ---'
rg -n -C 2 \
-e 'platform_wallet_create_invitation' \
-e 'createInvitation' \
-e 'MAX_INVITATION_DUFFS' \
-e 'minInvitationDuffs|maxInvitationDuffs' \
packages/swift-sdk packages/kotlin-sdk \
-g '!**/build/**' -g '!**/node_modules/**'
printf '%s\n' '--- Rust amount-validation test seams ---'
rg -n -C 4 \
-e 'amount_duffs.*(MAX_INVITATION_DUFFS|MIN_INVITATION_DUFFS)' \
-e 'create_invitation\(' \
-e 'UnreachableSigner' \
packages/rs-platform-wallet \
-g '*.rs'Length of output: 91544
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- Kotlin invitation UI validation ---'
sed -n '1,230p' \
packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/dashpay/CreateInvitationSheet.kt
printf '%s\n' '--- Kotlin invitation-related unit and UI tests ---'
sed -n '1,260p' \
packages/kotlin-sdk/KotlinExampleApp/app/src/test/java/org/dashfoundation/example/ui/dashpay/InvitationReclaimLogicTest.kt
sed -n '1,260p' \
packages/kotlin-sdk/KotlinExampleApp/app/src/androidTest/java/org/dashfoundation/example/DashPayTabUITest.kt
printf '%s\n' '--- Swift example test target membership and invitation test sources ---'
rg -n -C 3 \
-e 'CreateInvitationSheet\.swift' \
-e 'ReclaimInvitationClassifierTests\.swift' \
-e 'SwiftExampleAppTests' \
packages/swift-sdk \
-g '*.pbxproj' -g '*.xctestplan' -g '*.swift'
sed -n '1,180p' \
packages/swift-sdk/SwiftExampleApp/SwiftExampleAppTests/ReclaimInvitationClassifierTests.swift
sed -n '1,180p' \
packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/DashPayTabUITests.swift
printf '%s\n' '--- Kotlin constant and parser definitions ---'
rg -n -C 5 \
-e 'MAX_INVITATION|MIN_INVITATION' \
-e 'maxInvitation|minInvitation' \
-e 'amountDuffs' \
packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/dashpay/CreateInvitationSheet.kt
printf '%s\n' '--- invitation FFI public API candidates ---'
rg -n -C 3 \
-e 'platform_wallet_create_invitation' \
-e 'MAX_INVITATION_DUFFS|MIN_INVITATION_DUFFS' \
packages/rs-platform-wallet packages/rs-platform-ffi \
-g '*.rs' -g '*.h' \
|| trueLength of output: 60015
@thepastaclaw, confirmed. The expanded scope is valid.
CreateInvitationSheet.swifthas the actual stale Swift upper-bound validation. It rejects every amount above5_000_000duffs.- The Kotlin example does not enforce an upper bound locally. Its
0.003–0.05range is stale display text. Rust remains the enforcement point. - The obsolete contract also appears in:
packages/swift-sdk/SwiftExampleApp/Info.plistpackages/swift-sdk/SwiftExampleApp/SwiftExampleApp/AI_QA/QA004_invitation_reclaim.mdpackages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/dashpay/CreateInvitationSheet.ktpackages/kotlin-sdk/KotlinExampleApp/TEST_PLAN.mdpackages/kotlin-sdk/PARITY.mddocs/dashpay/DIP15_INVITATIONS_SPEC.md, including the old deferred contested-name statement and the obsolete0.01 DASHleak-cap text.
- No existing Swift or Kotlin client API exposes
MAX_INVITATION_DUFFS.
The focused fix should update all user-visible contracts to 26_000_000 duffs (0.26 DASH). It should extract the Rust amount guard into a direct test seam. Tests should accept 25_000_000 and 26_000_000 duffs, and reject 26_000_001 duffs. Swift should also test that its local validation accepts 25_000_000 duffs.
✏️ Learnings added
Learnt from: thepastaclaw
Repo: dashpay/platform PR: 4351
File: packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs:48-60
Timestamp: 2026-08-09T21:50:25.116Z
Learning: For DashPay DIP-13 invitations, `packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs` enforces `MAX_INVITATION_DUFFS = 26_000_000` (0.26 DASH). The cap supports contested username invitations funded at 0.25 DASH plus create and claim fee margin. Swift and Kotlin user-visible invitation funding limits, security notes, test plans, and DIP-15 documentation must remain aligned with this Rust-enforced cap.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
You are interacting with an AI system.
thepastaclaw
left a comment
There was a problem hiding this comment.
Final validation — Codex/Sol only (Phase 2 disabled)
The Rust cap change enables the intended 0.25 DASH contested invitation, but the new boundary lacks regression coverage and leaves downstream documentation and the Swift example enforcing the obsolete 0.05 DASH contract. The custom-scheme exposure is real but explicitly documented and accepted for this feature, with verified-link infrastructure already tracked separately, so it is not an in-scope blocker.
Source: codex-general backend gpt-5.6-sol; codex-rust-quality backend gpt-5.6-sol; codex-security-auditor backend gpt-5.6-sol; final verifier backend gpt-5.6-sol. openclaw-agent/cliproxy/gpt-5.6-sol is orchestration-only and not reviewer evidence.
Validated zero-blocker Codex/Sol precheck evidence was promoted to final because Phase 2 (Sonnet/Opus) is temporarily disabled. This is Codex/Sol-only final validation, not Codex + Sonnet/Opus coverage.
Review provenance
- Codex reviewers:
gpt-5.6-sol— general (completed),gpt-5.6-sol— rust-quality (completed),gpt-5.6-sol— security-auditor (completed) - Verifier:
gpt-5.6-sol— verifier - Sonnet/Opus: not run (Phase 2 disabled — temporary Codex/Sol-only final)
- Secondary pass: disabled (
temporary_phase2_sonnet_disable)
🟡 2 suggestion(s)
1 additional finding(s) omitted (not in diff).
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs`:
- [SUGGESTION] packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs:60: Add regression coverage for the contested invitation boundary
The reported failure was specifically caused by 25,000,000 duffs being rejected, but no test references `MAX_INVITATION_DUFFS` or exercises the updated range. The existing durability test submits only 1,000,000 duffs and would still pass if this constant reverted to 5,000,000. Add boundary tests showing that 25,000,000 and exactly 26,000,000 proceed past amount validation to the later persistence error, while 26,000,001 returns `PlatformWalletError::InvalidIdentityData` before persistence or funding is attempted.
In `docs/dashpay/DIP15_INVITATIONS_SPEC.md`:
- [SUGGESTION] docs/dashpay/DIP15_INVITATIONS_SPEC.md:38-40: Synchronize the documented invitation cap
The as-built specification still declares a 5,000,000-duff maximum and later says the contested tier is deferred, contradicting the new public Rust constant and this PR's stated behavior. This mismatch is functional in `packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DashPay/CreateInvitationSheet.swift`, where `maxInvitationDuffs` remains 5,000,000 and rejects contested invitations before calling Rust. Update that UI guard and its displayed range, the Kotlin SDK KDoc at `Dashpay.kt:419-421`, the Kotlin example copy, and the security documentation that still identifies the bound as 0.05 DASH so all consumers describe and permit the same 26,000,000-duff range.
| /// Android wallet has funded contested invitations at 0.25 since 2024, so the | ||
| /// deferral is over. (The pre-merge 0.01 iteration was below even a usable | ||
| /// non-contested invitation and rejected its own onboarding default.) | ||
| pub const MAX_INVITATION_DUFFS: u64 = 26_000_000; |
There was a problem hiding this comment.
🟡 Suggestion: Add regression coverage for the contested invitation boundary
The reported failure was specifically caused by 25,000,000 duffs being rejected, but no test references MAX_INVITATION_DUFFS or exercises the updated range. The existing durability test submits only 1,000,000 duffs and would still pass if this constant reverted to 5,000,000. Add boundary tests showing that 25,000,000 and exactly 26,000,000 proceed past amount validation to the later persistence error, while 26,000,001 returns PlatformWalletError::InvalidIdentityData before persistence or funding is attempted.
source: ['codex']
|
Closed in favor of the in-repo recreation: #4362 (same commits and authorship; no more personal-fork PRs). |
Required for existing Android functionality
The Android wallet has funded contested (premium-name) invitations at 0.25 DASH since 2024 (
DASH_PAY_FEE_CONTESTED, dash-walletConstants.java:332). Its legacy dashj invitation path imposes no cap, so contested invitations have always worked pre-cutover. The moment a wallet commits the dashj→SDK cutover,create_invitationbecomes the only route — and every contested invitation is rejected pre-broadcast:Observed on a device 2026-08-09 as an endless confirm/retry loop (the app-side loop is fixed separately; the capability gap is this cap). A contested L1 invitation has never succeeded through the SDK path — the 0.25-vs-0.05 contradiction has existed since the path was first wired.
Why raising is safe and sanctioned
claim_invitationis amount-agnostic (no cap at claim), and contested-invite claims are verified working in QA.0.26 = the 0.25 contested fee + margin for the create/claim fees, matching the Android wallet's rounded contested guidance.
MIN_INVITATION_DUFFSand all boundary tests are relative to the constant and unchanged;platform-walletlib: 610 passed / 0 failed.🤖 Generated with Claude Code
Summary by CodeRabbit