How OpenHuman's Web3 Wallet Enables Multi-Chain Signing: EVM, Bitcoin, Solana, and Tron

OpenHuman's Web3 wallet implements a core-owned, multi-chain architecture that signs transactions across EVM networks, Bitcoin, Solana, and Tron using a single encrypted BIP-39 mnemonic, with private keys derived on-demand inside an attested module process.

The tinyhumansai/openhuman repository provides a comprehensive Web3 wallet implementation that supports multi-chain signing through a unified interface. Unlike browser-extension wallets, this core-owned wallet maintains a single encrypted recovery phrase and derives chain-specific keys on demand, enabling seamless transaction signing across four distinct blockchain families without compromising key security.

Core Architecture of the Multi-Chain Wallet

Single Mnemonic, Multiple Chain Derivations

OpenHuman's wallet architecture centers on one encrypted BIP-39 mnemonic stored either in the OS keychain or a workspace-scoped JSON file, as detailed in src/openhuman/web3/wallet/README.md. This single secret seeds all supported chains through standardized derivation paths:

The onboarding logic in src/openhuman/web3/wallet/ops.rs (lines 18-27) creates this mnemonic once during wallet_setup, encrypts it, and derives the initial account for each chain family.

Module-Based Signing Boundary

The wallet enforces a critical security boundary: private keys never leave the process. When signing is requested, the secret_material(WalletChain::Evm) function (or its chain-specific variant) retrieves the encrypted mnemonic and decrypts it via crate::openhuman::security::encryption::rpc::decrypt_secret. The decrypted phrase is passed exactly once to modules::wallet::sign_transaction_in_module.

This module validates the derivation path, derives the secp256k1 or Ed25519 private key, signs the transaction, and returns only the raw signed bytes. This architecture ensures that private key material remains confined to the attested module, with no exposure to the broader application context.

Chain-Specific Signing Implementations

EVM Network Signing

For all EVM-compatible networks, the signing flow resides in src/openhuman/web3/wallet/chains/evm.rs (lines 38-45). The wallet builds a tinywallet_bus::wire::SecretMaterial struct, derives the private key from the BIP-44 path, and signs the transaction inside the loaded module. The signed raw transaction is broadcast via standard eth_sendRawTransaction RPC calls.

The EvmNetwork enum defined in src/openhuman/web3/wallet/defaults.rs encapsulates network-specific parameters including chain IDs, default RPC URLs, and block explorers. The same sign_and_broadcast primitive works across all EVM networks; the target network is selected from the prepared quote (quote.evm_network).

Bitcoin (BTC) PSBT Handling

Bitcoin signing follows the BIP-84 standard for SegWit (P2WPKH) addresses. In src/openhuman/web3/wallet/chains/btc.rs, the module derives the private key, constructs a Partially Signed Bitcoin Transaction (PSBT), signs the inputs, and broadcasts via Esplora REST endpoints. This supports both mainnet and testnet configurations.

Solana Ed25519 Signatures

Solana integration in src/openhuman/web3/wallet/chains/solana.rs uses Ed25519 key derivation at path m/44'/501'/…. The module constructs a Solana Message, signs it using the ed25519_dalek library, and transmits via Solana RPC methods. This handles both native SOL transfers and SPL token transactions.

Tron Network Integration

Tron signing in src/openhuman/web3/wallet/chains/tron.rs implements the TRON-specific derivation path m/44'/195'/…. After signing the transaction payload within the module, the wallet broadcasts to TronGrid REST endpoints, supporting both native TRX transfers and TRC-20 token transactions.

Transaction Flow from Quote to Broadcast

The multi-chain signing process follows a structured five-step flow:

  1. Quote Preparation: The wallet_prepare_transfer tool accepts PrepareTransferParams specifying the target chain, recipient address, and amount. This validates addresses, estimates fees, and stores a PreparedTransaction with a 5-minute TTL bound to the originating chat thread (APPROVAL_CHAT_CONTEXT).

  2. Secret Retrieval: Upon execution confirmation, sign_and_broadcast in src/openhuman/web3/wallet/chains/evm.rs (lines 45-66) calls secret_material() to decrypt the mnemonic and build the SecretMaterial struct.

  3. Module Signing: The wallet invokes modules::wallet::sign_transaction_in_module with the decrypted material and chain-specific derivation parameters. The module performs the cryptographic signing and returns the raw transaction bytes.

  4. Broadcast: Chain-specific RPC layers transmit the signed transaction:

  5. Status Monitoring: The tx_status function queries receipt status, falling back to eth_getTransactionByHash for EVM networks to distinguish pending from unknown transactions.

