Skip to content

feat(wallet): complete multi-chain signing + broadcast (EVM/BTC/Solana/Tron) - #2519

Merged
senamakel merged 5 commits into
tinyhumansai:mainfrom
senamakel:wallet/multi-chain-complete
May 23, 2026
Merged

senamakel merged 5 commits into
tinyhumansai:mainfrom
senamakel:wallet/multi-chain-complete

Conversation

@senamakel

@senamakel senamakel commented May 23, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Extend the wallet domain from EVM-only sign+broadcast to full multi-chain signing across EVM (Ethereum + Base + Arbitrum + Optimism + Polygon), Bitcoin (P2WPKH), Solana (native + SPL), Tron (TRX + TRC20).
  • Introduce chains/{evm,btc,solana,tron}.rs per-chain executors. execution.rs becomes a thin dispatcher; preparation logic + quote storage stay shared.
  • Add evmNetwork to prepare params + prepared quotes so a single WalletChain::Evm covers Ethereum + L2s, with chain_id cross-check at execute time to refuse cross-network signing.
  • Test coverage: 63 wallet unit tests (was 23) + 8 JSON-RPC E2E tests (was 2), including per-chain sign+broadcast against axum mocks and deterministic key-derivation vectors.

Problem

The wallet domain had stubs for BTC/Solana/Tron — execute_prepared returned "not implemented yet" for every non-EVM chain, and Base/Arbitrum/Optimism/Polygon weren't representable at all. Agents could prepare quotes but couldn't sign or broadcast on three of the four supported chains, and the EVM signer was hard-pinned to Ethereum mainnet.

Solution

Per-chain executors under chains/:

  • EVM — single key path (m/44'/60'/...), EvmNetwork enum selects RPC/explorer/asset catalog per network. Chain_id from RPC must match the network on the quote or signing aborts.
  • Bitcoin — BIP84 P2WPKH derivation via bitcoin crate, Esplora REST for UTXO/balance/broadcast, BIP143 segwit sighash + ECDSA signing, dust-aware change output.
  • Solana — SLIP-0010 ed25519 derivation, hand-built legacy message + shortvec wire format (avoids solana-sdk's ~150 transitive crates), native + SPL with off-curve find_program_address for ATA derivation.
  • Tron — BIP44 secp256k1 derivation, TronGrid REST endpoints, TRC20 ABI parameter encoding, recoverable ECDSA signing, surfaces node rejection codes.

Shared test_support module exposes the canonical test mnemonic + deterministic addresses so every chain test runs against the same wallet without re-deriving.

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy — 40 new wallet tests including failure paths (insufficient UTXOs, chain_id mismatch, Tron node rejection)
  • Diff coverage ≥ 80% — changed lines (Vitest + cargo-llvm-cov merged via diff-cover) meet the gate enforced by .github/workflows/coverage.yml. Run pnpm test:coverage and pnpm test:rust locally; PRs below 80% on changed lines will not merge.
  • N/A: behaviour-only change — Coverage matrix updated. The wallet domain itself isn't a row in the matrix; this PR completes signing on chains that were already advertised.
  • N/A: behaviour-only change — All affected feature IDs from the matrix are listed in the PR description under ## Related
  • No new external network dependencies introduced (mock backend used per Testing Strategy) — every chain test points at an axum mock via OPENHUMAN_WALLET_RPC_<CHAIN> env override
  • N/A: wallet signing is not a release-cut surface — Manual smoke checklist updated if this touches release-cut surfaces (docs/RELEASE-MANUAL-SMOKE.md)
  • N/A: no linked issue — Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Runtime: desktop (Tauri host) — adds bitcoin + ed25519-dalek + bs58 + coins-bip39 + curve25519-dalek + ripemd to the core binary. No frontend changes.
  • Security: mnemonic stays encrypted at rest; decrypted only inside the chain executor at sign time, then dropped. Chain_id cross-check prevents accidental cross-network EVM signing.
  • Compatibility: existing EVM-only callers still work — evmNetwork defaults to ethereum_mainnet when omitted, preserving the previous behavior.
  • Performance: cold build adds ~30s due to bitcoin/secp256k1 compile. Runtime overhead is negligible — signing is microseconds, dominated by network I/O.

Related

  • Closes: N/A
  • Follow-up PR(s)/TODOs:
    • Frontend wallet screens to surface the new evmNetwork selector (separate PR)
    • Token catalog expansion per L2 (USDT/DAI on Base, etc.) as users request
    • Solana SPL token ATA auto-creation on first transfer (currently assumes destination ATA exists)

AI Authored PR Metadata (required for Codex/Linear PRs)

Keep this section for AI-authored PRs. For human-only PRs, mark each field N/A.

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: wallet/multi-chain-complete
  • Commit SHA: f537111b5b6fd164305e998ddc6e868f69367ef3

Validation Run

  • pnpm --filter openhuman-app format:check — N/A: no frontend changes
  • pnpm typecheck — N/A: no frontend changes
  • Focused tests: cargo test --lib openhuman::wallet (63 pass) + cargo test --test json_rpc_e2e json_rpc_wallet (8 pass)
  • Rust fmt/check (if changed): cargo fmt + cargo check --bin openhuman-core clean
  • Tauri fmt/check (if changed): N/A — shell unchanged

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: wallet_execute_prepared now signs and broadcasts on BTC/Solana/Tron (previously returned "not implemented yet"). EVM gains L2 network selection via evmNetwork param.
  • User-visible effect: agents calling wallet_prepare_transfer + wallet_execute_prepared for non-EVM chains will now actually broadcast (instead of erroring). EVM callers can target Base/Arbitrum/Optimism/Polygon.

Parity Contract

  • Legacy behavior preserved: existing EVM-only callers (no evmNetwork param) keep targeting Ethereum mainnet exactly as before. All previously-passing tests still pass.
  • Guard/fallback/dispatch parity checks: chain_id mismatch guard added on EVM execute; BTC rejects non-P2WPKH addresses; Tron surfaces base58check checksum failures.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): N/A
  • Canonical PR: N/A
  • Resolution (closed/superseded/updated): N/A

