Skip to content

fix(wallet)!: non-xtr resource fixes, support for badges - #1615

Merged
sdbondi merged 2 commits into
tari-project:developmentfrom
sdbondi:wallet-badge-usage
Oct 27, 2025
Merged

fix(wallet)!: non-xtr resource fixes, support for badges#1615
sdbondi merged 2 commits into
tari-project:developmentfrom
sdbondi:wallet-badge-usage

Conversation

@sdbondi

@sdbondi sdbondi commented Oct 24, 2025

Copy link
Copy Markdown
Member

Description

fix(wallet)!: non-xtr resource fixes, support for badges
feat(consensus): setting to disable eviction proposals
fix(wallet): bug in transfers where the view key (Elgamal proof) was used with fee outputs (XTR) causing the transaction to be rejected.
fix: web ui fixes
feat: add page for utxo decryption with view key

Motivation and Context

The badge option for stealth transfers did nothing and was not supported by the API.
Fixes from testing with stable coin transfers.

How Has This Been Tested?

Manually

What process can a PR reviewer use to test or verify this change?

Breaking Changes

  • None
  • Requires data directory to be deleted
  • Other - Please specify

Summary by CodeRabbit

  • New Features

    • Added "Decrypt UTXO Balance" feature in settings to decrypt and display stealth UTXO balances.
    • Introduced support for multiple badge usage types in transfer operations (Resource, NonFungible, AmountOfResource).
    • Added CLI command to generate viewable balance keys.
  • Improvements

    • Made the fee field editable in the transfer form.
    • Enhanced stealth transfer with badge usage support.
    • Added configuration option for consensus eviction proposals in validator nodes.

@coderabbitai

coderabbitai Bot commented Oct 24, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This pull request introduces badge usage support for stealth transfers, implements eviction proposal configuration for the consensus layer, adds a decrypt UTXO balance UI feature, includes async blocking refactoring, and implements a new CLI command for viewable balance keys.

Changes

Cohort / File(s) Summary
Validator Node Eviction Configuration
applications/tari_app_utilities/config_presets/c_validator_node.toml, applications/tari_validator_node/src/bootstrap.rs, applications/tari_validator_node/src/config.rs, applications/tari_validator_node/src/consensus/mod.rs, crates/consensus/src/hotstuff/config.rs, crates/consensus/src/hotstuff/on_propose.rs, crates/consensus_tests/src/support/harness.rs
Added enable_eviction_proposal configuration field to ConsensusConfig and HotstuffConfig. Propagated through bootstrap and consensus spawn functions. Config defaults to true; preset disables evictions. Guard added to on_propose.rs to conditionally skip eviction calculations based on config flag.
Badge Usage Type System & Bindings
bindings/src/types/wallet-daemon-client/BadgeUsage.ts, bindings/src/types/wallet-daemon-client/StealthTransferRequest.ts, bindings/src/wallet-daemon-client.ts, clients/wallet_daemon_client/src/types.rs
Introduced new BadgeUsage discriminated union type supporting None, Resource, NonFungible, and AmountOfResource variants. Added badge_usage field to StealthTransferRequest. Exported types from wallet-daemon-client module.
Stealth Transfer SDK & Error Handling
crates/wallet/sdk/src/apis/stealth_transfer/params.rs, crates/wallet/sdk/src/apis/stealth_transfer/error.rs, crates/wallet/sdk/src/apis/stealth_transfer/api.rs
Added BadgeUsage enum to params with helper methods. Extended error variants for badge vault not found and invariant violations. Refactored stealth_transfer/api.rs with badge handling, block_in_place wrapping for I/O, and consolidated input/output/signer logic.
Wallet Daemon RPC & Handlers
applications/tari_walletd/src/handlers/accounts.rs, applications/tari_walletd/src/handlers/stealth_utxos.rs, applications/tari_walletd/web_ui/src/utils/json_rpc.ts
Updated stealth transfer handler to include badge_usage from request. Replaced block_in_place with tokio::task::spawn_blocking for I/O operations in stealth_utxos.rs. Added stealthDecryptUtxoBalance RPC wrapper.
Web UI Token Transfer & Fee Form
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx, applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx, applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts
Replaced badge field with badge_usage in transfer payload. Updated TransferParams interface to use BadgeUsage instead of string. Removed disabled attribute from Fee TextField. Updated error handling for rejection reasons.
Web UI Decrypt UTXO Balance
applications/tari_walletd/web_ui/src/routes/Settings/Components/DecryptUtxoBalance.tsx, applications/tari_walletd/web_ui/src/routes/Settings/Settings.tsx
New React component for decrypting UTXO balances with form inputs for resource address, UTXO ID, and expected value bounds. Added new Settings tab to host the decrypt component.
CLI & Application Entry Point
applications/tari_walletd/src/cli.rs, applications/tari_walletd/src/main.rs
Added NewViewableBalanceKey CLI subcommand with key_index and output_path parameters. Implemented handler to generate and persist encrypted view keys with public/private key output.
Miscellaneous Updates
bindings/src/types/tari-indexer-client/IndexerGetIdentityResponse.ts, crates/template_builtin/templates/account/src/lib.rs, integration_tests/src/wallet_daemon_client.rs
Simplified Array to string[] syntax in IndexerGetIdentityResponse. Added create_proof_by_non_fungible convenience method to Account template. Updated integration test with BadgeUsage import.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as SendMoney UI
    participant Handler as Stealth Transfer Handler
    participant SDK as Wallet SDK
    participant Store as Wallet Store

    User->>UI: Initiate stealth transfer with optional badge
    UI->>UI: Construct badge_usage (Resource variant or None)
    UI->>Handler: Send transfer request with badge_usage
    Handler->>Handler: Extract badge from badge_usage
    Handler->>SDK: Call stealth_transfer with badge_usage
    
    SDK->>SDK: Parse badge_usage variant
    alt Badge Usage Present
        SDK->>Store: Fetch badge vault for resource
        SDK->>SDK: Generate badge proof
        SDK->>SDK: Add badge vault to inputs
    else No Badge
        SDK->>SDK: Skip badge handling
    end
    
    SDK->>SDK: Compute fee with fee inputs
    SDK->>SDK: Collect transfer inputs/outputs
    SDK->>SDK: Build and sign transfer statement
    SDK->>Store: Submit transaction
    SDK-->>Handler: Return result
    Handler-->>UI: Return transfer response
    UI-->>User: Display result
