fix: centralize validation and fee-reserve hygiene, clean up error banners#927
Conversation
- PROJ-001: enforce shared model validators in UI and backend paths - PROJ-002: reserve transfer and withdrawal fees with integer estimator results - PROJ-003: keep technical errors in banner details - PROJ-005: use named placeholders and complete translation units in scope - PROJ-006: route local identity removal through IdentityTask - RUST-001: replace fragile offsets and annotate upstream string matching Co-Authored-By: Codex Sol <noreply@openai.com>
|
Warning Review limit reached
Next review available in: 18 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (13)
📝 WalkthroughWalkthroughChangesThe PR adds shared Unicode-aware validation for DashPay fields, DPNS names, token keywords, and wallet aliases; moves identity removal into backend task execution; adds fee-safe spend calculations; preserves legacy wallet metadata during migration; and standardizes UI error banners with attached diagnostic details. Validation and persistence
Identity lifecycle
UI error and flow updates
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant UI
participant BackendTask
participant Storage
User->>UI: Confirm identity removal
UI->>BackendTask: RemoveIdentity(identity_id)
BackendTask->>Storage: Delete identity and associated voter
Storage-->>BackendTask: Cleanup status
BackendTask-->>UI: RemovedIdentities result
UI-->>User: Show success or cleanup warning
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
…-fee-hygiene # Conflicts: # src/ui/wallets/wallets_screen/mod.rs
|
🕓 Ready for review — 9 ahead in queue (commit 4acdd53) |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/backend_task/identity/remove_identity.rs (1)
11-19: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid loading every local identity just to find one association.
load_local_qualified_identities()deserializes the full set before filtering to one record; a direct lookup would be cheaper here.🤖 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 `@src/backend_task/identity/remove_identity.rs` around lines 11 - 19, Update the lookup in the remove-identity flow to use a direct identity-specific retrieval instead of calling load_local_qualified_identities() and scanning the full collection. Preserve the existing associated_voter_identity ID extraction and optional behavior, using the repository’s existing single-identity lookup API.src/ui/wallets/single_key_send_screen.rs (1)
423-424: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winInline duffs/credits→DASH conversions duplicate the centralized
fee_estimation.rshelpers.Both files convert duffs/credits to DASH via raw
* 1e-8// 100_000_000_000.0arithmetic instead of the existingformat_duffs_as_dash/format_credits_as_dashhelpers inmodel/fee_estimation.rs— helpers this same PR already uses correctly elsewhere (single_key_send_screen.rsL695, L936-953). This duplicates fee-unit conversion logic across files and risks display drift (e.g. floating-point artifacts in the rawf64costvalue intoken_creator.rs).
src/ui/wallets/single_key_send_screen.rs#L423-L424: replacefee_dash = estimated_fee as f64 * 1e-8withformat_duffs_as_dash(estimated_fee).src/ui/wallets/single_key_send_screen.rs#L516-L521: replace the same inline conversion inrender_simple_sendwithformat_duffs_as_dash(estimated_fee).src/ui/wallets/single_key_send_screen.rs#L564-L566: replacefee_dash = self.fee_dialog.estimated_fee as f64 * 1e-8withformat_duffs_as_dash(self.fee_dialog.estimated_fee).src/ui/wallets/single_key_send_screen.rs#L579-L581: replacefee_dash = self.fee_dialog.required_fee as f64 * 1e-8withformat_duffs_as_dash(self.fee_dialog.required_fee).src/ui/wallets/single_key_send_screen.rs#L599-L600: replacefee_dash = fee_diff as f64 * 1e-8withformat_duffs_as_dash(fee_diff).src/ui/tokens/tokens_screen/token_creator.rs#L1405-L1443: replacelet cost = self.estimate_registration_cost() as f64 / 100_000_000_000.0;withlet cost = format_credits_as_dash(self.estimate_registration_cost());and drop the now-redundantDashsuffix or unit formatting in the message.🤖 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 `@src/ui/wallets/single_key_send_screen.rs` around lines 423 - 424, Replace duplicated fee-unit arithmetic with the centralized helpers: in src/ui/wallets/single_key_send_screen.rs at lines 423-424, 516-521, 564-566, 579-581, and 599-600, use format_duffs_as_dash for each corresponding fee value; in src/ui/tokens/tokens_screen/token_creator.rs at lines 1405-1443, use format_credits_as_dash for the registration cost and remove the redundant Dash suffix or unit formatting from the message.Source: Coding guidelines
🤖 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 `@src/backend_task/error.rs`:
- Around line 2646-2647: Remove the shielded-build error-text marker parsing and
the AnchorMismatch workaround in the surrounding error-handling flow. Replace
this classification with a typed SDK error path or a local amount-plus-fee
pre-check, preserving actionable balance and anchor error handling while routing
unrelated failures through the generic build-error path.
In `@src/ui/tokens/tokens_screen/token_creator.rs`:
- Around line 1238-1240: Update the duplicate-language error construction in the
token creator flow to interpolate name_with_language.2, the TokenNameLanguage
value already used by the duplicate check, instead of name_with_language.1. Keep
the existing error text and duplicate-detection logic unchanged.
In `@src/ui/wallets/wallets_screen/mod.rs`:
- Around line 2642-2658: Replace the synchronous wallet_meta read/write in the
wallet rename flow with a typed backend rename task. Have the task distinguish a
missing metadata row from storage failures, preserve existing metadata fields,
and only update the in-memory alias after the task succeeds. Use the existing
task dispatch/result handling conventions around this wallet rename path.
---
Nitpick comments:
In `@src/backend_task/identity/remove_identity.rs`:
- Around line 11-19: Update the lookup in the remove-identity flow to use a
direct identity-specific retrieval instead of calling
load_local_qualified_identities() and scanning the full collection. Preserve the
existing associated_voter_identity ID extraction and optional behavior, using
the repository’s existing single-identity lookup API.
In `@src/ui/wallets/single_key_send_screen.rs`:
- Around line 423-424: Replace duplicated fee-unit arithmetic with the
centralized helpers: in src/ui/wallets/single_key_send_screen.rs at lines
423-424, 516-521, 564-566, 579-581, and 599-600, use format_duffs_as_dash for
each corresponding fee value; in src/ui/tokens/tokens_screen/token_creator.rs at
lines 1405-1443, use format_credits_as_dash for the registration cost and remove
the redundant Dash suffix or unit formatting from the message.
🪄 Autofix (Beta)
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
Run ID: 62e7751a-a9e0-4c8b-979c-ea1f7f7cc460
📒 Files selected for processing (43)
src/backend_task/dashpay/contact_requests.rssrc/backend_task/dashpay/payments.rssrc/backend_task/error.rssrc/backend_task/identity/mod.rssrc/backend_task/identity/register_dpns_name.rssrc/backend_task/identity/remove_identity.rssrc/backend_task/migration/finish_unwire.rssrc/backend_task/mod.rssrc/backend_task/tokens/mod.rssrc/model/dashpay.rssrc/model/dpns.rssrc/model/fee_estimation.rssrc/model/mod.rssrc/model/token.rssrc/model/validation.rssrc/model/wallet/mod.rssrc/ui/contracts_documents/contracts_documents_screen.rssrc/ui/contracts_documents/document_action_screen.rssrc/ui/contracts_documents/group_actions_screen.rssrc/ui/dashpay/add_contact_screen.rssrc/ui/dashpay/contact_profile_viewer.rssrc/ui/dashpay/contacts_list.rssrc/ui/dashpay/qr_scanner.rssrc/ui/dashpay/send_payment.rssrc/ui/dpns/dpns_contested_names_screen.rssrc/ui/helpers.rssrc/ui/identities/identities_screen.rssrc/ui/identities/keys/add_key_screen.rssrc/ui/identities/keys/key_info_screen.rssrc/ui/identities/register_dpns_name_screen.rssrc/ui/identities/transfer_screen.rssrc/ui/identities/withdraw_screen.rssrc/ui/masternodes/detail_screen.rssrc/ui/tokens/tokens_screen/mod.rssrc/ui/tokens/tokens_screen/my_tokens.rssrc/ui/tokens/tokens_screen/token_creator.rssrc/ui/tools/grovestark_screen.rssrc/ui/tools/transition_visualizer_screen.rssrc/ui/wallets/import_mnemonic_screen.rssrc/ui/wallets/single_key_send_screen.rssrc/ui/wallets/wallets_screen/mod.rssrc/wallet_backend/single_key.rssrc/wallet_backend/wallet_meta.rs
There was a problem hiding this comment.
Claudius reviews PR #927 — "centralize validation and fee-reserve hygiene"
I dispatched my crew across this one — security (Smythe), project-consistency (Adams), and adversarial QA (Marvin) — and then, because I trust no one, verified the sharp claims myself. The verdict: this is a genuinely good PR. Validation is really centralized in model/ with backend tasks as the authoritative enforcement layer (no bypass path found), the hardcoded float Max reserves are gone in favor of the integer-credit estimator, remove_identity moving off the render pass actually fixes a pre-existing correctness bug, and the banner cleanup stops raw SDK text from leaking to users. Credit where due.
Two MEDIUM items worth your attention (posted inline):
- Orphaned encrypted seed (
wallet_meta.rs) — the new alias validation runs after the HD seed envelope is already written, so an overlong alias leaves secret material stranded in the vault. Amusingly, the single-key path you added in this same PR does it correctly — just mirror it on the HD path. - Developer-tool diagnostics (
transition_visualizer_screen.rs/grovestark_screen.rs) — the Everyday-User jargon rule got applied to two developer tools, burying the exact parse errors they exist to surface behind an off-by-defaultdebug!log.
Also verified — a false alarm: a reviewer flagged a rustfmt gate failure in dpns_contested_names_screen.rs. I ran cargo fmt --all -- --check myself: clean, exit 0. No action needed there.
The remaining 10 findings are LOW/INFO polish (byte-vs-codepoint validation semantics worth confirming against the DPP schema, an unused InvalidDpnsName detail field, an unannounced rename-truncation UX change, a couple of naming/placement nits, one stale QA doc). Full severity-ranked report with reproductions attached to the CI artifacts.
No CRITICAL or HIGH. Address the two MEDIUM items and this is ready.
🤖 Co-authored by Claudius the Magnificent AI Agent
📊 View full HTML review report
register_wallet ran validate_wallet_alias inside write_wallet_meta, which fires AFTER write_seed_envelope. An overlong HD alias therefore failed the meta write only after the encrypted seed was already resident in the vault, leaving an orphaned seed with no meta row (never hydrated, no cleanup path). Hoist the alias validation to the top of register_wallet, before any secret-critical write, mirroring the single-key import path. Adds a regression test asserting no raw-seed entry survives a rejected HD registration (RED against the buggy write order). Co-Authored-By: Claude Opus <noreply@anthropic.com>
The duplicate-token-name check keys on name_with_language.2 (the TokenNameLanguage) but the error message interpolated name_with_language.1 (the plural-name String), so users saw a nonsensical value and could not tell which language was duplicated. Interpolate .2 to match the check and the sibling name-length messages. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The Transition Visualizer is a Platform-Developer tool whose purpose is diagnosing why a state transition fails to parse. The unified error-message policy (aimed at the Everyday-User banner flows) had genericized these messages and routed the concrete error only to tracing::debug! (off by default) — and the inline parse_error label has no details panel, so the diagnostic was unreachable in the UI at default verbosity. Append the concrete error to each inline message (byte-list, hex/base64 decode, state-transition deserialize, JSON serialize) so the developer sees the actual failure without restarting under RUST_LOG=debug. GroveSTARK already preserves its error via .with_details() and is left unchanged. Co-Authored-By: Claude Opus <noreply@anthropic.com>
remove_identity loaded and deserialized every local qualified identity just to find the one being removed and read its associated voter id. Use the direct get_local_qualified_identity(&id) lookup instead — same extraction, no full-collection scan. Co-Authored-By: Claude Opus <noreply@anthropic.com>
|
Triage on the two nitpicks from the review body:
Inline duffs/credits→DASH vs |
…alidation-fee-hygiene # Conflicts: # src/backend_task/identity/register_dpns_name.rs # src/backend_task/mod.rs # src/ui/identities/register_dpns_name_screen.rs
…olds Ports the remaining piece of #622 (fix/integer-max-amount by thepastaclaw) that this branch's PROJ-002 hadn't covered yet: the Withdraw/Transfer button enable/disable thresholds in the identities list's action popup still used hardcoded f64-derived credit constants. Switches them to self.app_context.fee_estimator(), matching the pattern already used in transfer_screen.rs and withdraw_screen.rs on this branch. Also drops the amount from the disabled-button tooltip text since it no longer matches a single hardcoded figure. With this, PR #927 fully supersedes #622, which is being closed. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Folded in the last piece of #622 (fix/integer-max-amount, thepastaclaw) that this PR's 🤖 Co-authored by Claudius the Magnificent AI Agent |
There was a problem hiding this comment.
Claudius returns to PR #927 — fresh pass on 2d6abbd3
I sent the crew back in — Smythe (security), Adams (consistency/structure), Marvin (adversarial QA) — over the current head, which has moved on since my last visit (the two MEDIUMs from that round, the orphaned encrypted seed and the buried developer-tool diagnostics, are both fixed and resolved). Then, because I trust no one — least of all my own agents — I verified the sharp claims myself.
Verdict: no CRITICAL, HIGH, or MEDIUM findings. Approving.
Marvin dutifully surfaced two MEDIUMs; I personally ran both to ground and downgraded both to LOW:
- Withdraw screen missing a zero/amount+fee guard that
TransferScreenhas — real inconsistency, but not the fund bug it first appears to be:AmountInputalready enforces itsmax_amount(amount_input.rs:300-312— over-max input is rejected and the button disabled), soamount + fee ≤ balancealready holds via the widget. The residual is a 0-credit withdrawal reaching the backend (which rejects it) plus missing defense-in-depth — and it's pre-existing, not a regression. remove_identityshipped untested — a genuine test gap, but the primary delete is a verbatim move of pre-existing logic on a local-DB-only, re-syncable path; only the voter-cleanup branch is new. Worth a regression test, not a blocker.
What I verified holds up beautifully: the orphaned-secret invariant is genuinely closed — alias validated before write_seed_envelope, with a regression test that goes RED against the old ordering. max_spendable_credits is a clean saturating u64 subtraction that kills the old f64 reserve drift. Validators are single shared functions with no UI/backend split-brain. And the suspected duplicate format_credits_as_dash hazard? Not present — one definition, imported everywhere. Credit where it's due: this is tidy work.
The remaining 7 LOW/INFO items are polish — a token name/plural byte-vs-char divergence worth tightening, an unused InvalidDpnsName field, a const fn wedged into the use block, a misleading closure name, and the orphaned-voter warning whose "retry after restarting" advice has no actual path. Full severity-ranked HTML report is attached to the CI artifacts.
One open thread remains — CodeRabbit's HD/single-key rename-persistence refactor — correctly left deferred by mutual agreement for a coordinated follow-up. Not a blocker.
Ship it.
🤖 Co-authored by Claudius the Magnificent AI Agent
📊 View full HTML review report
TL;DR: Centralizes name/memo/keyword/label validation and fee-reserve math so they behave the same everywhere they're triggered, cleans up error banners to stop leaking raw technical text, routes identity removal through the app's task system, and hardens a fragile shielded-fee error parser.
User story
As a Dash Evo Tool user, I want fee calculations and input validation to behave consistently everywhere I trigger them, and error messages to be plain and actionable, so that I don't lose funds to a mismatched fee reserve or get stuck reading jargon I can't act on.
Scenario
Base flow
A user registers a DPNS name, sends a memo, creates a token, renames a wallet or contact, or presses "Max" on a transfer/withdrawal screen.
Actual behavior
Expected behavior
model/and is enforced by both UI and backend paths, so every caller gets the same rules.Detailed discussion
What was done
model/dpns.rsand sibling model modules; backend tasks now enforce it, UI delegates instead of reimplementing.model/fee_estimation.rs's credit estimators, computed in integer credits.MessageBannercall sites; technical detail now attaches via banner details instead of the message text.format!placeholders in the primary touched UI files to named placeholders and replaced the two known fragment-concatenation/comma-splice sites with complete sentences. The full repo-wide sweep (431 sites total) is intentionally out of scope here and left for incremental follow-up as files are touched.Testing
cargo fmt --all— passcargo test --lib --all-features— 2049 passed, 0 failed, 1 ignoredcargo clippy --lib --all-features -- -D warnings— cleanBreaking changes
None.
Checklist
cargo fmt --allcargo clippycleanPrior work
Findings sourced from a whole-project review (
full-project-review-fable.md, 2026-07-22), base commit 81fddd0. Companion PR: SendScreen test coverage from the same review pass.Attribution
🤖 Co-authored by Claudius the Magnificent AI Agent
Summary by CodeRabbit