Summary by CodeRabbit

  • New Features

    • Multi-chain wallet: native Bitcoin (segwit), EVM networks (Ethereum + L2s), Solana, Tron — prepare/execute native and token transfers; choose target EVM network.
    • Per-network EVM defaults, explorer links, and asset catalogs.
  • Documentation

    • API schemas: optional evmNetwork input and updated amount unit notes.
  • Tests

    • Extensive unit and end-to-end tests and shared test fixtures for multi-chain flows and mocked RPC/REST.
  • Chores

    • Centralized HTTP/RPC client and REST helpers; execution delegated to per-chain executors.

Review Change Stack

…lana/Tron

Extend the wallet domain from EVM-only to full sign+broadcast across four
chain families, plus a multi-network EVM stack (Ethereum + Base + Arbitrum
+ Optimism + Polygon). Adds chain-specific signing primitives, mockable
HTTP transport, deterministic test vectors, and full prepare→execute JSON-RPC
E2E coverage.

## Surface

- **EVM** (chains/evm.rs): single key path, `evmNetwork` parameter routes to
  Ethereum / Base / Arbitrum / Optimism / Polygon RPC endpoints with chain_id
  cross-check on execute to refuse to sign across network mismatches.
- **Bitcoin** (chains/btc.rs): BIP84 P2WPKH derivation, Esplora REST for
  UTXO/balance/broadcast, segwit (BIP143) sighash + ECDSA signing, change
  output above dust.
- **Solana** (chains/solana.rs): SLIP-0010 ed25519 derivation, hand-built
  legacy message + shortvec wire format (no solana-sdk dep), ed25519
  signing, native SOL + SPL token transfer via on-curve check for ATA
  derivation (find_program_address).
- **Tron** (chains/tron.rs): BIP44 secp256k1 derivation, TronGrid REST
  (createtransaction / triggersmartcontract / broadcasttransaction),
  TRC20 ABI parameter encoding, recoverable ECDSA signature, surfaces node
  rejection codes.

## Test coverage

- 63 wallet unit tests (was 23). Per-chain: address validation, key
  derivation deterministic vectors from the standard test mnemonic, full
  sign + broadcast against an axum mock per chain, error paths.
- 8 JSON-RPC E2E tests (was 2): wallet_setup, execution_surface,
  evm_base_network_prepare_execute, btc_prepare_execute, solana_prepare_execute,
  tron_prepare_execute, tron_trc20_prepare_execute, network_defaults_lists_all_chains.
- Shared test_support module exposes the canonical "abandon × 11 about"
  mnemonic + derived addresses so chain-specific tests don't re-derive.

## Dependencies

- bitcoin 0.32 — P2WPKH signing (includes secp256k1).
- ed25519-dalek 2 — Solana signing.
- bs58 0.5 (check feature) — Solana/Tron base58.
- coins-bip39 0.8 — shared mnemonic→seed (already in tree via ethers-signers).
- curve25519-dalek 4 — Solana ATA off-curve check.
- ripemd 0.1 — RIPEMD160 hashes.

Per CLAUDE.md the wallet domain stays under src/openhuman/wallet/ with the
controller-registry pattern preserved; the new per-chain executors live in
chains/{evm,btc,solana,tron}.rs and feed a thin dispatcher in execution.rs.
@senamakel
senamakel requested a review from a team May 23, 2026 06:11
@coderabbitai

coderabbitai Bot commented May 23, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c87347c2-a70b-41b4-8710-6bff2dad742e

📥 Commits

Reviewing files that changed from the base of the PR and between 40ad952 and 542d6f8.

📒 Files selected for processing (1)
  • tests/json_rpc_e2e.rs

📝 Walkthrough

Walkthrough

This PR adds per-chain wallet executors (BTC P2WPKH, Solana native/SPL, Tron TRX/TRC20), refactors EVM to be network-aware, centralizes HTTP RPC/REST transport, threads optional evm_network through prepare/execute models, updates defaults and asset catalogs, and expands tests/E2E mocks.

Changes

Multi-chain wallet execution

Layer / File(s) Summary
Dependencies & HTTP transport
Cargo.toml, src/openhuman/wallet/rpc.rs
Adds multi-chain signing dependencies and a shared reqwest client; introduces rpc_call_to, evm_rpc_call, and REST helpers (rest_get_text/json, rest_post_text/json).
EVM network abstraction & defaults
src/openhuman/wallet/defaults.rs
Adds EvmNetwork and per-network RPC/env/explorer resolution; makes asset catalogs EVM-network-aware and adds find_asset_for_network and explorer helpers.
Chains module dispatcher
src/openhuman/wallet/chains/mod.rs
Declares per-chain executor submodules (btc, evm, solana, tron).
Bitcoin executor
src/openhuman/wallet/chains/btc.rs
Implements P2WPKH address validation, Esplora balance/UTXO fetch, greedy UTXO selection, BIP32/BIP143 signing, segwit tx assembly/broadcast, and tests with a mock Esplora server.
EVM executor
src/openhuman/wallet/chains/evm.rs
Implements evm_balance, execute_evm_quote (derive signer, build native/ERC20/contract tx, validate chainId, sign & broadcast), and validate_evm_address.
Solana executor
src/openhuman/wallet/chains/solana.rs
Adds Solana address validation, SLIP-0010 ed25519 derivation, shortvec/wire-format encoding, ATA PDA logic, native/SPL message builders, RPC helpers, execution, and tests with a mock RPC.
Tron executor
src/openhuman/wallet/chains/tron.rs
Adds Tron base58check helpers, balance fetch, BIP39/BIP32-style secp256k1 derivation, TRC20 ABI encoding, raw tx signing (sha256 + recoverable ECDSA), broadcast, and tests with a mocked TronGrid server.
Execution orchestration & quote routing
src/openhuman/wallet/execution.rs
Threads optional evm_network through models and prepare params; refactors balances() to call per-chain providers; rewrites execute_prepared() to dispatch to chain executors, remove/restore quotes on errors, and ensure explorer_url fallback.
API schemas & tool updates
src/openhuman/wallet/schemas.rs, src/openhuman/tools/impl/wallet/prepare_transfer.rs, src/openhuman/wallet/mod.rs
Adds optional evmNetwork input to prepare_transfer, prepare_swap, and prepare_contract_call (defaulting to ethereum_mainnet when applicable); updates module docs and tool parameter schema.
Test fixtures & E2E suite
src/openhuman/wallet/test_support.rs, src/openhuman/wallet/execution.rs (tests), tests/json_rpc_e2e.rs
Adds deterministic test fixtures and setup_wallet_in, expands E2E tests with chain-scoped upstream mocks for EVM (configurable chainId), BTC, Solana, and Tron exercising prepare→execute flows and chain-id validation.

