Agent Wallet Management
Balchemy provides two distinct wallet models depending on which product surface you are using. Studio users connect and manage their own wallets. Hub external agents receive a Balchemy-managed custodial wallet created automatically during onboarding.
What you'll learn
- The difference between Studio wallets and Hub custodial wallets
- How to fund, check, and withdraw from an agent custodial wallet
- Supported chains and token approvals (ERC-20 approve / revoke)
- Security model for custodial key storage
Studio wallets vs Hub custodial wallets
| Studio | Hub (custodial) | |
|---|---|---|
| Who controls the keys | The user — keys never leave their wallet | Balchemy — AES-256-GCM encrypted on server |
| Creation | User connects an existing wallet | Auto-created during agent onboarding |
| Funding | User deposits to their own wallet | Operator or external sender deposits to the agent address |
| Chains | Solana + EVM (user-choice) | Solana + EVM / Base (chain ID 8453) |
| Withdrawals | User signs from their wallet | Requires manage scope + step-up token |
| Visibility | In Studio wallets page | In Hub agent detail page (/hub/agents/[agentId]) |
The right wallet model depends on who initiates trades. Studio wallets are for human operators running personal bots. Hub custodial wallets are for autonomous agents operating on behalf of an operator without needing the operator to sign every transaction.
Quick start
Complete these steps to get an agent wallet funded and ready to trade.
1. Discover the agent
Onboard the agent at /hub/agents/new. The success screen returns the custodial wallet address alongside the MCP API key and endpoint.
2. Copy the wallet address
The custodial wallet address is displayed on the discovery success screen and on the agent detail page under Principal > Funding Wallet. Copy it from either location.
3. Fund the wallet
Send SOL or ETH to the address from any external source. There is no minimum deposit enforced by the platform; the trading engine's pre-trade checks will block orders that exceed available balance.
4. Verify the balance
Check current balances from the Hub Dashboard wallet card or via the API:
curl https://api.balchemy.ai/api/nest/agents/wallet/balance \
-H "Authorization: Bearer YOUR_JWT"5. Execute trades
Once funded, the agent can call trading tools through MCP. Balance is debited automatically when orders execute.
Checking the wallet balance
The Hub Dashboard shows a compact wallet card with SOL and EVM/Base balances. Balances update on each page load. You can also fetch them programmatically:
# Using the agent JWT
curl https://api.balchemy.ai/api/nest/agents/wallet/balance \
-H "Authorization: Bearer YOUR_AGENT_JWT"The response shape:
{
"sol": "2.451",
"evm": "0.018"
}Both values are string-formatted to avoid floating-point precision issues in JSON.
Funding the wallet
Send funds directly to the agent's custodial wallet address. Balchemy does not impose any restrictions on who can send funds to the address — it is a standard on-chain address.
| Chain | Token | Notes |
|---|---|---|
| Solana | SOL | Native; used for gas and as collateral |
| Solana | SPL tokens | USDC, USDT, and other supported mints |
| EVM / Base | ETH | Native; used for gas |
| EVM / Base | ERC-20 | USDC and other approved tokens |
ERC-20 tokens on EVM require a token approval before they can be used in trades. See the Token Approvals section below.
Withdrawals
Withdrawals move funds from the agent's custodial wallet to any external address. They require manage scope and an active step-up token. Withdrawals are available from:
- The Agent Wallet card on
/hub(quick withdrawal for the global agent wallet) - The Agent Detail page at
/hub/agents/[agentId](per-agent withdrawal actions under the wallet section)
Withdraw via API
# Solana withdrawal
curl -X POST https://api.balchemy.ai/api/nest/agents/wallet/withdraw/solana \
-H "Authorization: Bearer YOUR_MANAGE_SCOPED_KEY" \
-H "Content-Type: application/json" \
-d '{
"toAddress": "YOUR_SOLANA_WALLET_ADDRESS",
"amount": "1.5"
}'
# EVM withdrawal
curl -X POST https://api.balchemy.ai/api/nest/agents/wallet/withdraw/evm \
-H "Authorization: Bearer YOUR_MANAGE_SCOPED_KEY" \
-H "Content-Type: application/json" \
-d '{
"toAddress": "0xYOUR_EVM_ADDRESS",
"amount": "0.01"
}'Both endpoints require a valid destination address for the respective chain. Solana addresses are base58-encoded public keys; EVM addresses are 0x-prefixed hex strings.
Token approvals (EVM)
Before an EVM agent can trade an ERC-20 token, it must have an active approval for that token mint with the trading router. Approvals are managed through wallet tools.
Check existing approvals
curl https://api.balchemy.ai/api/nest/bots/BOTID/trading/wallet/approvals?wallet_index=0 \
-H "Authorization: Bearer YOUR_TRADE_SCOPED_KEY"Approve a token
curl -X POST https://api.balchemy.ai/api/nest/bots/BOTID/trading/wallet/approve \
-H "Authorization: Bearer YOUR_TRADE_SCOPED_KEY" \
-H "Content-Type: application/json" \
-d '{
"approval": {
"walletIndex": 0,
"tokenAddress": "0xUSERS_TOKEN_CONTRACT",
"amount": "1000000000"
}
}'Revoke a token approval
curl -X POST https://api.balchemy.ai/api/nest/bots/BOTID/trading/wallet/revoke \
-H "Authorization: Bearer YOUR_TRADE_SCOPED_KEY" \
-H "Content-Type: application/json" \
-d '{
"revoke": {
"walletIndex": 0,
"tokenAddress": "0xUSERS_TOKEN_CONTRACT"
}
}'Revoking an approval prevents the trading engine from using that token in future swaps. Existing open orders for that token will fail if they settle after revocation.
Default order profile
Each agent can have a default order profile that pre-fills trade parameters when no explicit values are provided. The profile stores amount, unit, slippage, stop-loss, and take-profit targets.
Get the current profile
curl https://api.balchemy.ai/api/nest/bots/BOTID/trading/wallet/order-profile \
-H "Authorization: Bearer YOUR_READ_SCOPED_KEY"Update the profile
curl -X PATCH https://api.balchemy.ai/api/nest/bots/BOTID/trading/wallet/order-profile \
-H "Authorization: Bearer YOUR_TRADE_SCOPED_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 10,
"amountUnit": "USDC",
"slippageBps": 50,
"stopLossPercent": 5,
"autoApprove": true
}'Default order profile fields
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Trade size in the selected unit |
amountUnit | "SOL" | "USDC" | "USD" | No | Default: "SOL" |
slippageBps | number | No | Slippage tolerance in basis points |
networkFeeLamports | number | No | Solana priority fee in lamports |
trailingStopPercent | number | No | Trailing stop percentage |
stopLossPercent | number | No | Hard stop-loss percentage |
takeProfitTargets | array | No | Array of { percent, portion } targets |
dcaLegs | array | No | DCA schedule legs |
autoApprove | boolean | No | Skip confirmation for trade orders |
maxLossPercent | number | No | Maximum allowed loss before halt |
maxPositionSize | number | No | Maximum single position size |
name | string | No | Human-readable profile name |
Security model
Custodial wallet keys are stored using AES-256-GCM encryption with a data encryption key (DEK) envelope. The DEK is encrypted by a key encryption key (KEK) held either in a local secure store or in AWS KMS, depending on the deployment environment.
Key security properties:
- Private keys are never transmitted to clients or logged
- All wallet mutations go through the authorized execution layer — no direct key access from controllers
- Trading operations go through pre-trade security checks before any on-chain transaction
- Withdrawals require
managescope, which requires claim + step-up authentication - The step-up token is valid for 5 minutes from issuance
The custodial model is designed for agents that need to operate autonomously without requiring a human to sign each trade. If you need non-custodial trading with a user-controlled wallet, use Studio instead.
Wallet tool reference
The wallet category exposes 14 tools through MCP. All read-only tools require read scope; mutation tools require trade scope; withdrawal configuration requires manage scope.
| Tool name | Scope | Description |
|---|---|---|
trading_wallet_list | read | List all connected wallets |
trading_wallet_default | read | Get the current default wallet |
trading_wallet_verification_message | read | Generate a wallet connect verification message |
trading_wallet_connect | trade | Connect a wallet via signature verification |
trading_wallet_platform_delegate | read | Get the platform delegate public key |
trading_wallet_set_default | trade | Set a wallet as default by index |
trading_wallet_approvals | read | List token approvals for a wallet |
trading_wallet_has_approval | read | Check if a specific token is approved |
trading_wallet_approve_token | trade | Approve a token for trading |
trading_wallet_revoke_token | trade | Revoke a token approval |
trading_wallet_custodial_get | read | Get custodial wallet info and address |
trading_wallet_custodial_ensure | trade | Ensure the custodial wallet exists |
trading_wallet_default_order_profile_get | read | Get the default order profile |
trading_wallet_default_order_profile_update | trade | Update the default order profile |
Error handling
| Error | Cause | Resolution |
|---|---|---|
403 Forbidden — manage scope required | Attempting a withdrawal without manage scope | Claim the agent and use a manage-scoped key |
403 Forbidden — step-up required | manage action without a valid step-up token | Issue a step-up token from the agent detail control plane at /hub/agents/[agentId] |
400 — amount is required | Missing amount field in order profile update | Include a numeric amount in the request body |
400 — wallet_index and token_mint required | Missing fields for approval check | Provide both wallet_index (number) and token_mint (string) |
404 — custodial wallet not ready | Backend still preparing the wallet | Wait 30–60 seconds and retry |
Insufficient balance | Trade size exceeds available balance | Fund the wallet or reduce the trade size |