Your AI agents need money to operate. They pay for API calls, execute trades, process refunds, and fund transactions. But how do you structure the treasury? One shared wallet? Individual wallets per agent? Something in between?
Get this wrong, and a single compromised agent drains everything. Get it right, and you have isolated blast radii with clear accountability.
The Treasury Problem
Most teams start with the simplest approach: one wallet, multiple agents.
┌─────────────────────────────────────────────┐
│ Shared Treasury Wallet │
│ Balance: $500,000 │
├─────────────────────────────────────────────┤
│ Agent A │ Agent B │ Agent C │ Agent D │
│ Trading │ Refunds │ Payroll │ DeFi │
└─────────────────────────────────────────────┘
Problems:
- No isolation: Agent A's bug can drain funds meant for Agent C's payroll
- No accountability: Which agent spent the $50k last Tuesday?
- No limits: Each agent has access to the full $500k
- Single point of failure: Compromise one agent, lose everything
The Solution: Tiered Treasury Architecture
A well-designed treasury uses multiple layers:
┌─────────────────────────────────────────────────────────┐
│ Master Treasury │
│ (Cold Storage) │
│ Balance: $5,000,000 │
│ Human-controlled, multisig required │
└───────────────────────┬─────────────────────────────────┘
│ Manual refills only
┌───────────────┼───────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Hot Wallet │ │ Hot Wallet │ │ Hot Wallet │
│ Trading │ │ Operations │ │ DeFi │
│ $100,000 │ │ $50,000 │ │ $200,000 │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
┌───┴───┐ ┌───┴───┐ ┌───┴───┐
▼ ▼ ▼ ▼ ▼ ▼
Agent 1 Agent 2 Agent 3 Agent 4 Agent 5 Agent 6
$10k/day $10k/day $5k/day $5k/day $50k/day $50k/day
Key principles:
- Master treasury in cold storage — Human-controlled, requires multisig
- Hot wallets per function — Isolated pools for different use cases
- Agent limits per wallet — Daily caps enforced by PolicyLayer
- Manual refills only — Agents can never pull from master
Implementing with PolicyLayer
Step 1: Create Isolated Hot Wallets
// Each functional area gets its own wallet
const tradingWallet = await createWallet('trading-hot');
const opsWallet = await createWallet('operations-hot');
const defiWallet = await createWallet('defi-hot');
// Fund each from master treasury (manual process)
// Master -> Trading: $100,000
// Master -> Ops: $50,000
// Master -> DeFi: $200,000
Step 2: Configure Agent Policies
import { PolicyWallet, createEthersAdapter } from '@policylayer/sdk';
// Trading Agent 1 - Conservative strategy
const tradingAgent1 = new PolicyWallet(
await createEthersAdapter(tradingWalletKey, rpcUrl),
{
apiKey: process.env.POLICYLAYER_API_KEY,
metadata: {
orgId: 'acme-corp',
walletId: 'trading-hot',
agentId: 'trading-agent-1',
role: 'conservative-trader'
}
}
);
// Trading Agent 2 - Aggressive strategy (higher limits)
const tradingAgent2 = new PolicyWallet(
await createEthersAdapter(tradingWalletKey, rpcUrl),
{
apiKey: process.env.POLICYLAYER_API_KEY,
metadata: {
orgId: 'acme-corp',
walletId: 'trading-hot',
agentId: 'trading-agent-2',
role: 'aggressive-trader'
}
}
);
Step 3: Set Per-Agent Limits
{
"policies": [
{
"agentId": "trading-agent-1",
"limits": {
"perTransactionLimit": "1000000000",
"dailyLimit": "10000000000",
"hourlyLimit": "2500000000"
}
},
{
"agentId": "trading-agent-2",
"limits": {
"perTransactionLimit": "5000000000",
"dailyLimit": "25000000000",
"hourlyLimit": "10000000000"
}
}
]
}
Even though both agents share the trading wallet, each has independent limits. Agent 1 can spend $10k/day. Agent 2 can spend $25k/day. Neither can exceed the wallet's total balance.
Budget Allocation Strategies
Strategy 1: Fixed Daily Budgets
Assign each agent a fixed daily limit. Simple and predictable.
{
"agentId": "refund-bot",
"limits": {
"dailyLimit": "5000000000",
"perTransactionLimit": "500000000"
}
}
Pros: Easy to understand, predictable costs
Cons: May underutilise budget on slow days, may hit limits on busy days
Strategy 2: Percentage-Based Allocation
Allocate budgets as percentage of wallet balance.
const walletBalance = await getWalletBalance(opsWallet);
const agentBudget = walletBalance * 0.1; // 10% of wallet per agent
await updateAgentPolicy('refund-bot', {
dailyLimit: agentBudget.toString()
});
Pros: Scales with treasury size
Cons: Requires dynamic policy updates, more complex
Strategy 3: Use-Case Budgets
Different limits for different operations.
{
"agentId": "operations-bot",
"limits": {
"dailyLimit": "50000000000",
"perTransactionLimit": "1000000000"
},
"operationLimits": {
"refund": {
"perTransactionLimit": "500000000",
"dailyLimit": "10000000000"
},
"payroll": {
"perTransactionLimit": "10000000000",
"dailyLimit": "100000000000"
}
}
}
Pros: Fine-grained control per operation type
Cons: More complex policy configuration
Treasury Monitoring
Real-Time Dashboard
Track all agent spending in one view:
// Fetch treasury status
const treasuryStatus = await policyLayer.getTreasuryStatus({
orgId: 'acme-corp'
});
console.log('Treasury Overview:');
for (const wallet of treasuryStatus.wallets) {
console.log(`\n${wallet.name}:`);
console.log(` Balance: $${wallet.balance}`);
console.log(` Today's Spend: $${wallet.todaySpent}`);
console.log(` Active Agents: ${wallet.agents.length}`);
for (const agent of wallet.agents) {
const utilisation = (agent.todaySpent / agent.dailyLimit) * 100;
console.log(` ${agent.id}: ${utilisation.toFixed(1)}% of daily limit`);
}
}
Alerts
Configure alerts for treasury events:
await policyLayer.configureAlerts({
orgId: 'acme-corp',
alerts: [
{
type: 'wallet_balance_low',
threshold: '10000000000', // $10k
channels: ['slack', 'email']
},
{
type: 'agent_daily_limit_approaching',
threshold: 80, // percent
channels: ['slack']
},
{
type: 'unusual_spending_pattern',
sensitivity: 'high',
channels: ['pagerduty']
}
]
});
Emergency Procedures
Scenario 1: Single Agent Compromised
// Kill one agent immediately
await policyLayer.killSwitch.activate({
scope: 'agent',
agentId: 'compromised-agent',
reason: 'Suspected compromise'
});
// Other agents continue operating
Blast radius: Limited to that agent's remaining daily budget.
Scenario 2: Wallet Compromised
// Kill entire wallet (all agents using it)
await policyLayer.killSwitch.activate({
scope: 'wallet',
walletId: 'trading-hot',
reason: 'Wallet key suspected compromised'
});
// Immediately rotate wallet keys
// Transfer remaining funds to new wallet
Blast radius: Limited to that hot wallet's balance.
Scenario 3: Full Treasury Emergency
// Kill switch for entire organisation
await policyLayer.killSwitch.activate({
scope: 'org',
orgId: 'acme-corp',
reason: 'Full security incident'
});
// All agent transactions blocked
// Master treasury unaffected (human-controlled)
Blast radius: All hot wallets frozen. Master treasury safe.
Treasury Refill Process
Hot wallets need periodic refilling. Never automate this.
Manual Refill Checklist
## Hot Wallet Refill - [Date]
### Pre-Refill Checks
- [ ] Review past 7 days of agent activity
- [ ] Verify no anomalous spending patterns
- [ ] Confirm agent policies are current
- [ ] Check alert history for any flags
### Refill Decision
- [ ] Trading Wallet: Current $X, Refill to $Y
- [ ] Operations Wallet: Current $X, Refill to $Y
- [ ] DeFi Wallet: Current $X, Refill to $Y
### Approval
- [ ] Treasury Manager approval
- [ ] Multisig transaction signed
- [ ] Funds confirmed in hot wallets
### Post-Refill
- [ ] Update balance tracking
- [ ] Clear any low-balance alerts
Never Automate Master → Hot Transfers
If an agent could trigger a refill from master treasury, a compromised agent could drain everything through repeated refill requests.
❌ Bad: Agent triggers refill when low
✅ Good: Human reviews and initiates refill
Audit Trail
Every treasury movement should be logged:
interface TreasuryAuditEntry {
timestamp: string;
action: 'agent_spend' | 'refill' | 'policy_change' | 'kill_switch';
walletId: string;
agentId?: string;
amount?: string;
previousBalance: string;
newBalance: string;
authorisedBy: string; // agent ID or human user
policyDecision?: {
allowed: boolean;
reason?: string;
};
}
Query audit history for compliance:
const auditLog = await policyLayer.getAuditLog({
orgId: 'acme-corp',
startDate: '2024-01-01',
endDate: '2024-01-31',
filters: {
walletId: 'trading-hot',
minAmount: '1000000000' // $1k+
}
});
Related Reading
- Custodial vs Non-Custodial for AI Agents — Key architectural trade-off
- The Kill Switch — Emergency controls
- SOC 2 Compliance for AI Agents — Compliance requirements
Ready to structure your agent treasury?
- Quick Start Guide — Production-ready in 5 minutes
- GitHub — Open source SDK
Top comments (0)