Wallets & key management
Boson is signer-agnostic. The SDK takes a Web3LibAdapter; the MCP server never sees keys (it returns unsigned transactions). The question is how your code holds and uses keys.
The five patterns
| Pattern | When | Notes |
|---|---|---|
| Browser wallet (MetaMask, Coinbase Wallet, WalletConnect, RainbowKit) | Human-facing apps | Standard; integrate via EthersAdapter(provider, provider.getSigner()). |
| Server EOA | Backend automation, dev/staging | Easy. Risky in production — protect with KMS, vault, restricted IP. |
| MPC / threshold sig (Fireblocks, Privy, Turnkey, etc.) | Multi-party custody | Bridge to ethers/viem; your provider's SDK gives you a signer. |
| Smart account (Safe, ERC-4337) | Programmable rules, batched ops, session keys | Sign through the account's userOp flow; relay via your bundler. |
| Session key | Agent commerce, mobile apps | Short-lived key scoped to specific actions, delegated from a master account. |
Pick the pattern that matches your security model. Boson doesn't care which.
Browser wallet (typical Next.js / React app)
import { CoreSDK } from "@bosonprotocol/core-sdk"
import { EthersAdapter } from "@bosonprotocol/ethers-sdk"
import { ethers } from "ethers"
const provider = new ethers.providers.Web3Provider(window.ethereum)
const signer = provider.getSigner()
const sdk = CoreSDK.fromDefaultConfig({
web3Lib: new EthersAdapter(provider, signer),
envName: "production",
configId: "production-137-0",
})Server EOA (Node script, cron, queue worker)
import { ethers } from "ethers"
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL)
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider)
// pass `wallet` to EthersAdapter the same wayIn production, never hold private keys in environment variables on a long-running host. Use AWS KMS, Hashicorp Vault, or a managed signer (Fireblocks, Privy, Turnkey). The SDK doesn't care; just hand it an object with signMessage, signTransaction, etc.
Agent wallets
Autonomous agents call state-changing tools in tight loops. Special considerations:
- Nonce management. Don't send tx N+1 before tx N is mined unless you're tracking nonces yourself.
- Gas funding. Refill the agent wallet automatically if it dips below a threshold.
- Spending caps. Use a smart account with a session key bounded by max-spend rules.
- Restart safety. On crash, check on-chain state (e.g.
get_exchanges) before re-trying — see Idempotency & retry.
For deep coverage: Build for AI agents → Wallet patterns.
Smart accounts
A Safe (multisig) can be a seller or buyer. The SDK signs typed data with the smart account; the Safe owners co-sign offline; the resulting Safe-tx is what hits the Diamond.
For ERC-4337 accounts, the SDK builds the inner calldata; you wrap it in a userOp and send to a bundler. The Diamond's meta-tx facet accepts EIP-1271 signatures (smart-account signatures), so this works out of the box.
Session keys
Use these to delegate scoped authority to an agent or mobile app:
- Master account (Safe / 4337) authorizes a session key.
- Session key signs Boson actions within a scope (max value, allowed selectors, time-boxed).
- Master can revoke any time.
Useful for sandboxing an autonomous buyer agent so it can spend ≤ $100/day on commits, redeem, and dispute — but nothing else.
Common footguns
- MetaMask switches networks asynchronously. If you switch from L1 to L2 mid-flow, your provider may still be on the old chain for a beat. Always check
provider.networkbefore broadcasting. - Hardware wallets can be slow with EIP-712. Build in a 30s timeout for
signTypedData, and don't auto-retry — you'll just trigger a second user-confirm dialog. - Don't reuse a buyer wallet as a seller treasury. It works, but it conflates accounting. Different wallets for different roles.
Next
- Signing & meta-transactions — what gets signed, and the meta-tx options.
- Build for AI agents → Wallet patterns — agent-specific deep dive.
- Going to production — checklist.