Sequence Diagram

sequenceDiagram
  participant API
  participant Execution as execution.rs
  participant Defaults as defaults.rs
  participant BTC as chains/btc.rs
  participant EVM as chains/evm.rs
  participant Solana as chains/solana.rs
  participant Tron as chains/tron.rs
  API->>Execution: prepare_transfer(chain, evmNetwork)
  Execution->>Defaults: evm_asset_catalog(network)
  API->>Execution: execute_prepared(quote)
  alt chain == Bitcoin
    Execution->>BTC: execute_btc_quote()
    BTC->>BTC: derive key, select UTXOs, sign, broadcast
  else chain == EVM
    Execution->>EVM: execute_evm_quote()
    EVM->>EVM: derive signer, fetch nonce/gas/chainId, sign, broadcast
  else chain == Solana
    Execution->>Solana: execute_solana_quote()
    Solana->>Solana: derive ed25519 key, build message, sign, sendTransaction
  else chain == Tron
    Execution->>Tron: execute_tron_quote()
    Tron->>Tron: derive secp256k1 key, encode params, sign sha256, broadcast
  end
  Execution->>API: return ExecutionResult (tx hash / explorer URL)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • graycyrus

Poem

🐰 Hopping keys and hex in hand,

Segwit, SOL, and TRX all stand,
EVM lanes now know each chain,
Signed and sent across the plain,
A rabbit cheers the multi-chain band 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and clearly summarizes the main change: extending wallet functionality to support multi-chain signing and broadcast for the four named chains (EVM, BTC, Solana, Tron).
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@coderabbitai coderabbitai Bot added the feature Net-new user-facing capability or product behavior. label May 23, 2026

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

Caution

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

⚠️ Outside diff range comments (2)
src/openhuman/wallet/rpc.rs (1)

84-93: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add explicit debug/trace logs on error branches before returning Err.

You already log RPC entry/exit, but non-success HTTP and JSON-RPC error branches currently return without branch-local diagnostics. Add a debug/trace line in each error path with method + redacted URL to align diagnostics across transports.

As per coding guidelines: “Use log / tracing at debug or trace level on RPC entry and exit, error paths, state transitions, and any branch that is hard to infer from tests alone.”

Also applies to: 115-118, 148-151, 170-173

🤖 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/openhuman/wallet/rpc.rs` around lines 84 - 93, Before each early return
in the RPC error branches (the HTTP non-success branch using
status.is_success(), the JSON decode error from serde_json::from_str, and the
JSON-RPC error branch that inspects body.get("error")), add a debug/trace log
that includes the method and a redacted form of the request URL (e.g., call an
existing redact helper or add one like redact_url(&url) if none exists) and any
safe error snippet (not raw credentials); use log::debug! or tracing::trace!
immediately before the Err(...) return so each branch logs method + redacted URL
and a short error context; apply the same change to the other similar blocks
mentioned (lines ~115-118, ~148-151, ~170-173) referencing the same symbols
(status.is_success(), serde_json::from_str, body.get("error")).
src/openhuman/wallet/execution.rs (1)

467-535: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Return EVM balances per network, not just Ethereum mainnet.

This branch hardcodes EvmNetwork::EthereumMainnet for both the RPC call and the emitted evm_network. After this PR, supported_assets() and chain_status() enumerate every EVM network, but balances() still hides Base/Arbitrum/Optimism/Polygon funds.

🤖 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/openhuman/wallet/execution.rs` around lines 467 - 535, The EVM branch is
hardcoded to EvmNetwork::EthereumMainnet; update the WalletChain::Evm match to
destructure/extract the actual EVM network from account.chain (match
WalletChain::Evm(network) => ...) and pass that network into
chain_evm::evm_balance(...) and set the resulting BalanceInfo.evm_network to
Some(network) instead of always Some(EvmNetwork::EthereumMainnet); ensure you
update any equality check that set evm_network (the later if account.chain ==
WalletChain::Evm) to use the extracted network value so per-network balances
(Base/Arbitrum/Optimism/Polygon/etc.) are returned.
🧹 Nitpick comments (5)
src/openhuman/wallet/chains/tron.rs (1)

55-66: ⚡ Quick win

Add structured logs around TronGrid calls and rejection branches.

This module only emits a success log after broadcast. getaccount, createtransaction, triggersmartcontract, and the rejection path at Lines 324-332 currently lose the method/address/quote context needed to diagnose node-side failures in production.

As per coding guidelines "Use log / tracing at debug or trace level on RPC entry and exit, error paths, state transitions, and any branch that is hard to infer from tests alone."

Also applies to: 199-239, 319-344