Loading
sequenceDiagram
    participant Bootstrap
    participant Config as Validator Config
    participant Consensus as Consensus Module
    participant Hotstuff as Hotstuff Engine

    Bootstrap->>Config: Load validator node config
    Config->>Config: Parse ConsensusConfig with enable_eviction_proposal
    Bootstrap->>Consensus: spawn(..., consensus_config)
    Consensus->>Hotstuff: Create HotstuffConfig with enable_eviction_proposal flag
    
    loop Block Proposal
        Hotstuff->>Hotstuff: Check enable_eviction_proposal flag
        alt Flag Enabled
            Hotstuff->>Hotstuff: Calculate evictions
            Hotstuff->>Hotstuff: Add eviction nodes to proposal
        else Flag Disabled
            Hotstuff->>Hotstuff: Skip eviction calculations
        end
        Hotstuff->>Hotstuff: Finalize proposal
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Primary concern: stealth_transfer/api.rs — Dense logic refactoring with extensive badge handling, signer resolution, fee flow, and I/O blocking changes requiring careful line-by-line verification.
  • Secondary concern: useAccounts.ts & SendMoney.tsx — Cross-layer integration of badge_usage type; multiple conditional branches for Resource extraction need validation.
  • Tertiary concern: main.rs — NewViewableBalanceKey handler involves file I/O, key derivation, and JSON serialization; verify error handling paths and key index resolution logic.
  • Configuration propagation — Trace enable_eviction_proposal from config file through bootstrap, consensus spawn, and on_propose guard to ensure correct plumbing (generally lower complexity).

Possibly related PRs

Poem

🐰 With badges now guiding the stealth transfer way,
And keys viewable—balance in decrypt's display,
Evictions configured, the validator stands tall,
While tasks spawn in threads—no more blocking calls,
A rabbit's delight: form fields, now editable for all!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "fix(wallet)!: non-xtr resource fixes, support for badges" accurately captures the main changes in the changeset. The title correctly references two primary objectives from the PR description: (1) non-XTR resource fixes addressing a bug where the view key was incorrectly used with fee outputs, visible in the extensive stealth_transfer API refactoring and fee handling changes, and (2) support for badges, which is evidenced by the introduction of the BadgeUsage type across multiple wallet components, configuration updates to StealthTransferParams, and changes throughout the wallet daemon handlers. While the changeset includes additional work such as consensus eviction proposal configuration, web UI enhancements, and new CLI commands, these appear to be secondary features testing fixes rather than the main drivers of the PR, making the wallet-scoped title appropriate and sufficiently descriptive.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

@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: 5

🧹 Nitpick comments (11)
crates/template_builtin/templates/account/src/lib.rs (1)

181-183: Consider emitting an event for consistency.

The implementation is correct. However, for consistency with other proof creation methods in this impl block (lines 175-179, 185-196, 198-205), consider emitting an event before delegating:

 pub fn create_proof_by_non_fungible(&mut self, nft: NonFungibleAddress) -> Proof {
+    emit_event("create_proof_by_non_fungible", [
+        ("resource", nft.resource_address().to_string()),
+        ("id", nft.id().to_string()),
+    ]);
     self.create_proof_by_non_fungible_ids(*nft.resource_address(), vec![nft.id().clone()])
 }

This improves observability by clearly indicating which API entry point was invoked.

applications/tari_walletd/web_ui/src/routes/Settings/Components/DecryptUtxoBalance.tsx (3)

41-53: Type the response and handle BigInt/JSON-RPC errors.

Avoid any; add proper type and error handling to prevent crashes on bad input.

