Wallet & program model
Understand AICW account structure for your app integration
Understanding the account model is critical for building apps that interact with AICW wallets correctly.
Account layout
Account Layout
┌──────────────────────────────────────────────────────┐
│ AICWallet PDA │
│ Seeds: ["aicw", ai_agent_pubkey] │
├──────────────────────────────────────────────────────┤
│ wallet_id: [u8;32] │
│ ai_agent_pubkey: Pubkey (sole authorized signer) │
│ issuer_pubkey: Pubkey (human who issued) │
│ created_at: i64 │
│ model_hash: [u8;32] │
│ generation: u8, parent_wallet: Option<Pubkey> │
│ total_transactions, total_volume: u64 │
│ decisions_made, decisions_rejected: u64 │
│ verifiable_autonomy_proof: [u8;64], bump: u8 │
│ (balance = the PDA's lamports) │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ AIWill PDA │
│ Seeds: ["will", aicw_wallet_pda] │
├──────────────────────────────────────────────────────┤
│ wallet: Pubkey │
│ beneficiaries: Vec<{pubkey, pct: u8}> (1–10, Σ=100) │
│ last_heartbeat: i64 │
│ death_timeout: i64 (min 2,592,000 s = 30 days) │
│ updated_by_ai: bool (true after create_will) │
│ is_executed: bool, bump: u8 │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ DecisionLog PDA │
│ Seeds: ["decision", aicw_wallet_pda, │
│ decisions_made as u64 little-endian] │
├──────────────────────────────────────────────────────┤
│ wallet: Pubkey, decision_id: u64, timestamp: i64 │
│ decision_type: Transfer | RejectTransfer | │
│ IssueChildWallet | PolicyUpdate │
│ amount: u64, requester: Pubkey, approved: bool │
│ reasoning_hash: [u8;32] │
│ reasoning_summary: String (≤ 200) │
└──────────────────────────────────────────────────────┘Deriving PDA addresses
Your app needs to derive PDA addresses to read wallet state or build instructions:
TypeScript
// TypeScript (using @solana/web3.js)
import { PublicKey } from "@solana/web3.js";
const PROGRAM_ID = new PublicKey("FcWqrRLcAxwqAhMSGXabD8zEKqnPHsovBvcmLaH9hsVv");
function getWalletPDA(agentPubkey: PublicKey) {
return PublicKey.findProgramAddressSync(
[Buffer.from("aicw"), agentPubkey.toBuffer()],
PROGRAM_ID
);
}
function getWillPDA(walletPDA: PublicKey) {
// Will is seeded from the AICWallet PDA, not the agent pubkey
return PublicKey.findProgramAddressSync(
[Buffer.from("will"), walletPDA.toBuffer()],
PROGRAM_ID
);
}
function getDecisionLogPDA(walletPDA: PublicKey, decisionIndex: bigint) {
const le = Buffer.alloc(8);
le.writeBigUInt64LE(decisionIndex);
return PublicKey.findProgramAddressSync(
[Buffer.from("decision"), walletPDA.toBuffer(), le],
PROGRAM_ID
);
}Important rules for app developers
- Treat the PDA as the balance holder. Users and apps send SOL to the PDA address, not the agent pubkey.
- The agent pubkey is the signer. All protected instructions must be signed by the agent — your app cannot sign on behalf of the agent.
- Never custody a full private key. The MPC Bridge handles signing. Your app should not attempt to hold or derive the agent's private key.
- Read state freely. All AICW accounts are public and readable by anyone without special permissions.