🤖 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/openhuman/wallet/chains/tron.rs` around lines 55 - 66, The native_balance
function lacks structured logs around the Tron RPC call and error branches; add
debug/trace logs at entry (including the address and that validate_tron_address
passed), before the RPC call (include method "getaccount", target URL and hex
address from tron_address_to_hex), after a successful response showing returned
balance, and an error log on any early returns or on rest_post_json failures;
use the existing functions/native symbols (native_balance,
validate_tron_address, rpc_url_for_chain, tron_address_to_hex, rest_post_json)
and emit structured logs with context fields like method="getaccount", address,
hex_address, url, and balance so failures can be diagnosed.
src/openhuman/wallet/chains/solana.rs (1)

78-87: ⚡ Quick win

Add RPC entry/error logs for blockhash and sendTransaction.

The module only logs the success path after broadcast. When getBalance, getLatestBlockhash, or sendTransaction fails, you lose the method/address/quote context needed to debug provider issues.

As per coding guidelines "Use log / tracing at debug or trace level on RPC entry and exit, error paths, state transitions, and any branch that is hard to infer from tests alone."

Also applies to: 296-315, 317-383

🤖 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/openhuman/wallet/chains/solana.rs` around lines 78 - 87, The functions
calling Solana RPCs (e.g., native_balance and the routines handling
getLatestBlockhash and sendTransaction) lack entry/exit and error logs, which
makes debugging provider failures hard; update each RPC caller (references:
native_balance, the getLatestBlockhash handler and sendTransaction path in this
module) to log at debug/trace level when an RPC call is about to be made
(include RPC method name like
"getBalance"/"getLatestBlockhash"/"sendTransaction", the target address or
transaction/quote id where applicable), log the successful response on exit, and
log errors with full context (method, address/quote, and the returned error)
before returning Err so callers can trace failures. Ensure logs use the crate's
logging/tracing facility and include enough contextual fields to correlate
requests and responses.
src/openhuman/wallet/chains/evm.rs (1)

32-35: ⚡ Quick win

Add structured logs around nonce/gas/sendRawTransaction RPCs.

The only log here is the happy-path broadcast. Chain ID mismatches, nonce fetch failures, gas estimation failures, and RPC rejects currently lose the method/network/quote context the repo asks for.

As per coding guidelines "Use log / tracing at debug or trace level on RPC entry and exit, error paths, state transitions, and any branch that is hard to infer from tests alone."

Also applies to: 104-165

🤖 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/openhuman/wallet/chains/evm.rs` around lines 32 - 35, Add structured
debug/trace logs around RPC entry/exit and error branches for EVM RPC
interactions: instrument evm_balance (and the other EVM RPC wrappers in the same
file, e.g., functions that call evm_rpc_call,
nonce/gas/estimate/sendRawTransaction between lines ~104-165) to log method name
("eth_getBalance", nonce, gas estimate, sendRawTransaction), the EvmNetwork
value, the address/tx hash/quote context, and the raw RPC response on success,
and include the error/response body on failure; specifically, before calling
evm_rpc_call log an entry with method and params, on successful hex_to_u256
conversion log the parsed U256 and method/network, and on any ?-propagated error
capture and log the error with method/network/quote info to retain context for
debugging.
src/openhuman/wallet/chains/btc.rs (1)

84-109: ⚡ Quick win

Add structured logs around the Esplora RPCs.

native_balance(), fetch_utxos(), and broadcast_raw_hex() only log the success path after broadcast. When Esplora calls fail, there is no method/address/quote context in the logs to explain which provider call broke.

As per coding guidelines "Use log / tracing at debug or trace level on RPC entry and exit, error paths, state transitions, and any branch that is hard to infer from tests alone."

Also applies to: 176-277

🤖 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/openhuman/wallet/chains/btc.rs` around lines 84 - 109, Add structured
tracing around Esplora RPC calls: instrument native_balance, fetch_utxos, and
broadcast_raw_hex (and other Esplora RPC helpers in this file) with trace/debug
logs on entry (include method name and key params: address or tx_hex),
trace/debug on successful exit with returned info (e.g., confirmed/pending or
UTXO count or txid), and error logs that include the method name, address/tx_hex
context and the error string when rest_get_json/rest_post_text fail; use the
existing logging/tracing crate conventions (trace/debug for entry/exit, error
for failures) and ensure messages are structured (key/value pairs) so callers
can correlate which provider and call failed.
tests/json_rpc_e2e.rs (1)

3176-3281: ⚡ Quick win

Return the mock server handles so the tests can shut them down.

Each start_mock_* helper drops the JoinHandle from serve_on_ephemeral, so every new chain test leaves an axum server alive until process exit. That leaks tasks/listeners across the binary and makes later failures harder to reason about.