Practical Code Examples

Preparing a Cross-Chain Transfer Quote

use openhuman::web3::wallet::execution::PrepareTransferParams;
use openhuman::web3::wallet::defaults::EvmNetwork;

let params = PrepareTransferParams {
    from_chain: WalletChain::Evm,
    to_address: "0xAbC...".into(),
    amount_raw: "0.01".into(),
    token_address: None, // native ETH transfer
    evm_network: Some(EvmNetwork::EthereumMainnet),
};

let quote = wallet::prepare_transfer(params).await?;

This validates the EVM address, estimates gas via eth_estimateGas, and stores the prepared transaction with a TTL.

Executing a Prepared Quote

use openhuman::web3::wallet::execution::ExecutePreparedParams;

let exec = ExecutePreparedParams {
    quote_id: quote.quote_id.clone(),
    confirmed: true,
};

let result = wallet::execute_prepared(exec).await?;
println!("Tx hash: {}", result.transaction_hash);

The execute_prepared function routes to chain-specific implementations like execute_evm_quote, which invoke the module-based signing flow.

Checking Transaction Status

let status = wallet::tx_status(EvmNetwork::EthereumMainnet, "0xabc123...").await?;

match status.state {
    TxState::Confirmed => println!("Confirmed!"),
    TxState::Pending => println!("Pending..."),
    TxState::NotFound => println!("Tx not found"),
}

Direct EVM Signing (Internal API)

use openhuman::web3::wallet::sign_and_broadcast_evm;

let raw = sign_and_broadcast_evm(
    EvmNetwork::Polygon,
    "0xRecipient...",
    Some("0xdeadbeef".into()),   // contract calldata
    "0",                         // zero native value
).await?;

println!("Broadcasted tx: {}", raw.transaction_hash);

Summary

  • OpenHuman's wallet uses a single encrypted BIP-39 mnemonic to derive keys for EVM, Bitcoin, Solana, and Tron networks.
  • Private keys never leave the module boundary; signing occurs inside modules::wallet::sign_transaction_in_module with attestation.
  • Chain-specific derivation paths (BIP-44 for EVM, BIP-84 for Bitcoin, Ed25519 for Solana) are handled in dedicated files under src/openhuman/web3/wallet/chains/.
  • Quote-based transactions include a 5-minute TTL and chat-thread binding to prevent session hijacking.
  • All signing flows culminate in raw transaction broadcasts via chain-appropriate RPC endpoints (Ethereum JSON-RPC, Esplora, Solana RPC, TronGrid).

Frequently Asked Questions

How does OpenHuman's wallet secure private keys during multi-chain signing?

The wallet decrypts the BIP-39 mnemonic only within the attested wallet module via modules::wallet::sign_transaction_in_module. Private keys are derived on-demand inside this module, sign the transaction, and are immediately discarded. The raw signed bytes are returned, but the private key material never exits the module process, ensuring core-owned security across all supported chains.

Which blockchain networks does OpenHuman's Web3 wallet support?

According to the source code in tinyhumansai/openhuman, the wallet supports four blockchain families: EVM networks (Ethereum, Base, Arbitrum, Optimism, Polygon, BNB Chain), Bitcoin (Mainnet and Testnet P2WPKH), Solana (Mainnet and Devnet), and Tron (Mainnet and Testnet). Each implementation resides in its respective file under src/openhuman/web3/wallet/chains/.

What derivation path does OpenHuman use for Ethereum addresses?

OpenHuman follows the BIP-44 standard with path m/44'/60'/… for all EVM networks, including Ethereum, Base, and Polygon. This logic is implemented in src/openhuman/web3/wallet/chains/evm.rs, where the wallet module derives the secp256k1 private key from this path before signing transactions via eth_sendRawTransaction.

How does the wallet prevent replay attacks on prepared transactions?

Prepared transactions include a 5-minute time-to-live (TTL) and are bound to the originating chat thread via APPROVAL_CHAT_CONTEXT. This ensures quotes expire quickly and cannot be executed outside their original session context, preventing cross-session transaction hijacking while maintaining multi-chain signing flexibility.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →