Skip to content

fix(platform-wallet): raise the invitation cap to cover the contested username tier - #4351

Closed
bfoss765 wants to merge 1 commit into
dashpay:v4.2-devfrom
bfoss765:fix/invitation-cap-contested
Closed

fix(platform-wallet): raise the invitation cap to cover the contested username tier#4351
bfoss765 wants to merge 1 commit into
dashpay:v4.2-devfrom
bfoss765:fix/invitation-cap-contested

Conversation

@bfoss765

@bfoss765 bfoss765 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

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: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_invitation becomes the only route — and every contested invitation is rejected pre-broadcast:

Invalid identity data: invitation amount 25000000 exceeds the cap 5000000 duffs

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

  • The constant'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 (no cap at claim), and contested-invite claims are verified working in QA.
  • The cap is a client-side leaked-link bound, not consensus — registration/top-up have no equivalent.
  • Both the dashj legacy path and the shielded invitation path already mint 0.25 bearer links today, so 0.05 on this one path protected nothing while breaking path parity post-cutover.

0.26 = the 0.25 contested fee + margin for the create/claim fees, matching the Android wallet's rounded contested guidance. MIN_INVITATION_DUFFS and all boundary tests are relative to the constant and unchanged; platform-wallet lib: 610 passed / 0 failed.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Increased the maximum invitation funding limit to support both standard and contested DPNS-name onboarding scenarios.

… 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>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The 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.

Changes

Invitation funding

Layer / File(s) Summary
Update invitation funding cap
packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs
MAX_INVITATION_DUFFS increases from 5,000,000 to 26,000,000 duffs. The documentation covers identity creation with normal or contested DPNS names.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related PRs

Suggested reviewers: lklimek, romchornyi, shumkov

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: increasing the platform-wallet invitation cap for contested username invitations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@thepastaclaw

thepastaclaw commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit 25534db)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6373e00 and 25534db.

📒 Files selected for processing (1)
  • packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs

Comment on lines +48 to +60
/// 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;

@coderabbitai coderabbitai Bot Aug 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 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 expanded

Length 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' \
  || true

Length of output: 60015


@thepastaclaw, confirmed. The expanded scope is valid.

  • CreateInvitationSheet.swift has the actual stale Swift upper-bound validation. It rejects every amount above 5_000_000 duffs.
  • The Kotlin example does not enforce an upper bound locally. Its 0.003–0.05 range is stale display text. Rust remains the enforcement point.
  • The obsolete contract also appears in:
    • packages/swift-sdk/SwiftExampleApp/Info.plist
    • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/AI_QA/QA004_invitation_reclaim.md
    • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/dashpay/CreateInvitationSheet.kt
    • packages/kotlin-sdk/KotlinExampleApp/TEST_PLAN.md
    • packages/kotlin-sdk/PARITY.md
    • docs/dashpay/DIP15_INVITATIONS_SPEC.md, including the old deferred contested-name statement and the obsolete 0.01 DASH leak-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 thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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']

@bfoss765

Copy link
Copy Markdown
Collaborator Author

Closed in favor of the in-repo recreation: #4362 (same commits and authorship; no more personal-fork PRs).

@bfoss765 bfoss765 closed this Aug 10, 2026
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.

2 participants