🤖 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 `@tests/json_rpc_e2e.rs` around lines 3176 - 3281, The helpers
(start_mock_evm_with_chain_id, start_mock_btc, start_mock_solana,
start_mock_tron) currently drop the JoinHandle returned by serve_on_ephemeral,
leaking running axum servers; change each helper to return that JoinHandle
(alongside the SocketAddr and any broadcast Arc) so callers can shut servers
down (abort() or await the handle). Locate serve_on_ephemeral usage in
start_mock_evm_with_chain_id, start_mock_btc, start_mock_solana, start_mock_tron
and modify their signatures/returns to include the JoinHandle type returned by
serve_on_ephemeral, propagate the handle out, and update call sites/tests to
call .abort() or .await on the handle to stop the server after each test.
🤖 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/openhuman/wallet/chains/btc.rs`:
- Around line 63-79: The current validate_btc_address() enforces is_p2wpkh() and
is used for both from_address and to_address, which wrongly rejects valid
recipient types (e.g., Taproot bc1p or P2SH); split sender vs recipient
validation: keep a strict validate_btc_sender() (or validate_btc_from_address)
that performs the P2WPKH check on the parsed Address, and change
validate_btc_address() (or create validate_btc_recipient()) used for to_address
to only trim, parse, require mainnet via
Address::from_str(...).require_network(Network::Bitcoin) and return the trimmed
string without the is_p2wpkh() restriction so script_pubkey_for_addr() can
encode other valid script types; update call sites (from_address -> sender
validator, to_address -> recipient validator).

In `@src/openhuman/wallet/chains/evm.rs`:
- Around line 105-110: The nonce fetch uses eth_getTransactionCount with
"latest", which ignores pending transactions and can cause nonce collisions;
update the rpc_call_to invocation that assigns nonce_hex (the
eth_getTransactionCount call using &rpc_url and quote.from_address) to request
"pending" instead of "latest" so the returned nonce accounts for
already-submitted pending txs.

In `@src/openhuman/wallet/chains/solana.rs`:
- Around line 353-361: In the PreparedKind::TokenTransfer match arm (where you
compute mint via b58_to_pubkey, derive src_ata and dst_ata and call
build_spl_transfer_message), add logic to ensure the destination ATA exists
before creating the Transfer instruction: query the chain/state for dst_ata
existence and if it does not exist either prepend an associated token account
creation instruction to the message (create_associated_token_account for
dst_ata) or return a clear preflight error (e.g., "destination ATA not found for
mint <mint>") instead of blindly building the SPL Transfer; update the code
paths that call build_spl_transfer_message to accept/handle an initial ATA
creation instruction or the early error so first-time transfers to wallets
without an ATA succeed or fail with a clear message.

In `@src/openhuman/wallet/execution.rs`:
- Around line 767-787: The current flow uses get_quote(...) and only calls
take_quote(...) after broadcasting, allowing two concurrent callers to both get
the same quote and double-broadcast; fix it by atomically removing/marking the
quote as taken before sending: call take_quote(&params.quote_id) (use the
returned quote for the chain calls instead of get_quote/clone) to ensure only
one broadcaster proceeds, then perform the chain-specific calls
(execute_evm_quote / execute_btc_quote / execute_solana_quote /
execute_tron_quote) on that taken quote; if a broadcast fails and you need the
quote to remain retryable, restore it (via your quote-insert/restore helper) in
the error path so callers can retry, but do not delay removal until after the
chain call.

In `@src/openhuman/wallet/rpc.rs`:
- Around line 27-32: Replace the per-call client() builder with a process-wide
lazy reqwest::Client created via once_cell::sync::Lazy (or OnceCell) so all
RPC/REST calls reuse the same connection pool; modify the existing client()
function to return the shared client (or a reference) instead of building each
time. Additionally, add log::debug!/log::trace! statements (using the
"[wallet::rpc]" prefix and include method/URL host) immediately before every Err
return in transport failure branches, non-2xx HTTP status handling, RPC response
error cases, and JSON decode/missing-result branches so the error path logs
context before returning the formatted error string. Ensure references to the
client creation live in the client() function and the error logging is added in
the functions that call client() and process responses (the RPC/REST
request/response handling code).

In `@src/openhuman/wallet/schemas.rs`:
- Around line 239-244: The new FieldSchema "evmNetwork" was added to the
controller but the wallet_prepare_transfer tool params (wallet_prepare_transfer)
still reject unknown fields via additionalProperties: false and don't declare
evmNetwork, so calls through that tool can't use it; update the tool's parameter
schema in the prepare_transfer implementation (the schema construction for
wallet_prepare_transfer) to include an optional "evmNetwork" field with the same
type/shape (Option<String>/optional string) and the matching comment, ensuring
it is declared alongside the other fields so additionalProperties can remain
false while the new field is accepted.

In `@src/openhuman/wallet/test_support.rs`:
- Around line 55-77: setup_wallet_in currently mutates OPENHUMAN_WORKSPACE and
never restores it, causing test-order dependency; wrap the env change in a guard
that captures the previous value (std::env::var_os("OPENHUMAN_WORKSPACE")) and
on Drop restores it (if Some then std::env::set_var, else std::env::remove_var)
so restoration runs even if the async function returns early or errors;
implement a small local struct (e.g., WorkspaceEnvGuard) with a Drop impl, set
the env before creating the guard, and keep existing logic (encrypt_secret,
setup) unchanged so cleanup is deterministic.

In `@tests/json_rpc_e2e.rs`:
- Around line 3253-3281: The test currently doesn't verify which Tron endpoint
was called; modify the mock handlers (mock_tron_create and mock_tron_trigger) in
tests/json_rpc_e2e.rs to record per-route hits or capture the incoming request
body (e.g., by incrementing a shared Arc<Mutex<HashMap>> or pushing to a shared
Vec) created in start_mock_tron, then in the test
json_rpc_wallet_tron_trc20_prepare_execute_round_trips assert that
mock_tron_trigger was invoked (and the native TRX test asserts mock_tron_create
was invoked); also update scripts/test-rust-with-mock.sh if it runs any
RPC-method whitelist so the new recorded RPCs are included. Ensure you reference
the shared tracker from start_mock_tron so both handlers can update it and the
test can read and assert on it after the call.
- Around line 3195-3222: Update the BTC mock to validate the requested address
and broadcast payload: in mock_btc_utxo extract the path param (addr) from the
request (e.g., accept Path<String> or use axum::extract::Path) and assert it
equals E2E_TEST_MNEMONIC_ADDRS_BTC before returning state.utxo; in
mock_btc_broadcast assert the incoming body is non-empty and valid hex (or at
least matches r"^[0-9a-fA-F]+$") before pushing to state.broadcast_txs,
preserving the existing mutex push logic; apply the same changes for the other
BTC mock at the second location referenced (lines ~3387-3451) so tests fail if
the wrong address or a non-hex/empty tx is used.
- Around line 2965-2977: Replace the loose ">= 8" and single-base check with an
explicit assertion that the catalog contains the exact expected network
identifiers: iterate over the expected network id string list (e.g. the set of
EVM networks and non-EVM chains used by the UI) and assert list.iter().any(|a|
a.get("evmNetwork").and_then(Value::as_str) == Some(expected)) for each expected
id; update the same pattern in the other block referenced (lines 3665-3707) to
pin the full multi-chain catalog instead of a lower bound; also update
scripts/test-rust-with-mock.sh to add/verify the new RPC method names used by
the UI so the test harness returns the pinned responses the tests expect.

---

Outside diff comments:
In `@src/openhuman/wallet/execution.rs`:
- Around line 467-535: The EVM branch is hardcoded to
EvmNetwork::EthereumMainnet; update the WalletChain::Evm match to
destructure/extract the actual EVM network from account.chain (match
WalletChain::Evm(network) => ...) and pass that network into
chain_evm::evm_balance(...) and set the resulting BalanceInfo.evm_network to
Some(network) instead of always Some(EvmNetwork::EthereumMainnet); ensure you
update any equality check that set evm_network (the later if account.chain ==
WalletChain::Evm) to use the extracted network value so per-network balances
(Base/Arbitrum/Optimism/Polygon/etc.) are returned.

In `@src/openhuman/wallet/rpc.rs`:
- Around line 84-93: Before each early return in the RPC error branches (the
HTTP non-success branch using status.is_success(), the JSON decode error from
serde_json::from_str, and the JSON-RPC error branch that inspects
body.get("error")), add a debug/trace log that includes the method and a
redacted form of the request URL (e.g., call an existing redact helper or add
one like redact_url(&url) if none exists) and any safe error snippet (not raw
credentials); use log::debug! or tracing::trace! immediately before the Err(...)
return so each branch logs method + redacted URL and a short error context;
apply the same change to the other similar blocks mentioned (lines ~115-118,
~148-151, ~170-173) referencing the same symbols (status.is_success(),
serde_json::from_str, body.get("error")).

---

Nitpick comments:
In `@src/openhuman/wallet/chains/btc.rs`:
- Around line 84-109: Add structured tracing around Esplora RPC calls:
instrument native_balance, fetch_utxos, and broadcast_raw_hex (and other Esplora
RPC helpers in this file) with trace/debug logs on entry (include method name
and key params: address or tx_hex), trace/debug on successful exit with returned
info (e.g., confirmed/pending or UTXO count or txid), and error logs that
include the method name, address/tx_hex context and the error string when
rest_get_json/rest_post_text fail; use the existing logging/tracing crate
conventions (trace/debug for entry/exit, error for failures) and ensure messages
are structured (key/value pairs) so callers can correlate which provider and
call failed.

In `@src/openhuman/wallet/chains/evm.rs`:
- Around line 32-35: Add structured debug/trace logs around RPC entry/exit and
error branches for EVM RPC interactions: instrument evm_balance (and the other
EVM RPC wrappers in the same file, e.g., functions that call evm_rpc_call,
nonce/gas/estimate/sendRawTransaction between lines ~104-165) to log method name
("eth_getBalance", nonce, gas estimate, sendRawTransaction), the EvmNetwork
value, the address/tx hash/quote context, and the raw RPC response on success,
and include the error/response body on failure; specifically, before calling
evm_rpc_call log an entry with method and params, on successful hex_to_u256
conversion log the parsed U256 and method/network, and on any ?-propagated error
capture and log the error with method/network/quote info to retain context for
debugging.

In `@src/openhuman/wallet/chains/solana.rs`:
- Around line 78-87: The functions calling Solana RPCs (e.g., native_balance and
the routines handling getLatestBlockhash and sendTransaction) lack entry/exit
and error logs, which makes debugging provider failures hard; update each RPC
caller (references: native_balance, the getLatestBlockhash handler and
sendTransaction path in this module) to log at debug/trace level when an RPC
call is about to be made (include RPC method name like
"getBalance"/"getLatestBlockhash"/"sendTransaction", the target address or
transaction/quote id where applicable), log the successful response on exit, and
log errors with full context (method, address/quote, and the returned error)
before returning Err so callers can trace failures. Ensure logs use the crate's
logging/tracing facility and include enough contextual fields to correlate
requests and responses.

In `@src/openhuman/wallet/chains/tron.rs`:
- Around line 55-66: The native_balance function lacks structured logs around
the Tron RPC call and error branches; add debug/trace logs at entry (including
the address and that validate_tron_address passed), before the RPC call (include
method "getaccount", target URL and hex address from tron_address_to_hex), after
a successful response showing returned balance, and an error log on any early
returns or on rest_post_json failures; use the existing functions/native symbols
(native_balance, validate_tron_address, rpc_url_for_chain, tron_address_to_hex,
rest_post_json) and emit structured logs with context fields like
method="getaccount", address, hex_address, url, and balance so failures can be
diagnosed.

In `@tests/json_rpc_e2e.rs`:
- Around line 3176-3281: The helpers (start_mock_evm_with_chain_id,
start_mock_btc, start_mock_solana, start_mock_tron) currently drop the
JoinHandle returned by serve_on_ephemeral, leaking running axum servers; change
each helper to return that JoinHandle (alongside the SocketAddr and any
broadcast Arc) so callers can shut servers down (abort() or await the handle).
Locate serve_on_ephemeral usage in start_mock_evm_with_chain_id, start_mock_btc,
start_mock_solana, start_mock_tron and modify their signatures/returns to
include the JoinHandle type returned by serve_on_ephemeral, propagate the handle
out, and update call sites/tests to call .abort() or .await on the handle to
stop the server after each test.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 408d29f4-644f-4d92-8975-165657067b3f

📥 Commits

Reviewing files that changed from the base of the PR and between 9bae6d5 and f537111.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • Cargo.toml
  • src/openhuman/wallet/chains/btc.rs
  • src/openhuman/wallet/chains/evm.rs
  • src/openhuman/wallet/chains/mod.rs
  • src/openhuman/wallet/chains/solana.rs
  • src/openhuman/wallet/chains/tron.rs
  • src/openhuman/wallet/defaults.rs
  • src/openhuman/wallet/execution.rs
  • src/openhuman/wallet/mod.rs
  • src/openhuman/wallet/rpc.rs
  • src/openhuman/wallet/schemas.rs
  • src/openhuman/wallet/test_support.rs
  • tests/json_rpc_e2e.rs

Comment thread src/openhuman/wallet/chains/btc.rs Resolved
Comment thread src/openhuman/wallet/chains/evm.rs Resolved
Comment thread src/openhuman/wallet/chains/solana.rs Resolved
Comment thread src/openhuman/wallet/execution.rs Resolved
Comment thread src/openhuman/wallet/rpc.rs Resolved
Comment thread src/openhuman/wallet/schemas.rs Resolved
Comment thread src/openhuman/wallet/test_support.rs Resolved
Comment thread tests/json_rpc_e2e.rs Resolved
Comment thread tests/json_rpc_e2e.rs Resolved
Comment thread tests/json_rpc_e2e.rs Resolved
- execute_prepared: take quote atomically before broadcasting; restore on
  failure. Prevents two concurrent confirmations from double-submitting the
  same quote.
- EVM signer: use eth_getTransactionCount(..., "pending") so back-to-back
  confirmations don't collide on the same nonce.
- BTC validation: split sender (P2WPKH required for signing) from recipient
  (any mainnet address — P2TR/P2SH recipients now accepted).
- Solana SPL: preflight getAccountInfo on the destination ATA so we fail
  loudly with a clear error instead of burning the broadcast when the ATA
  doesn't exist yet. New unit test for the missing-ATA path.
- wallet/rpc: share a process-wide reqwest::Client via once_cell::Lazy so
  connection pooling stays hot across calls.
- wallet_prepare_transfer tool schema: declare evmNetwork so tool callers
  can target Base/Arbitrum/Optimism/Polygon (previously additionalProperties:
  false silently dropped it).
- E2E tests: pin every expected EVM L2 + chain_id in network_defaults and
  supported_assets; assert BTC mock is queried for the BIP84-derived sender
  address; assert Tron native vs TRC20 hit the correct REST endpoints (no
  silent regression onto the wrong path); assert BTC broadcast body is hex.
- test_support: expose WorkspaceEnvGuard so callers can opt in to env
  restoration; documented why the helper itself can't restore eagerly.

64 wallet unit tests + 8 wallet E2E tests pass.

@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 (1)
src/openhuman/wallet/chains/btc.rs (1)

200-219: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Verify the derived signer matches from_address.

validate_btc_sender_address() only proves from_address is a mainnet P2WPKH string. If the stored derivation path/address pair drifts, this path will fetch UTXOs for one address and sign with another key, which turns into a late broadcast rejection instead of a clear early failure. Compare the derived sender address to from_addr right after key derivation and abort on mismatch.

🐛 Proposed fix
     let (private_key, public_key) = derive_btc_private_key(&mnemonic, &secret.derivation_path)?;
+    let derived_from = bitcoin::Address::p2wpkh(&public_key, Network::Bitcoin).to_string();
+    if derived_from != from_addr {
+        return Err(format!(
+            "BTC key derivation mismatch: derived {} but expected {}",
+            derived_from, from_addr
+        ));
+    }
 
     let from_spk = script_pubkey_for_addr(&from_addr)?;
🤖 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/openhuman/wallet/chains/btc.rs` around lines 200 - 219, After deriving
keys in derive_btc_private_key (right after calling
derive_btc_private_key(&mnemonic, &secret.derivation_path)), compute the Bitcoin
address corresponding to the derived public_key (same format as
validate_btc_sender_address expects, e.g., mainnet P2WPKH) and compare it to
from_addr; if they differ, return an Err with a clear message (e.g., "derived
address X does not match requested from_addr Y") so the function fails early
rather than signing with the wrong key. Ensure you use the same address-encoding
logic used elsewhere (e.g., script_pubkey_for_addr/address-from-scriptPubKey
helper) to generate the derived address for a correct comparison.
🧹 Nitpick comments (2)
src/openhuman/wallet/chains/solana.rs (1)

378-388: ⚡ Quick win

Log the ATA preflight miss before returning.

This new branch is exactly the kind of hard-to-infer failure path the wallet logging guideline calls out. Emit a debug!/trace! line with quote_id, mint, owner, and derived ATA before the Err(...) so these user-visible preflight failures are diagnosable.

♻️ Proposed fix
             if !account_exists(&pubkey_to_b58(&dst_ata)).await? {
+                debug!(
+                    "{LOG_PREFIX} spl_preflight_missing_ata quote_id={} mint={} owner={} ata={}",
+                    quote.quote_id,
+                    mint_addr,
+                    pubkey_to_b58(&to_pubkey),
+                    pubkey_to_b58(&dst_ata)
+                );
                 return Err(format!(
                     "SPL preflight: destination Associated Token Account does not exist for mint {} owner {}; create it before transferring",
                     mint_addr,
                     pubkey_to_b58(&to_pubkey)
                 ));

As per coding guidelines, "Use log / tracing at debug or trace level on RPC entry and exit, error paths, state transitions, and any branch that is hard to infer from tests alone."

🤖 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/openhuman/wallet/chains/solana.rs` around lines 378 - 388, Before
returning the error in the SPL preflight branch (the block that checks
account_exists(&pubkey_to_b58(&dst_ata)) and currently returns Err(...)), emit a
debug/trace log that includes quote_id, mint_addr, pubkey_to_b58(&to_pubkey)
(owner) and pubkey_to_b58(&dst_ata) (derived ATA). Locate the branch around the
account_exists call in the same function and add a call to debug! or trace! with
those named fields immediately before the Err(...) return so the failure is
recorded for diagnostics.
src/openhuman/wallet/execution.rs (1)

767-786: ⚡ Quick win

Add debug logs around the take/dispatch/restore lifecycle.

This branch now owns the hardest-to-reconstruct quote state transitions in the module. A small debug/trace log on entry and success would make confirmation-race and failed-broadcast reports much easier to diagnose.

🪵 Example
     let quote = take_quote(&params.quote_id)?;
     let chain = quote.chain;
     let restorable = quote.clone();
+    debug!(
+        "{LOG_PREFIX} execute_prepared start chain={} quote_id={} kind={:?}",
+        chain_str(chain),
+        quote.quote_id,
+        quote.kind
+    );
     let result = match chain {
         WalletChain::Evm => chain_evm::execute_evm_quote(quote).await,
         WalletChain::Btc => chain_btc::execute_btc_quote(quote).await,
         WalletChain::Solana => chain_sol::execute_solana_quote(quote).await,
         WalletChain::Tron => chain_tron::execute_tron_quote(quote).await,
@@
             return Err(error);
         }
     };
+    debug!(
+        "{LOG_PREFIX} execute_prepared success chain={} quote_id={} tx_hash={}",
+        chain_str(chain),
+        result.quote_id,
+        result.transaction_hash
+    );

As per coding guidelines, "Use log / tracing at debug or trace level on RPC entry and exit, error paths, state transitions, and any branch that is hard to infer from tests alone."

🤖 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/openhuman/wallet/execution.rs` around lines 767 - 786, Add debug/trace
logs around the take/dispatch/restore lifecycle: log a trace/debug right after
take_quote(&params.quote_id) with the quote_id and chain, another debug/trace on
dispatch entry before calling chain_evm::execute_evm_quote /
chain_btc::execute_btc_quote / chain_sol::execute_solana_quote /
chain_tron::execute_tron_quote, a debug on successful result before returning
the value, and a debug/trace when restoring via store_quote(restorable) (include
quote_id, chain_str(chain) and any error string). Use the module's existing
logging macros/levels (debug or trace for entry/exit, debug for state
transitions) and ensure messages reference quote_id and chain to aid debugging.
🤖 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/openhuman/wallet/execution.rs`:
- Around line 782-783: When restoring a failed quote, refresh its TTL before
persisting so it isn't immediately expired: update the restored quote's
expires_at_ms to now + the quote TTL (e.g., QUOTE_TTL_MS or 5 minutes) on the
restorable object and then call store_quote(restorable); in other words, set
restorable.expires_at_ms = current_time_ms() + QUOTE_TTL_MS (or invoke an
existing helper like refresh_quote_ttl(restorable) if available) prior to
store_quote(restorable) in the restore/failure path.

---

Outside diff comments:
In `@src/openhuman/wallet/chains/btc.rs`:
- Around line 200-219: After deriving keys in derive_btc_private_key (right
after calling derive_btc_private_key(&mnemonic, &secret.derivation_path)),
compute the Bitcoin address corresponding to the derived public_key (same format
as validate_btc_sender_address expects, e.g., mainnet P2WPKH) and compare it to
from_addr; if they differ, return an Err with a clear message (e.g., "derived
address X does not match requested from_addr Y") so the function fails early
rather than signing with the wrong key. Ensure you use the same address-encoding
logic used elsewhere (e.g., script_pubkey_for_addr/address-from-scriptPubKey
helper) to generate the derived address for a correct comparison.

---

Nitpick comments:
In `@src/openhuman/wallet/chains/solana.rs`:
- Around line 378-388: Before returning the error in the SPL preflight branch
(the block that checks account_exists(&pubkey_to_b58(&dst_ata)) and currently
returns Err(...)), emit a debug/trace log that includes quote_id, mint_addr,
pubkey_to_b58(&to_pubkey) (owner) and pubkey_to_b58(&dst_ata) (derived ATA).
Locate the branch around the account_exists call in the same function and add a
call to debug! or trace! with those named fields immediately before the Err(...)
return so the failure is recorded for diagnostics.

In `@src/openhuman/wallet/execution.rs`:
- Around line 767-786: Add debug/trace logs around the take/dispatch/restore
lifecycle: log a trace/debug right after take_quote(&params.quote_id) with the
quote_id and chain, another debug/trace on dispatch entry before calling
chain_evm::execute_evm_quote / chain_btc::execute_btc_quote /
chain_sol::execute_solana_quote / chain_tron::execute_tron_quote, a debug on
successful result before returning the value, and a debug/trace when restoring
via store_quote(restorable) (include quote_id, chain_str(chain) and any error
string). Use the module's existing logging macros/levels (debug or trace for
entry/exit, debug for state transitions) and ensure messages reference quote_id
and chain to aid debugging.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d8168240-6a5e-4f72-a2c8-2e6839ad6dbe

📥 Commits

Reviewing files that changed from the base of the PR and between f537111 and e89e06f.

📒 Files selected for processing (8)
  • src/openhuman/tools/impl/wallet/prepare_transfer.rs
  • src/openhuman/wallet/chains/btc.rs
  • src/openhuman/wallet/chains/evm.rs
  • src/openhuman/wallet/chains/solana.rs
  • src/openhuman/wallet/execution.rs
  • src/openhuman/wallet/rpc.rs
  • src/openhuman/wallet/test_support.rs
  • tests/json_rpc_e2e.rs

Comment thread src/openhuman/wallet/execution.rs Resolved
senamakel added 2 commits May 23, 2026 00:31
If the chain call took longer than the 5-minute quote window (network
timeouts, slow node), restoring the quote with its original expires_at_ms
would hand back an immediately-expired entry — retry would fail with
"quote expired". Reset expires_at_ms to now + QUOTE_TTL_MS on the restore
path so the caller actually gets a usable retry window.
…complete

# Conflicts:
#	app/src-tauri/Cargo.lock
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 23, 2026
…complete

# Conflicts:
#	tests/json_rpc_e2e.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant