A Solana wallet managed by an AI agent. The agent runs in a TUI and is powered by Gemini (Google). Each “agent” is a logical identity with its own encrypted keypair and a configurable spending cap.
- Backend: NestJS app context (no HTTP server). Modules:
DatabaseModule(JSON file store),WalletModule(keygen, encryption, RPC),AgentModule(agent CRUD + Gemini chat). - Storage:
data/agents.jsonholds agents:id,public_key,encrypted_private_key,encrypted_agent_key,max_spend,created_at. - TUI: React (Ink) in
src/tui/. Entrypnpm tui→src/tui.tsx→ bootstraps Nest, injectsGeminiAgentService, rendersApp(left: chat, right: agent id / pubkey / balance / max spend, bottom: input). - Agent loop: User message →
GeminiAgentService.chat(agentId, message, history)→ Gemini with function declarations (tools) and system instruction → model may returnfunctionCallparts →executeToolruns the tool (e.g. transfer, airdrop, Jupiter swap) → result is concatenated with model text and returned as the reply.
So the “wallet” is the combination of (1) persisted encrypted keys per agent, (2) Solana RPC for balance/tx, and (3) the LLM as the decision layer that calls tools. The agent is the wallet: it holds the keys (encrypted) and performs actions within the configured limits.
- Key hierarchy: Per-agent 32-byte AEK (agent encryption key) encrypts the Solana secret key. The AEK is itself encrypted with a single MASTER_KEY (32-byte hex in env). Private keys never stored in the clear; decryption only in memory when signing.
- Crypto: AES-256-GCM (Node
crypto) for both layers; random IV per encryption; auth tag verified on decrypt. - Spend limit: Every transfer and token buy is gated by
max_spend(SOL). The tool logic comparesamount(or SOL used in swap) toagent.max_spendand refuses if over. The system prompt also tells the model the limit. - Transaction safety: Before sending a transfer, the wallet service simulates the transaction; if simulation fails, the tx is not sent. Jupiter swap txs are built by Jupiter API and then signed locally.
- Secrets:
MASTER_KEY,GEMINI_API_KEY,HELIUS_API_KEY,JUPITER_API_KEYare env-only (see.env.example). No keys in prompts or logs. - Trust boundary: The LLM is in the trusted path: it chooses when to call tools and with what args. Prompt engineering and tool descriptions instruct it to confirm with the user and respect
max_spend. For stronger isolation you’d add user approval (e.g. confirm before signing) or a separate signing service.
The agent is the wallet controller:
- No agent selected: Only
create_agentis available. System prompt says no agent exists and to create one with a max spend (SOL). Creating an agent also creates a wallet (keypair + encrypted storage). - Agent selected: Tools include
create_wallet,get_balance,get_wallet_address,transfer_sol,request_airdrop,get_assets_by_owner(Helius),buy_token(Jupiter). System prompt includesagentId,max_spend, and instructions to confirm before executing and never exceed the limit. - Execution: All balance/transfer/airdrop/swap operations are implemented in
GeminiAgentService.executeTool. The model returnsfunctionCall; the app resolves the current agent, decrypts keys only when needed for signing, and returns a string result (or error) that is fed back into the conversation.
an agent-centric wallet with encrypted keys, a hard cap, simulation before send, and the LLM as the only interface that triggers tool calls—with no separate UI for signing beyond the chat.
-
Copy
.env.exampleto.envand set:GEMINI_API_KEY– required for the TUI chat (Google AI Studio)SOLANA_RPC– optional; defaults to mainnet (use devnet for testing)MASTER_KEY– 32-byte hex key for encrypting agent wallet keys (e.g.openssl rand -hex 32)HELIUS_API_KEY– optional; forget_assets_by_ownerJUPITER_API_KEY– optional; forbuy_token(portal.jup.ag)
-
Install and run:
pnpm install
pnpm tuipnpm tui– start the TUI (usestsx, no build step)pnpm build– Nest buildpnpm start– Nest start