-  const [balance, setBalance] = useState<any>(null);
+  import type { StealthUtxosDecryptValueResponse } from "@tari-project/typescript-bindings";
+  const [balance, setBalance] = useState<StealthUtxosDecryptValueResponse | null>(null);
+  const [error, setError] = useState<string | null>(null);
@@
-  const onViewBalanceClicked = async () => {
-    const resp = await stealthDecryptUtxoBalance({
+  const onViewBalanceClicked = async () => {
+    try {
+      const resp = await stealthDecryptUtxoBalance({
         resource_address: formState.resourceAddress!,
         ids: [formState.utxoId!],
         minimum_expected_value: formState.minimumExpectedValue ? BigInt(formState.minimumExpectedValue) : null,
         maximum_expected_value: formState.maximumExpectedValue ? BigInt(formState.maximumExpectedValue) : null,
         view_key_id: BigInt(formState.keyId),
-    } as StealthUtxosDecryptValueRequest);
-
-    setBalance(resp);
+      } as StealthUtxosDecryptValueRequest);
+      setError(null);
+      setBalance(resp);
+    } catch (e: any) {
+      setBalance(null);
+      setError(e?.message ?? "Failed to decrypt UTXO balance");
+    }
   };

55-66: Don’t treat 0 as failure; fix message.

Use nullish coalescing and correct the text.

-            {key}: {balance.balances[key] || "Failed not decrypt value"}
+            {key}: {balance.balances[key] ?? "Failed to decrypt value"}

67-72: Guard required fields before decrypt.

Prevent null utxoId submission and accidental BigInt errors by gating the button.

-  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
     setFormState({
       ...formState,
       [e.target.name]: e.target.value,
     });
   };
@@
-  <Button variant="contained" onClick={onViewBalanceClicked} disabled={!formState.resourceAddress}>
+  <Button
+    variant="contained"
+    onClick={onViewBalanceClicked}
+    disabled={!formState.resourceAddress || !formState.utxoId}
+  >

Also applies to: 101-104

applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx (3)

200-200: badge_usage construction: OK, but tighten types and avoid casts.

  • Current: { Resource: transferFormState.badge } or "None" as BadgeUsage.
  • Suggest: model badges as ResourceAddress[] and type transferFormState.badge as ResourceAddress to drop the cast and catch mismatches at compile time. Also consider supporting other BadgeUsage variants when UI exposes them.

Example diff (types only; adjust where defined):

-  const badges = ... as string[];
+  const badges: ResourceAddress[] = ...
...
-  badge_usage: transferFormState.badge ? { Resource: transferFormState.badge } : ("None" as BadgeUsage),
+  badge_usage: transferFormState.badge ? { Resource: transferFormState.badge } : "None",

Also applies to: 275-277


211-216: Result discriminants: verify shapes and improve reason extraction.

  • "Reject" key looks correct per new bindings, but confirm the exact casing.
  • AcceptFeeRejectRest indexing assumes tuple shape; guard against object-shaped variants to avoid undefined messages.

Minimal defensive tweak:

- throw new Error(`Transaction rejected: ${rejectReasonToString(transactionResult.AcceptFeeRejectRest[1])}`);
+ const afr = transactionResult.AcceptFeeRejectRest;
+ const reason = Array.isArray(afr) ? afr[1] : (afr?.reason ?? afr?.Reject ?? null);
+ throw new Error(`Transaction rejected: ${rejectReasonToString(reason)}`);

178-206: Duplicate payload assembly between estimate and confirm.

Build the transfer payload once to avoid drift (e.g., future BadgeUsage variants or flags). Extract a helper buildTransferPayload() used by both paths.

Also applies to: 265-284

crates/wallet/sdk/src/apis/stealth_transfer/api.rs (4)

30-31: OK to import block_in_place; verify runtime flavor.

block_in_place requires the multi-threaded Tokio runtime. Confirm walletd runs with the multi-thread flavor to avoid stalls. If not guaranteed, prefer spawn_blocking or reduce the blocking section.


347-355: Scope of block_in_place is large; shrink the blocking critical section.

Only wrap truly blocking CPU/IO work and leave pure assembly outside to improve scheduler fairness. Consider moving lock creation + UTXO/vault locking into block_in_place and keep statement assembly outside.


550-571: Inputs for badges and UTXOs: consider dedup and completeness.

  • You add badge_vault.id via substate_inputs and badge resource/NFT via builder.add_input(...). Ensure no duplicate input entries and that resource substates required by create_proof_* are always included for all BadgeUsage variants. A small set-based dedup on inputs can prevent redundant entries.

Also applies to: 572-583


843-846: Extra add_input(XTR): verify necessity.

The TODO suggests this is a workaround. If still required, document why (metadata lookup, template dependency) and consider moving to builder-level defaults to avoid leaking into callsites.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d80aae4 and b2d12be.

