Skip to main content
Skip to main content

Wallet Management

web3agent includes a built-in wallet manager for server-side and autonomous agent use. It handles key generation, encrypted storage, activation, derivation, and audit logging without requiring external key management infrastructure.

This is designed for agents that sign transactions with their own keys. For browser apps where the user signs, see Browser Wallet Flows.


Key Sources

web3agent resolves the active signing key in this order:

  1. PRIVATE_KEY environment variable (highest priority)
  2. MNEMONIC environment variable (BIP-39 mnemonic, derives the first account)
  3. The currently activated wallet from ~/.web3agent/wallet.json

If none of these are set, write operations will fail. Read-only operations (quotes, explorer lookups, market data) work without a wallet.


Wallet Tools

The wallet tools are available via the MCP server and the CLI. They're also callable via runtime.invokeTool() in the Runtime API.

ToolWhat it does
wallet_generateCreate a new wallet and store it encrypted
wallet_activateSet a stored wallet as the active signer
wallet_deactivateClear the active wallet
wallet_deriveDerive a child account from the active mnemonic
wallet_get_addressGet the address of the active wallet
wallet_listList all stored wallets
wallet_sign_messageSign an arbitrary message (EIP-191)
wallet_exportExport the private key or mnemonic of a stored wallet

Generating a Wallet

npx web3agent tool call wallet_generate --input '{}' --json
{
  "ok": true,
  "data": {
    "address": "0x742d35Cc6634C0532925a3b8D4C9C0F7b3e1234",
    "id": "wallet_abc123",
    "mnemonic": "word1 word2 word3 ... word12"
  }
}

The mnemonic is shown once at generation time. Store it somewhere safe immediately. After this call, only the encrypted key is kept on disk.

Via the Runtime API:

const result = await runtime.invokeTool("wallet_generate", {});
if (result.ok) {
  console.log(`New wallet: ${result.data.address}`);
  // Store result.data.mnemonic in your secrets manager NOW
}

Activating a Wallet

Generated wallets are stored but not active by default. Activate one to make it the default signer:

npx web3agent tool call wallet_activate \
  --input '{"id":"wallet_abc123"}' \
  --json

Or activate by address:

npx web3agent tool call wallet_activate \
  --input '{"address":"0x742d35Cc6634C0532925a3b8D4C9C0F7b3e1234"}' \
  --json

Only one wallet can be active at a time. Activating a new one deactivates the previous one.


Listing Wallets

npx web3agent tool call wallet_list --input '{}' --json
{
  "ok": true,
  "data": {
    "wallets": [
      {
        "id": "wallet_abc123",
        "address": "0x742d35Cc6634C0532925a3b8D4C9C0F7b3e1234",
        "active": true,
        "createdAt": "2026-04-30T10:00:00Z"
      }
    ]
  }
}

Getting the Active Address

npx web3agent tool call wallet_get_address --input '{}' --json
{
  "ok": true,
  "data": {
    "address": "0x742d35Cc6634C0532925a3b8D4C9C0F7b3e1234"
  }
}

Useful for confirming which wallet is active before executing a write operation.


BIP-39 Mnemonic and Derivation

If you set a MNEMONIC environment variable, web3agent derives the first account (m/44'/60'/0'/0/0) by default. You can derive additional accounts with wallet_derive:

npx web3agent tool call wallet_derive \
  --input '{"index":1}' \
  --json
{
  "ok": true,
  "data": {
    "address": "0x8a3f...",
    "derivationPath": "m/44'/60'/0'/0/1",
    "index": 1
  }
}

This is useful for multi-agent setups where each agent gets its own derived account from a single seed phrase.

// Derive accounts for 5 agents from one mnemonic
const agents = await Promise.all(
  [0, 1, 2, 3, 4].map(index =>
    runtime.invokeTool("wallet_derive", { index }),
  ),
);

Signing Messages

npx web3agent tool call wallet_sign_message \
  --input '{"message":"Hello from web3agent"}' \
  --json
{
  "ok": true,
  "data": {
    "signature": "0x...",
    "address": "0x742d35Cc6634C0532925a3b8D4C9C0F7b3e1234"
  }
}

This produces an EIP-191 personal sign. For EIP-712 typed data signing, use the evm_sign_typed_data tool.


Exporting Keys

Private keys and mnemonics should never be committed to version control, logged, or transmitted over unencrypted channels. Treat them with the same care as passwords.

npx web3agent tool call wallet_export \
  --input '{"id":"wallet_abc123","format":"privateKey"}' \
  --json
{
  "ok": true,
  "data": {
    "privateKey": "0x..."
  }
}

Export is available for migration purposes (moving a key to a hardware wallet or secrets manager). After exporting, consider the key compromised if the export happened on an untrusted machine.


Storage and Security

Wallets are stored at ~/.web3agent/wallet.json. The file is encrypted at rest using AES-256. The encryption key is derived from a machine-specific secret.

Key facts:

  • Keys never leave the local machine through web3agent itself.
  • The wallet file is not synced to any cloud service by web3agent.
  • All wallet operations are written to an audit log at ~/.web3agent/audit.log.
  • The PRIVATE_KEY and MNEMONIC env vars take priority over stored wallets, so you can always override without modifying stored state.

Audit Logging

Every wallet operation generates an audit log entry:

2026-04-30T10:00:00Z [wallet] generate address=0x742d... id=wallet_abc123
2026-04-30T10:01:00Z [wallet] activate id=wallet_abc123 address=0x742d...
2026-04-30T10:05:00Z [wallet] sign_message address=0x742d... message_hash=0x...

The audit log is append-only and stored locally. It doesn't include private keys or mnemonics.


Deactivating a Wallet

npx web3agent tool call wallet_deactivate --input '{}' --json

After deactivation, write operations will fail unless PRIVATE_KEY or MNEMONIC is set in the environment. Useful for locking down an agent between trading sessions.


Multi-Agent Pattern

For systems with multiple agents, each with its own wallet, the recommended pattern is:

  1. Generate one wallet per agent at setup time.
  2. Store the wallet IDs and addresses in your database.
  3. At runtime, pass the private key directly via the PRIVATE_KEY env in createRuntime() rather than relying on the stored wallet system.
import { createRuntime } from "web3agent/runtime";

// At setup: generate wallets
const setupRuntime = await createRuntime({ env: {} });
const wallet = await setupRuntime.invokeTool("wallet_generate", {});
// Store wallet.data.address in DB, wallet.data.mnemonic in secrets manager
await setupRuntime.shutdown();

// At runtime: inject the key directly
const agentRuntime = await createRuntime({
  env: {
    CHAIN_ID: "8453",
    PRIVATE_KEY: await secretsManager.get(`agent-${agentId}-key`),
    CONFIRM_WRITES: "false",
  },
});

This keeps each agent's key isolated and avoids the activation/deactivation dance when running many agents in parallel.