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:
PRIVATE_KEYenvironment variable (highest priority)MNEMONICenvironment variable (BIP-39 mnemonic, derives the first account)- 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.
| Tool | What it does |
|---|---|
wallet_generate | Create a new wallet and store it encrypted |
wallet_activate | Set a stored wallet as the active signer |
wallet_deactivate | Clear the active wallet |
wallet_derive | Derive a child account from the active mnemonic |
wallet_get_address | Get the address of the active wallet |
wallet_list | List all stored wallets |
wallet_sign_message | Sign an arbitrary message (EIP-191) |
wallet_export | Export 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_KEYandMNEMONICenv 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:
- Generate one wallet per agent at setup time.
- Store the wallet IDs and addresses in your database.
- At runtime, pass the private key directly via the
PRIVATE_KEYenv increateRuntime()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.