📒 Files selected for processing (26)
  • applications/tari_app_utilities/config_presets/c_validator_node.toml (1 hunks)
  • applications/tari_validator_node/src/bootstrap.rs (1 hunks)
  • applications/tari_validator_node/src/config.rs (3 hunks)
  • applications/tari_validator_node/src/consensus/mod.rs (2 hunks)
  • applications/tari_walletd/src/handlers/accounts.rs (1 hunks)
  • applications/tari_walletd/src/handlers/stealth_utxos.rs (2 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx (4 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx (0 hunks)
  • applications/tari_walletd/web_ui/src/routes/Settings/Components/DecryptUtxoBalance.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/Settings/Settings.tsx (2 hunks)
  • applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts (5 hunks)
  • applications/tari_walletd/web_ui/src/utils/json_rpc.ts (2 hunks)
  • bindings/src/types/tari-indexer-client/IndexerGetIdentityResponse.ts (1 hunks)
  • bindings/src/types/wallet-daemon-client/BadgeUsage.ts (1 hunks)
  • bindings/src/types/wallet-daemon-client/StealthTransferRequest.ts (1 hunks)
  • bindings/src/wallet-daemon-client.ts (1 hunks)
  • clients/wallet_daemon_client/src/types.rs (2 hunks)
  • crates/consensus/src/hotstuff/config.rs (1 hunks)
  • crates/consensus/src/hotstuff/on_propose.rs (1 hunks)
  • crates/consensus_tests/src/support/harness.rs (1 hunks)
  • crates/engine_types/src/crypto/elgamal.rs (1 hunks)
  • crates/template_builtin/templates/account/src/lib.rs (1 hunks)
  • crates/wallet/sdk/src/apis/stealth_transfer/api.rs (5 hunks)
  • crates/wallet/sdk/src/apis/stealth_transfer/error.rs (2 hunks)
  • crates/wallet/sdk/src/apis/stealth_transfer/params.rs (3 hunks)
  • integration_tests/src/wallet_daemon_client.rs (2 hunks)
💤 Files with no reviewable changes (1)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx
🧰 Additional context used
🧬 Code graph analysis (12)
integration_tests/src/wallet_daemon_client.rs (4)
crates/engine/src/runtime/mod.rs (1)
  • stealth_transfer (203-208)
crates/engine/src/transaction/processor.rs (1)
  • stealth_transfer (373-396)
crates/engine/src/runtime/impl.rs (1)
  • stealth_transfer (2760-2776)
bindings/src/types/wallet-daemon-client/BadgeUsage.ts (1)
  • BadgeUsage (6-10)
crates/wallet/sdk/src/apis/stealth_transfer/error.rs (1)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts (1)
bindings/src/types/wallet-daemon-client/BadgeUsage.ts (1)
  • BadgeUsage (6-10)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx (2)
bindings/src/types/wallet-daemon-client/BadgeUsage.ts (1)
  • BadgeUsage (6-10)
bindings/src/helpers/helpers.ts (1)
  • rejectReasonToString (121-164)
bindings/src/types/wallet-daemon-client/BadgeUsage.ts (3)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
bindings/src/types/NonFungibleAddress.ts (1)
  • NonFungibleAddress (7-7)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
applications/tari_walletd/web_ui/src/routes/Settings/Components/DecryptUtxoBalance.tsx (2)
applications/tari_walletd/web_ui/src/utils/json_rpc.ts (1)
  • stealthDecryptUtxoBalance (313-315)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueRequest.ts (1)
  • StealthUtxosDecryptValueRequest (5-11)
clients/wallet_daemon_client/src/types.rs (1)
bindings/src/types/wallet-daemon-client/BadgeUsage.ts (1)
  • BadgeUsage (6-10)
applications/tari_walletd/web_ui/src/utils/json_rpc.ts (2)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueRequest.ts (1)
  • StealthUtxosDecryptValueRequest (5-11)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueResponse.ts (1)
  • StealthUtxosDecryptValueResponse (4-4)
bindings/src/types/wallet-daemon-client/StealthTransferRequest.ts (1)
bindings/src/types/wallet-daemon-client/BadgeUsage.ts (1)
  • BadgeUsage (6-10)
crates/wallet/sdk/src/apis/stealth_transfer/params.rs (4)
bindings/src/types/NonFungibleAddress.ts (1)
  • NonFungibleAddress (7-7)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
bindings/src/types/wallet-daemon-client/BadgeUsage.ts (1)
  • BadgeUsage (6-10)
applications/tari_walletd/src/handlers/stealth_utxos.rs (1)
crates/engine_types/src/crypto/elgamal.rs (1)
  • lookup (281-286)
crates/wallet/sdk/src/apis/stealth_transfer/api.rs (3)
crates/wallet/sdk/src/apis/stealth_outputs.rs (1)
  • params (675-687)
bindings/src/types/wallet-daemon-client/BadgeUsage.ts (1)
  • BadgeUsage (6-10)
crates/transaction/src/builder/mod.rs (2)
  • None (199-199)
  • None (227-227)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: test
  • GitHub Check: check nightly
  • GitHub Check: file licenses
  • GitHub Check: machete
  • GitHub Check: check stable
  • GitHub Check: fmt
  • GitHub Check: clippy
🔇 Additional comments (30)
crates/consensus/src/hotstuff/on_propose.rs (1)

527-546: LGTM!

The eviction proposal guard is correctly implemented. When enable_eviction_proposal is false, the filter returns None, bypassing the eviction node calculation and resulting in an empty list via unwrap_or_default().

crates/consensus/src/hotstuff/config.rs (1)

18-18: LGTM!

The new configuration field is correctly added to HotstuffConfig.

crates/consensus_tests/src/support/harness.rs (1)

656-656: LGTM!

Enabling eviction proposals by default in tests is appropriate to ensure the full consensus behavior is tested.

applications/tari_validator_node/src/consensus/mod.rs (1)

47-47: LGTM!

The consensus configuration is correctly imported and wired through to the HotstuffConfig.

Also applies to: 53-53, 84-84

applications/tari_validator_node/src/bootstrap.rs (1)

332-332: LGTM!

The consensus configuration is correctly passed to consensus::spawn.

applications/tari_validator_node/src/config.rs (2)

175-190: LGTM, but note the critical issue in the TOML preset.

The ConsensusConfig struct is well-defined with clear documentation. The field name is enable_eviction_proposal (singular), which should match the TOML configuration key. However, the TOML preset in applications/tari_app_utilities/config_presets/c_validator_node.toml uses the plural form, which will cause a deserialization error or silent failure.


110-110: LGTM!

The consensus configuration field is correctly added to ValidatorNodeConfig and initialized with sensible defaults.

Also applies to: 164-164

bindings/src/types/tari-indexer-client/IndexerGetIdentityResponse.ts (1)

7-7: No action required — change resulted from ts-rs regeneration.

Verification confirms this change was from regenerating TypeScript bindings, not manual editing. The commit b2d12be regenerated 4 files in bindings/src/types/ (including this one, plus BadgeUsage.ts and StealthTransferRequest.ts), which aligns with the commit's wallet/badge fixes. ts-rs v11.0 (confirmed in Cargo.toml) generated the more concise string[] syntax as part of this automated batch regeneration.

bindings/src/types/wallet-daemon-client/StealthTransferRequest.ts (1)

1-16: LGTM! BadgeUsage integration is clean.

The generated binding correctly extends StealthTransferRequest with the new badge_usage field, enabling badge-based stealth transfers as described in the PR objectives.

applications/tari_walletd/web_ui/src/utils/json_rpc.ts (2)

110-111: LGTM! Imports support the new decrypt UTXO balance feature.

The new type imports are correctly placed and necessary for the stealthDecryptUtxoBalance wrapper.


313-315: LGTM! RPC wrapper follows established patterns.

The new stealthDecryptUtxoBalance wrapper correctly delegates to the underlying client method and maintains consistency with other RPC wrappers in the file.

bindings/src/types/wallet-daemon-client/BadgeUsage.ts (1)

1-10: LGTM! Well-structured discriminated union.

The BadgeUsage type is cleanly defined with four variants covering different badge usage scenarios. The discriminated union structure enables type-safe handling in TypeScript.

applications/tari_walletd/src/handlers/stealth_utxos.rs (4)

22-22: LGTM! Necessary import for async refactor.

The spawn_blocking import is required for the improved async handling of CPU-intensive balance decryption operations.


102-103: Precomputation enables spawn_blocking migration.

Collecting elgamal_proofs and cloning sdk before the match statement avoids lifetime issues when moving into spawn_blocking closures. The sdk.clone() should be cheap if internally Arc-based.


106-120: Excellent refactor to spawn_blocking for I/O and CPU-intensive work.

Moving file loading and balance brute-forcing into spawn_blocking prevents blocking the async runtime. The error handling with .await?? correctly unwraps both the JoinError and inner anyhow::Error.


123-131: LGTM! Consistent spawn_blocking pattern for CPU-bound work.

The None branch correctly uses spawn_blocking for the CPU-intensive balance brute-force with AlwaysMissLookupTable, maintaining consistency with the Some(file) branch.

bindings/src/wallet-daemon-client.ts (1)

112-112: LGTM! Standard barrel export.

The re-export of BadgeUsage follows the established pattern and makes the type available through the main bindings entry point.

applications/tari_walletd/web_ui/src/routes/Settings/Settings.tsx (2)

32-32: LGTM! Import for new Decrypt UTXO component.

The import follows the established pattern and conventions for this file.


67-71: LGTM! New settings tab for UTXO decryption.

The menu item follows the established pattern and successfully integrates the new Decrypt UTXO Balance feature into the Settings page.

integration_tests/src/wallet_daemon_client.rs (2)

31-31: LGTM! Import for new BadgeUsage field.

The import correctly adds BadgeUsage from the stealth_transfer API module, enabling its use in the test payload.


126-126: LGTM! Test updated for API change.

Setting badge_usage: BadgeUsage::None correctly adapts the test to the new API while maintaining the original test intent (stealth transfer without badge requirements).

crates/wallet/sdk/src/apis/stealth_transfer/error.rs (2)

5-5: LGTM! Import for new error variant.

The ResourceAddress import is necessary for the BadgeVaultNotFound error variant and follows the existing import patterns.


33-34: LGTM! Descriptive error variant for badge failures.

The BadgeVaultNotFound error variant provides clear context by including the resource_address, enabling better error reporting and debugging for badge-related operations.

applications/tari_walletd/src/handlers/accounts.rs (1)

971-987: Propagating badge_usage looks correct; consider tightening validation in SDK.

Plumbing the new field is fine. Ensure StealthTransferParams::validate also rejects BadgeUsage::AmountOfResource with a negative amount to fail fast before building/signing a tx. See suggested SDK change in params.rs comment.

applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts (1)

128-131: BadgeUsage wiring looks good; ensure callers default to "None".

Resource-only extraction for public/confidential is correct and passing through for stealth is expected. To avoid undefined at call sites, ensure TransferParams.badge_usage is initialized to "None".

Would you like a small helper to normalize UI inputs into a valid BadgeUsage ("None" | {Resource...} | ...), so all call sites can pass through safely?

Also applies to: 140-156, 158-174, 183-191

crates/wallet/sdk/src/apis/stealth_transfer/params.rs (1)

4-4: Add badge_usage.amount validation to the validate() method.

The review correctly identifies a missing validation check: badge_usage.amount is not validated for negative values, despite other amount fields being checked. However, the claim about serde trait bounds is unsupported by the codebase—StealthTransferParams does not derive Serialize, and no evidence of serde serialization for these types exists.

Keep the validation addition (which is necessary and consistent with existing validation patterns for other amount fields), but remove the serde import change:

 impl StealthTransferParams {
     pub fn validate(&self, network: Network) -> Result<(), StealthTransferApiError> {
         if self.outputs.is_empty() {
             return Err(StealthTransferApiError::InvalidParameter {
                 param: "outputs",
                 reason: "At least one output must be specified".to_string(),
             });
         }
 
+        if let BadgeUsage::AmountOfResource { amount, .. } = &self.badge_usage {
+            if amount.is_negative() {
+                return Err(StealthTransferApiError::InvalidParameter {
+                    param: "badge_usage.amount",
+                    reason: "Badge amount must be non-negative".to_string(),
+                });
+            }
+        }

Likely an incorrect or invalid review comment.

applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx (1)

30-34: Imports LGTM; keep versions in lockstep.

Importing BadgeUsage and rejectReasonToString is correct for the new payload and messaging. Ensure the UI bindings package version matches the daemon to avoid enum drift.

crates/wallet/sdk/src/apis/stealth_transfer/api.rs (3)

356-399: Fee statement correctness: confirm “revealed vs stealth” split.

You subtract max_fee from stealth to form fee_stealth_change_amt while setting output_revealed_amount = max_fee. This assumes revealed fee is always the revealed portion; if fee inputs are all-stealth, revealed_output_amount should be zero. Validate generate_transfer_statement semantics to ensure mixed inputs are handled as intended.


739-772: Proof lifecycle: good; confirm DropAllProofs only when proofs exist.

Logic drops proofs only when a badge is used. If future variants introduce other proofs, keep this conditional close to the proof creation to avoid regressions. LGTM as-is.

Also applies to: 835-841


726-738: Fee path: withdraw + pay_fee_stealth flow LGTM.

The conditional use of withdraw with input bucket when revealed fee > 0 matches builder semantics.

Comment thread applications/tari_app_utilities/config_presets/c_validator_node.toml Outdated
};
use tari_ootle_wallet_sdk::{
apis::confidential_transfer::ConfidentialTransferInputSelection,
apis::{confidential_transfer::ConfidentialTransferInputSelection, stealth_transfer::BadgeUsage},

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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

BadgeUsage must implement serde traits; otherwise this struct won’t serialize.

StealthTransferRequest derives serde Serialize/Deserialize. The SDK enum BadgeUsage (in crates/wallet/sdk/.../params.rs) currently derives tari_bor::{Deserialize, Serialize}, not serde. This will fail trait bounds here.

Fix in SDK by deriving serde instead of tari_bor (see params.rs comment and diff). Your skip_serializing_if = "BadgeUsage::is_none" will then work as intended.

Also applies to: 1064-1066


🏁 Script executed:

#!/bin/bash
# Verify BadgeUsage derives serde (not only tari_bor)
rg -n 'enum\s+BadgeUsage\b' crates/wallet/sdk/src/apis/stealth_transfer/params.rs -C2
rg -n 'use\s+tari_bor::\{Deserialize,\s*Serialize\}' crates/wallet/sdk/src/apis/stealth_transfer/params.rs
rg -n 'use\s+serde::\{Deserialize,\s*Serialize\}' crates/wallet/sdk/src/apis/stealth_transfer/params.rs

Length of output: 278


[/skip_cloning]


---

🏁 Script executed:

```shell
#!/bin/bash
# Check all imports in params.rs to see if serde is also imported
rg -n '^use' crates/wallet/sdk/src/apis/stealth_transfer/params.rs | head -20

# Find StealthTransferRequest struct and check BadgeUsage usage
rg -n 'struct\s+StealthTransferRequest' clients/wallet_daemon_client/src/types.rs -A20 | head -30

# Check if BadgeUsage field exists in wallet_daemon_client's StealthTransferRequest
rg -n 'badge' clients/wallet_daemon_client/src/types.rs -B2 -A2

Length of output: 1915



---

🏁 Script executed:

```shell
#!/bin/bash
# Get full StealthTransferRequest struct definition with derives
rg -n 'pub struct StealthTransferRequest' clients/wallet_daemon_client/src/types.rs -B5 -A15

# Get full BadgeUsage enum definition with all derives
rg -n 'pub enum BadgeUsage' crates/wallet/sdk/src/apis/stealth_transfer/params.rs -B3 -A8

Length of output: 1398



---

🏁 Script executed:

```shell
#!/bin/bash
# Check serde imports in wallet_daemon_client types.rs
rg -n '^use serde' clients/wallet_daemon_client/src/types.rs

# Check for any feature flags or cfg_attr on BadgeUsage
rg -n '#\[cfg' crates/wallet/sdk/src/apis/stealth_transfer/params.rs | grep -i badge -B2 -A2

# Broader check: are there any conditional derives on BadgeUsage?
rg -n 'derive.*BadgeUsage|BadgeUsage.*derive' crates/wallet/sdk/src/apis/stealth_transfer/params.rs

Length of output: 39


BadgeUsage must derive serde traits; current implementation blocks compilation.

StealthTransferRequest (line 1058) derives serde traits and uses #[serde(default, skip_serializing_if = "BadgeUsage::is_none")] on the badge_usage field. However, BadgeUsage in crates/wallet/sdk/src/apis/stealth_transfer/params.rs (line 143) currently derives only tari_bor traits, not serde. The serde derive macro will fail to serialize/deserialize this field.

Add serde import and derive in params.rs:

use serde::{Deserialize, Serialize};
...
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub enum BadgeUsage { ... }
🤖 Prompt for AI Agents
In clients/wallet_daemon_client/src/types.rs around line 42 and in
crates/wallet/sdk/src/apis/stealth_transfer/params.rs around line 143,
BadgeUsage is only deriving tari_bor traits but StealthTransferRequest in
types.rs derives serde and uses serde attributes on badge_usage which causes
compilation failure; update params.rs to import serde (use serde::{Deserialize,
Serialize};) and add serde derives to BadgeUsage (e.g., #[derive(Debug, Clone,
Deserialize, Serialize, Default)]) so the enum can be serialized/deserialized by
serde while preserving existing traits.

Comment thread crates/engine_types/src/crypto/elgamal.rs Outdated
Comment thread crates/wallet/sdk/src/apis/stealth_transfer/api.rs
Comment thread crates/wallet/sdk/src/apis/stealth_transfer/api.rs Outdated
@github-actions

Copy link
Copy Markdown

Test Results (CI)

479 tests  ±0   467 ✅ +2   1h 32m 37s ⏱️ + 1m 52s
 78 suites ±0     0 💤 ±0 
  2 files   ±0    12 ❌  - 2 

For more details on these failures, see this check.

Results for commit b2d12be. ± Comparison against base commit d80aae4.

@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

Caution

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

⚠️ Outside diff range comments (2)
crates/wallet/sdk/src/apis/stealth_transfer/api.rs (2)

205-207: Don’t panic on negative input amounts; return a typed error.

This expect can crash the daemon. Use InvariantViolation like elsewhere.

-                let total_confidential_spent = Amount::sum_from_positive(inputs.iter().map(|i| i.value))
-                    // The wallet has somehow stored a negative amount, which should not happen.
-                    .expect("BUG: an unblinded input amount was negative");
+                let total_confidential_spent = Amount::sum_from_positive(inputs.iter().map(|i| i.value))
+                    .ok_or_else(|| StealthTransferApiError::InvariantViolation {
+                        details: "An unblinded input amount was negative".to_string(),
+                    })?;

231-237: Lock misuse: shadowing provided lock_id creates a second lock that’s not released.

PreferConfidential creates a new lock instead of using the caller’s lock_id. On errors, unlock_on_failure won’t release this inner lock.

-                let lock_id = self.outputs_api.create_lock()?;
                 let (inputs, blinded_amount_locked) = self.outputs_api.lock_outputs_until_partial_amount(
                     owner_account_component_address,
                     &resource_address,
                     spend_amount,
-                    lock_id,
+                    lock_id,
                 )?;
♻️ Duplicate comments (2)
crates/wallet/sdk/src/apis/stealth_transfer/api.rs (2)

388-399: Align spend_key_branch/id with the actual signer (fee statement).

You select a Nonce signer when no revealed funds, but still pass Account/owner_key_id here. This risks verification/key mismatch.

-                    spend_key_branch: KeyBranch::Account,
-                    spend_key_id: owner_key_id,
+                    spend_key_branch: signing_key_branch,
+                    spend_key_id: signing_key_id,

506-521: Align spend_key_branch/id with the actual signer (main statement).

Same issue as fee statement; use the resolved main signing key.

-                    spend_key_branch: KeyBranch::Account,
-                    spend_key_id: owner_key_id,
+                    spend_key_branch: signing_key_branch,
+                    spend_key_id: signing_key_id,
🧹 Nitpick comments (2)
crates/wallet/sdk/src/apis/stealth_transfer/error.rs (1)

51-55: Consider marking BadgeVaultNotFound as “not found” for transport layers.

If higher layers map IsNotFoundError -> 404/Optional, extend is_not_found_error to include BadgeVaultNotFound.

 impl IsNotFoundError for StealthTransferApiError {
     fn is_not_found_error(&self) -> bool {
-        matches!(self, Self::StoreError(e) if e.is_not_found_error() )
+        matches!(
+            self,
+            Self::StoreError(e) if e.is_not_found_error()
+            | Self::BadgeVaultNotFound { .. }
+        )
     }
 }
applications/tari_walletd/src/main.rs (1)

156-175: Protect the secret key when writing to disk (0600) and avoid accidental logging.

Set restrictive perms on the output file; keep stdout printing but ensure logs don’t capture the secret.

+#[cfg(unix)]
+use std::os::unix::fs::OpenOptionsExt;
@@
-                Some(path) => {
-                    let mut file = fs::File::options()
+                Some(path) => {
+                    let mut file = fs::OpenOptions::new()
                         .create(true)
                         .write(true)
                         .truncate(true)
+                        // Unix: file mode 0600 to protect the secret
+                        .apply(|opts| {
+                            #[cfg(unix)]
+                            {
+                                use std::os::unix::fs::OpenOptionsExt;
+                                opts.mode(0o600);
+                            }
+                            opts
+                        })
                         .open(path)
                         .context("failed to open file for writing")?;
                     serde_json::to_writer_pretty(&mut file, &json).context("failed to encode key json to file")?;
                     println!("Key written to {}", path.display());
                 },

Alternatively, call fs::set_permissions after create if you prefer to avoid cfgs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2d12be and 112b531.

📒 Files selected for processing (5)
  • applications/tari_app_utilities/config_presets/c_validator_node.toml (1 hunks)
  • applications/tari_walletd/src/cli.rs (1 hunks)
  • applications/tari_walletd/src/main.rs (1 hunks)
  • crates/wallet/sdk/src/apis/stealth_transfer/api.rs (5 hunks)
  • crates/wallet/sdk/src/apis/stealth_transfer/error.rs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • applications/tari_app_utilities/config_presets/c_validator_node.toml
🧰 Additional context used
🧬 Code graph analysis (3)
applications/tari_walletd/src/main.rs (2)
applications/tari_walletd/src/lib.rs (2)
  • init_wallet_store (178-182)
  • initialize_wallet_sdk (184-198)
utilities/tariswap_test_bench/src/runner.rs (1)
  • initialize_wallet_sdk (113-125)
crates/wallet/sdk/src/apis/stealth_transfer/error.rs (1)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
crates/wallet/sdk/src/apis/stealth_transfer/api.rs (3)
bindings/src/types/wallet-daemon-client/BadgeUsage.ts (1)
  • BadgeUsage (6-10)
crates/wallet/sdk/src/apis/stealth_transfer/types.rs (1)
  • total_amount (52-54)
crates/wallet/sdk/src/apis/stealth_transfer/params.rs (2)
  • total_output_amount (98-100)
  • total_output_amount (121-123)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: test
  • GitHub Check: file licenses
  • GitHub Check: clippy
  • GitHub Check: fmt
  • GitHub Check: check stable
  • GitHub Check: check nightly
  • GitHub Check: machete
🔇 Additional comments (4)
crates/wallet/sdk/src/apis/stealth_transfer/error.rs (2)

33-35: Good: specific error for missing badge vault.

Clear variant and message will help upstream callers and UI.


47-49: Good: domain error for invariants.

Preferable to panics; aligns with wallet-daemon stability.

crates/wallet/sdk/src/apis/stealth_transfer/api.rs (1)

571-581: Badge vault handling looks correct.

Typed error on absence plus substate input inclusion is solid.

If AccountsApi can return stale vaults, consider guarding with an on-chain check in a follow-up.

applications/tari_walletd/src/cli.rs (1)

141-147: LGTM: clear, focused subcommand for viewable-balance keys.

Names and flags mirror CreateAccount; discoverable and consistent.

Comment thread crates/wallet/sdk/src/apis/stealth_transfer/api.rs
@sdbondi
sdbondi merged commit f15af8b into tari-project:development Oct 27, 2025
11 of 12 checks passed
@sdbondi
sdbondi deleted the wallet-badge-usage branch October 27, 2025 04:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants