Skip to content

feat(platform-wallet): exact network fee from send_payment + shared testnet faucet client#4049

Open
QuantumExplorer wants to merge 5 commits into
v4.1-devfrom
feat/dashpay-payment-fee-and-faucet-client
Open

feat(platform-wallet): exact network fee from send_payment + shared testnet faucet client#4049
QuantumExplorer wants to merge 5 commits into
v4.1-devfrom
feat/dashpay-payment-fee-and-faucet-client

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jul 9, 2026

Copy link
Copy Markdown
Member

Two commits used by dashwallet-ios (the DashPay contacts arc, dashwallet-ios #787), cherry-picked clean onto v4.1-dev:

  1. Fee threadingsend_payment returns the exact network fee of the broadcast transaction; platform_wallet_send_dashpay_payment gains a nullable out_fee_duffs out-param; Swift sendDashPayPayment returns (txid: Data, feeDuffs: UInt64). Lets wallet UIs show the real fee on the payment success screen instead of an estimate.

  2. Testnet faucet client promoted into the SDK packageTestnetFaucet + the cap.js proof-of-work solver move from SwiftExampleApp into Sources/SwiftDashSDK/Utils so dashwallet-ios can drive the faucet in-app without carrying a drifting copy (public API unchanged; the example app picks it up through its existing import). Includes the example-app call-site fix for the new sendDashPayPayment tuple return.

Verified: cargo check -p platform-wallet -p platform-wallet-ffi clean on this branch; both SwiftExampleApp and dashwallet-ios build against these commits (on the pre-rebase base); the fee display and the faucet request were exercised live on testnet (faucet txid 395eeb08…).

Not included (owned elsewhere): the tx-decode commit (PR #3981) and the RawKeySigner extraction (47a83cf6d0, Row #27's branch).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • DashPay payments now return the exact network fee alongside the transaction ID, making payment costs clearer after sending.
    • SDK integrations and example flows have been updated to handle and surface the fee value without changing the send experience.

QuantumExplorer and others added 2 commits July 9, 2026 16:00
The builder computed the fee for every DashPay payment and discarded
it. Thread it through: send_payment returns (txid, entry, fee);
platform_wallet_send_dashpay_payment gains a nullable out_fee_duffs;
Swift sendDashPayPayment returns (txid, feeDuffs) so wallets can show
the exact fee of the broadcast transaction.

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

Moves TestnetFaucet + CapSolver from SwiftExampleApp into
Sources/SwiftDashSDK/Utils so dashwallet-ios can drive the faucet
in-app without carrying a drifting copy. Public API unchanged
(TestnetFaucet().requestCoreDash, TestnetFaucet.webURL); the example
app picks it up through its existing SwiftDashSDK import.

Also folds in the example-app fallout from the fee-threading change:
sendDashPayPayment returns (txid, feeDuffs) now, so
SendDashPayPaymentSheet destructures the tuple.

Both apps build; live-verified against faucet.thepasta.org
(challenge -> solve -> redeem -> /api/core-faucet 200, txid 395eeb08...).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v4.1.0 milestone Jul 9, 2026
@thepastaclaw

thepastaclaw commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Review skipped (commit a810120): PR head moved to eafbfda before reviewer launch.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

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

Next review available in: 11 minutes

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

Run ID: a8453e6b-5aa5-4931-96c0-9874e5941328

📥 Commits

Reviewing files that changed from the base of the PR and between 57003a9 and eafbfda.

📒 Files selected for processing (3)
  • packages/rs-platform-wallet-ffi/src/dashpay.rs
  • packages/rs-platform-wallet/src/wallet/identity/network/payments.rs
  • packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift
📝 Walkthrough

Walkthrough

Adds a broadcast transaction fee (duffs) return value to the DashPay payment send path, propagated from the Rust wallet's send_payment through the C FFI (out_fee_duffs) and into the Swift SDK's sendDashPayPayment, which now returns a tuple with txid and feeDuffs. The example app is updated to match. Additionally, TestnetFaucet and TestnetFaucetOutcome visibility is promoted to public.

Changes

DashPay fee propagation

Layer / File(s) Summary
Rust send_payment returns fee
packages/rs-platform-wallet/src/wallet/identity/network/payments.rs
send_payment now captures the fee from build_signed and returns (txid, entry, fee) instead of (txid, entry).
FFI exposes out_fee_duffs
packages/rs-platform-wallet-ffi/src/dashpay.rs
platform_wallet_send_dashpay_payment adds an optional out_fee_duffs: *mut u64 parameter and writes fee_duffs when non-null.
Swift ManagedPlatformWallet returns fee tuple
packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/ManagedPlatformWallet.swift
sendDashPayPayment return type changes to (txid: Data, feeDuffs: UInt64), passing a feeDuffs output pointer into the FFI call.
Example app adapts to tuple return
packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DashPay/SendDashPayPaymentSheet.swift
Destructures the awaited tuple into (txid, _), discarding the fee.

TestnetFaucet public API

Layer / File(s) Summary
Faucet visibility promotion
packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift
TestnetFaucetOutcome, TestnetFaucet, webURL, init(session:), and requestCoreDash(address:) are changed from internal to public, with updated header documentation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SendDashPayPaymentSheet
  participant ManagedPlatformWallet
  participant FFI
  participant RustWallet

  SendDashPayPaymentSheet->>ManagedPlatformWallet: sendDashPayPayment(...)
  ManagedPlatformWallet->>FFI: platform_wallet_send_dashpay_payment(out_txid, out_fee_duffs)
  FFI->>RustWallet: send_payment(...)
  RustWallet-->>FFI: (txid, entry, fee)
  FFI-->>ManagedPlatformWallet: writes out_txid, out_fee_duffs
  ManagedPlatformWallet-->>SendDashPayPaymentSheet: (txid, feeDuffs)
Loading

Possibly related PRs

  • dashpay/platform#3985: Both PRs modify the DashPay send_payment send-path in payments.rs, one adding fee_duffs to the return value and the other changing broadcast/retry reservation-releasing behavior.

Suggested reviewers: shumkov, llbartekll, ZocoLini, thepastaclaw

🚥 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 summarizes the two main changes: exact payment fee reporting and the shared testnet faucet client.
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
  • Commit unit tests in branch feat/dashpay-payment-fee-and-faucet-client

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/rs-platform-wallet/src/wallet/identity/network/payments.rs (1)

539-542: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the # Returns doc comment to mention the fee.

The return type now includes u64 (network fee in duffs), but the doc comment still describes only the Txid and PaymentEntry. Callers reading the docs won't know about the third element.

📝 Proposed doc update
 /// # Returns
 ///
-/// The `Txid` of the broadcast transaction and the newly created
-/// [`PaymentEntry`] recording the outgoing payment.
+/// A tuple of the `Txid` of the broadcast transaction, the newly created
+/// [`PaymentEntry`] recording the outgoing payment, and the exact network
+/// fee in duffs (inputs − outputs) of the broadcast transaction.
🤖 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/payments.rs` around
lines 539 - 542, The `# Returns` doc comment for the payment/broadcast function
is out of date and does not mention the `u64` network fee in duffs. Update the
return documentation near the function that returns the `Txid`, `PaymentEntry`,
and fee so it clearly describes all three tuple elements, including the fee
amount and its units.
packages/rs-platform-wallet-ffi/src/dashpay.rs (1)

538-547: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Document out_fee_duffs in # Safety
Add the nullable-out-pointer contract for out_fee_duffs: callers may pass NULL to ignore the fee, and a non-null pointer must reference valid u64 storage.

🤖 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-ffi/src/dashpay.rs` around lines 538 - 547,
Update the safety contract for platform_wallet_send_dashpay_payment to
explicitly document out_fee_duffs as a nullable output pointer: callers may pass
NULL to ignore the fee, and any non-null value must point to valid writable u64
storage. Make sure the function’s # Safety section matches the existing
nullable-output conventions used by the other FFI parameters in dashpay.rs.
🤖 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.

Outside diff comments:
In `@packages/rs-platform-wallet-ffi/src/dashpay.rs`:
- Around line 538-547: Update the safety contract for
platform_wallet_send_dashpay_payment to explicitly document out_fee_duffs as a
nullable output pointer: callers may pass NULL to ignore the fee, and any
non-null value must point to valid writable u64 storage. Make sure the
function’s # Safety section matches the existing nullable-output conventions
used by the other FFI parameters in dashpay.rs.

In `@packages/rs-platform-wallet/src/wallet/identity/network/payments.rs`:
- Around line 539-542: The `# Returns` doc comment for the payment/broadcast
function is out of date and does not mention the `u64` network fee in duffs.
Update the return documentation near the function that returns the `Txid`,
`PaymentEntry`, and fee so it clearly describes all three tuple elements,
including the fee amount and its units.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f7123145-bd8d-4051-b361-f70822ba32bb

📥 Commits

Reviewing files that changed from the base of the PR and between aae79a8 and 57003a9.

📒 Files selected for processing (5)
  • packages/rs-platform-wallet-ffi/src/dashpay.rs
  • packages/rs-platform-wallet/src/wallet/identity/network/payments.rs
  • packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/ManagedPlatformWallet.swift
  • packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DashPay/SendDashPayPaymentSheet.swift

…fs contract

CodeRabbit review nits on #4049: send_payment's # Returns now covers
the third (fee) tuple element, and the FFI # Safety documents
out_fee_duffs as a nullable out-pointer written only on success.

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

Copy link
Copy Markdown
Member Author

Both CodeRabbit findings fixed in 9ca4736: send_payment's # Returns now documents the third (fee) tuple element, and the FFI # Safety documents out_fee_duffs as a nullable out-pointer (NULL to ignore; non-null must be valid writable u64 storage, written only on success — verified against the write site, which is only reached past unwrap_result_or_return!).

🤖 Addressed by Claude Code

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

Code Review

Source: reviewers: claude opus general (failed quota), codex gpt-5.5 general, claude opus security-auditor (failed quota), codex gpt-5.5 security-auditor, claude opus ffi-engineer (failed quota), codex gpt-5.5 ffi-engineer. Verifier: codex gpt-5.5 (Claude verifier failed quota).

Prior run at 57003a9 had no verified output, so there are no carried-forward findings. In the latest delta, the fee documentation/out-param contract is new and blocking because the returned value is still the builder's size-based fee, not the transaction's inputs-minus-outputs fee. Cumulatively, the promoted SDK faucet client has one in-scope hardening suggestion; the unrelated data-contract parser issue is real but outside PR #4049.

🔴 1 blocking | 🟡 1 suggestion(s)

🤖 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/payments.rs`:
- [BLOCKING] packages/rs-platform-wallet/src/wallet/identity/network/payments.rs:670-677: Return the actual transaction fee, not the builder's size fee
  `send_payment` now exposes this value through Rust, FFI, and Swift as the exact network fee of the broadcast transaction, but it forwards the second value from `TransactionBuilder::build_signed`. In the pinned `key-wallet` source, `build_signed` computes that value as `fee_rate.calculate_fee(encoded_size(tx))`; it does not compute `sum(selected inputs) - sum(tx outputs)`. These diverge when the builder omits a dust change output: the dust remainder is also paid to miners, while the returned value remains only the size-based fee. That makes the new public API under-report the fee in real sends and breaks the PR's stated contract.

In `packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift`:
- [SUGGESTION] packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift:264-266: Cap faucet proof-of-work by total expected work
  This PR promotes `TestnetFaucet.requestCoreDash` into the SDK as reusable public API, but the captcha bounds still allow a same-domain or compromised faucet endpoint to return `c=256,d=6`. That is roughly 4.3 billion expected SHA-256 attempts before the 30-second timeout cancels the solver, pinning device CPU for each user-initiated faucet attempt. Bound the aggregate challenge cost, not just each field independently.

Comment thread packages/rs-platform-wallet/src/wallet/identity/network/payments.rs Outdated
QuantumExplorer and others added 2 commits July 9, 2026 16:48
…e size fee

Review finding on #4049: build_signed's returned fee is size-based
(fee_rate x encoded_size) and under-reports whenever the builder drops
a dust change remainder (<= 546 duffs) to miners instead of emitting
the change output. send_payment now computes the fee it exposes as
sum(selected input values) - sum(output values), reading input values
from the account UTXO map (build reserves selected UTXOs but does not
remove them). A selected input missing from the map is a hard error,
never a fabricated fee.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review hardening on #4049: the per-field bounds still admitted
c=256, d=6 (~4.3B expected SHA-256 attempts) from a compromised or
misbehaving faucet endpoint — enough to pin the device CPU until the
30 s timeout on every faucet tap. Bound the aggregate expected work
(c x 16^d) at 64M attempts (~10x the live faucet's soft challenge);
costlier challenges throw and route to the web-faucet fallback.

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

Copy link
Copy Markdown
Member Author

Both findings from the changes-requested review are addressed:

  • [BLOCKING] size-based fee — this run reviewed the pre-fix delta; it was already fixed in a810120d67 (same finding as the earlier inline thread): send_payment computes the exposed fee as Σ(selected input values) − Σ(output values) from the account UTXO map, so a dropped dust change remainder is included. 29/29 payments-module tests pass.
  • [SUGGESTION] aggregate PoW cap — fixed in eafbfda4ab: the faucet client now bounds the aggregate expected work (c × 16^d ≤ 64M attempts, ~10× the live faucet's soft challenge) in addition to the per-field bounds, so a compromised/misbehaving endpoint can't pin the CPU until the 30 s timeout; costlier challenges throw and route to the web-faucet fallback.

🤖 Addressed by Claude Code

@shumkov shumkov requested a review from thepastaclaw July 10, 2026 06:30

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

Re-reviewed current head eafbfda. Both prior findings are fixed: send_payment now reports inputs minus outputs, including dropped dust change, and the faucet caps aggregate expected proof-of-work at 64M attempts. Wallet and Swift CI pass. No additional issues found.

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.

3 participants