The flash loan infrastructure layer for Solana.
One protocol. Three audiences. Sixty tokens.
Plugins for AI Agents · CTX for Protocols · SDK for Developers · Docs · Discord
VAEA Flash gives three different audiences access to flash loans on Solana — each through the interface that fits them:
| Audience | Product | What it does |
|---|---|---|
| AI Agents | Plugins | Native plugins for Solana Agent Kit, GOAT SDK, and ElizaOS. One line to add aggregated flash loans to any AI agent. 5 tools, 65+ tokens, automatic routing. |
| Protocols | CTX No-CPI | A 102-byte PDA your Solana program reads to verify a flash loan is active — without consuming a single CPI level. Your program keeps its full 4-level depth. |
| Bot Developers | SDK | A TypeScript + Rust SDK that aggregates Marginfi, Kamino, and Jupiter Lend. One function call, automatic routing, zero-downtime failover, ~91µs local build. |
All three products connect to the same underlying infrastructure: the VAEA on-chain program and the Smart Router that routes across 3 lending protocols, 60+ tokens, with automatic failover.
Audience: AI agents (ElizaOS, Solana Agent Kit, GOAT SDK, LangChain, Vercel AI SDK) that need capital for on-chain strategies.
Problem: AI agents can reason about arbitrage, liquidations, and DeFi strategies — but they can't access capital. Existing flash loan tools are mono-protocol (Kamino OR Marginfi, never both). No comparison, no routing, no safety net.
Solution: VAEA provides native plugins for the two leading AI agent frameworks. One line of code gives your agent access to aggregated flash loans across 3 protocols and 65+ tokens — with automatic routing, fee guards, and profitability checks.
import { SolanaAgentKit } from "solana-agent-kit";
import { VaeaFlashPlugin } from "@vaea/solana-agent-kit-plugin";
const agent = new SolanaAgentKit(wallet, RPC_URL, { OPENAI_API_KEY })
.use(VaeaFlashPlugin()); // ← 1 line, that's itWorks with: ElizaOS, LangChain, Vercel AI SDK, custom agents.
import { vaeaFlashPlugin } from "@vaea/goat-plugin";
const tools = await getOnChainTools({
wallet: solana({ connection, keypair }),
plugins: [vaeaFlashPlugin()],
});Works with: LangChain, Vercel AI SDK, ElizaOS, any GOAT-compatible framework.
| Tool | Type | Description |
|---|---|---|
vaea_check_capacity |
👁️ Eyes | Available liquidity — all tokens or filtered by symbol |
vaea_get_quote |
👁️ Eyes | Exact fee breakdown for a specific flash loan |
vaea_find_best_route |
👁️ Eyes | Optimal route — compare Kamino vs Marginfi vs JupLend |
vaea_check_profitability |
👁️ Eyes | Is this strategy worth executing? Factors all costs |
vaea_build_flash_loan |
🔧 Build | Build unsigned atomic TX (prefix + suffix instructions) |
| Feature | Direct (Kamino/Marginfi plugin) | VAEA Plugin |
|---|---|---|
| Source comparison | ❌ Manual, per-protocol | ✅ Automatic across 3 |
| Multi-source routing | ❌ | ✅ Split across protocols |
| Fee guard | ❌ | ✅ Built-in |
| Profitability check | ❌ | ✅ Built-in |
| Tokens supported | ~20 per protocol | 65+ aggregated |
| Plugins needed | 3 separate plugins | 1 plugin |
- Build-only — plugins never touch private keys, only build unsigned transactions
- Atomic — if repay fails, entire TX reverts, zero risk
- Fee guard —
max_fee_bpsauto-rejects expensive routes
For AI agents using the Model Context Protocol (Claude Desktop, Cursor, Windsurf), VAEA also provides an MCP server with 8 tools:
{
"mcpServers": {
"vaea-flash": {
"command": "npx",
"args": ["-y", "@vaea/mcp-server"]
}
}
}| Package | Framework | Install |
|---|---|---|
@vaea/solana-agent-kit-plugin |
Solana Agent Kit v2 | npm i @vaea/solana-agent-kit-plugin |
@vaea/goat-plugin |
GOAT SDK | npm i @vaea/goat-plugin |
@vaea/mcp-server |
MCP (Claude, Cursor) | npx @vaea/mcp-server |
Audience: Solana program developers (Anchor or native) who need to know if their instruction is executing inside a flash loan.
Problem: On Solana, CPI depth is limited to 4 levels. If your protocol calls a flash loan program via CPI to verify the loan is active, that's one level consumed. In a deep call chain — ProtocolA → ProtocolB → Jupiter → AMM → Token — you've already used 4 levels. Adding a CPI for flash loan verification makes it 5. Transaction fails.
Solution: Don't CPI. Read a PDA instead.
The VAEA on-chain program creates a FlashState PDA (102 bytes) when begin_flash executes. This PDA exists only for the duration of the transaction (same-slot enforcement via sysvar introspection). Your program reads this account — a standard Solana account read, not a cross-program invocation.
// In your Anchor program — zero CPI to VAEA
use vaea_flash_ctx::{FlashLoan, FlashPolicy};
pub fn my_handler(ctx: Context<MyAccounts>) -> Result<()> {
let loan = FlashLoan::verify(
&ctx.accounts.flash_state, // 102-byte PDA
&ctx.accounts.sysvar_ix, // sysvar instructions
)?;
// loan.amount — how much was borrowed
// loan.token_mint — which token
// loan.fee_lamports — VAEA fee in native units
// v2 Security fields (unfakeable):
// loan.ix_index — position of begin_flash in the TX
// loan.gap — IX between begin_flash and end_flash
// loan.num_ix — total instructions in the TX
// Optional: enforce security policy
let policy = FlashPolicy {
max_gap: Some(5), max_ix: Some(10), max_ix_index: Some(2),
};
let loan = FlashLoan::verify_with_policy(
&ctx.accounts.flash_state, &ctx.accounts.sysvar_ix, &policy,
)?;
// Your program still has FULL CPI depth (4 levels):
marginfi::cpi::repay(...)?; // Level 1→2→3
jupiter::cpi::route(...)?; // Level 1→2→3→4 ← works
Ok(())
}With CPI flash loan verification: With VAEA CTX (No-CPI):
Protocol → FlashLoan.verify = 1 CPI Protocol reads PDA = 0 CPI
Protocol → Jupiter → AMM → Token = 4 Protocol → Jupiter → AMM → Token = 3
Proto_A → Proto_B → Jup → AMM = 5 ❌ Proto_A → Proto_B → Jup → AMM = 4 ✅
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 8 | discriminator | Anchor account discriminator |
| 8 | 32 | payer | Borrower's public key |
| 40 | 32 | token_mint | Borrowed token mint |
| 72 | 8 | amount | Amount in native units (u64) |
| 80 | 8 | fee_lamports | VAEA fee in native units (u64) |
| 88 | 1 | source_tier | 0=SDK, 1=UI, 2=Protocol |
| 89 | 8 | slot_created | Slot when loan was initiated |
| 97 | 1 | bump | PDA bump seed |
| 98 | 1 | version | Schema version (currently 2) |
| 99 | 1 | ix_index | v2 Position of begin_flash in the TX |
| 100 | 1 | gap | v2 IX count between begin_flash and end_flash |
| 101 | 1 | num_ix | v2 Total instructions in the transaction |
PDA seeds: ["flash", payer, token_mint]. Only the VAEA program (VAEAmcjQ5RB9yonyrCRSkRT8womX6uqm5PS7PXr528b) can create this account.
A flash loan borrowed directly from Kamino or Marginfi (without VAEA) does not create a FlashState PDA. There is no account to read. A program using vaea-flash-ctx only accepts flash loans that pass through VAEA.
This isn't a vendor lock-in — it's a consequence of how the verification works. The PDA is the proof. No PDA, no verification. VAEA is the only program that creates this proof without consuming CPI depth.
[dependencies]
vaea-flash-ctx = "0.1"Audience: Bot developers, liquidators, arbitrageurs — anyone who writes code and needs instant capital on Solana.
Problem: Marginfi, Kamino, and Jupiter Lend each expose their own flash loan interface with different accounts, different fees, different constraints. If one protocol is paused or exploited, your bot breaks.
Solution: One SDK, three protocols, automatic routing with zero-downtime failover.
const flash = new VaeaFlash({ connection, wallet });
await flash.executeLocal({
token: 'SOL',
amount: 1000,
onFunds: async () => [myArbIx],
});The SDK resolves the token, derives all accounts locally, picks the cheapest protocol, builds the transaction in ~91µs, signs, and sends. No HTTP call. No API dependency.
use vaea_flash_sdk::{VaeaFlash, BorrowParams};
let flash = VaeaFlash::with_rpc(
"https://api.vaea.fi",
"https://api.mainnet-beta.solana.com",
&payer,
)?;
let sig = flash.execute(BorrowParams {
token: "SOL".into(),
amount: 1000.0,
instructions: vec![my_arb_ix],
max_fee_bps: Some(10),
..Default::default()
}).await?;| Package | Install | Language |
|---|---|---|
@vaea/flash |
npm i @vaea/flash |
TypeScript |
vaea-flash-sdk |
cargo add vaea-flash-sdk |
Rust |
| Scenario | Direct Integration | Via VAEA |
|---|---|---|
| Protocol active | ✅ Works | ✅ Works |
| Protocol paused | ❌ Bot dead | ✅ Auto-reroutes |
| Protocol exploited | ❌ Bot dead | ✅ Auto-reroutes |
| Recovery time | Hours — manual code change | 0 seconds |
| Feature | What It Does |
|---|---|
| Smart Router | Compares all 3 protocols, picks the cheapest path |
| Turbo Mode | Local build in ~91µs — zero HTTP |
| Jito Bundles | Private MEV-protected execution |
| Fee Guard | Auto-rejects if fee exceeds threshold |
| Profitability Check | Factors in all costs before sending |
| Smart Retry | Adaptive retry with priority fee escalation |
| Multi-Token | Borrow SOL + USDC atomically in one TX |
| Warm Cache | Background polling for instant capacity reads |
┌─────────────────────────────────────────────────────────────┐
│ AI Agent / Bot / Script / dApp │
│ Uses: Plugins (AI agents) or SDK (devs) │
│ → Discovers liquidity → Plans route → Builds TX │
│ → Smart Router picks cheapest across 3 protocols │
│ → Creates FlashState PDA on-chain │
└────────┬────────────────────────────────────────────────────┘
│ calls your program between borrow and repay
▼
┌─────────────────────────────────────────────────────────────┐
│ Your Solana Program │
│ Uses: vaea-flash-ctx (CTX) │
│ → Reads FlashState PDA (102 bytes, 0 CPI) │
│ → Verifies: amount, token, fee, slot + security fields │
│ → Executes own logic with full 4-level CPI depth │
└─────────────────────────────────────────────────────────────┘
The plugins and SDK borrow capital. The CTX crate verifies the loan inside called programs. Together, they create a complete flash loan pipeline where the borrower gets aggregated liquidity and the called program gets Zero-CPI verification.
The Smart Router discovers tokens dynamically from all three protocols. Core tokens available on multiple protocols:
| Token | Protocols | Total Cost (VAEA fee included) |
|---|---|---|
| SOL | Marginfi + Kamino + Jupiter Lend | 2.1 bps |
| USDC | Marginfi + Kamino + Jupiter Lend | 2.1 bps |
| USDT | Marginfi + Kamino + Jupiter Lend | 2.1 bps |
| mSOL | Marginfi + Kamino + Jupiter Lend | 2.1 bps |
| JitoSOL | Marginfi + Kamino + Jupiter Lend | 2.1 bps |
| JupSOL | Marginfi + Jupiter Lend | 2 bps |
| cbBTC | Kamino + Jupiter Lend | 2.1 bps |
| JUP | Marginfi + Kamino + Jupiter Lend | 2.1 bps |
| JLP | Kamino + Jupiter Lend | 2.1 bps |
| bSOL | Marginfi + Kamino | 2.1 bps |
GET /v1/capacity— real-time borrowing capacity per token.
GET /v1/matrix— full token × protocol liquidity matrix.
If a protocol is paused, the router skips it automatically.
| Tier | Fee | Who Pays |
|---|---|---|
| SDK | 2 bps (0.02%) | Flash loan user (bots, scripts, CLI) |
| UI | 2 bps (0.02%) | Flash loan user (frontend dApps) |
| Protocol | 2 bps (0.02%) | Flash loan user (via protocol integration) |
Flat. Same rate everywhere. The fee is always paid by the end user who initiates the flash loan, not by the protocol that integrates vaea-flash-ctx. CTX integration is free for protocol developers.
Protocol fees (Kamino: ~0.1 bps, Marginfi/JupLend: 0) are added on top. Minimum fee: 1 lamport.
Base URL: https://api.vaea.fi · Rate limit: 30 req/s per IP
| Method | Endpoint | Description |
|---|---|---|
GET |
/v1/capacity |
Real-time borrowing capacity for all tokens (~2s refresh) |
GET |
/v1/capacity/aggregated |
Per-source breakdown (Marginfi / Kamino / JupLend) |
GET |
/v1/vte?token=SOL&amount=1000 |
Truth Engine — best route with cost analysis + alternatives |
GET |
/v1/quote?token=SOL&amount=1000&source=sdk |
Fee quote with full breakdown |
POST |
/v1/build |
Build flash loan instructions (prefix + suffix) |
POST |
/v1/flash/build |
Build optimized flash TX with VAEA program wrapping |
GET |
/v1/matrix |
Liquidity matrix — all tokens × all protocols |
GET |
/v1/sources |
Protocol list with token counts and fee tiers |
GET |
/v1/discovery |
Dynamic token discovery summary |
GET |
/v1/health |
System health + protocol pause status (Marginfi/Kamino/JupLend) |
GET |
/v1/stats |
API usage statistics (endpoint hit counts, uptime) |
/v1/healthresponse includes per-protocol pause detection:
protocols.marginfi.paused—trueif MarginfiPanicStateCacheis activeprotocols.kamino.paused—trueif Kaminoemergency_modeorborrow_disabledprotocols.jupiter_lend.paused— alwaysfalse(no on-chain pause flag)active_protocols/total_protocols— count of usable sources
┌─────────────────────────────────────────────────────────┐
│ Consumers │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Plugins │ │ SDK (TS) │ │ SDK (Rust) │ │
│ │ AI Agents │ │ Bot devs │ │ Bot devs │ │
│ └──────┬──────┘ └──────┬──────┘ └────────┬────────┘ │
└─────────┼────────────────┼──────────────────┼──────────┘
└────────────────┼──────────────────┘
▼
│ VAEA On-Chain Program │
│ begin_flash → [user logic] │
│ → end_flash │
│ FlashState PDA: 102 B (v2) │
│ 5 accounts per IX │
└──┬─────────┬──────────┬──────────┘
│ │ │
┌─────▼──┐ ┌────▼───┐ ┌───▼──────────┐
│Marginfi│ │ Kamino │ │ Jupiter Lend │
│ ~143 │ │ ~51 │ │ ~39 │
│ banks │ │reserves│ │ reserves │
└────────┘ └────────┘ └──────────────┘
┌────────────────────────────────────┐
│ CTX — vaea-flash-ctx v2 │
│ Reads FlashState PDA │
│ 0 CPI · ~2K CU · 102 bytes │
│ Security: ix_index, gap, num_ix │
│ Used BY protocols only │
└────────────────────────────────────┘
VAEA owns no liquidity. The Smart Router compares all three lending protocols, picks the cheapest path, and splits when needed. If a source is unavailable, it reroutes automatically.
| Property | Detail |
|---|---|
| Atomicity | If repay fails → entire TX reverts. No partial execution |
| Sysvar introspection | begin_flash ↔ end_flash pairing enforced via instruction sysvar |
| Non-custodial | Tokens flow: lending protocol → you → lending protocol |
| Permissionless | No KYC, no registration, no approval |
| PDA isolation | Seeds include token_mint — prevents cross-token collisions |
| Fee floor | Minimum 1 lamport per flash loan |
| Zero data retention | No database. Pure on-chain state reads |
| v2 ix_index | Position of begin_flash in the TX — detects exploit setup phases (many IX before borrow = unusual) |
| v2 gap | IX count between begin and end — large gap increases attack surface for price manipulation |
| v2 num_ix | Total instructions — abnormally high count (>15) indicates complex multi-step exploit pattern |
VAEA makes Solana healthier:
- ✅ Accelerates liquidations → less bad debt for lending protocols
- ✅ Facilitates arbitrage → tighter price parity across DEXes
- ✅ Gives protocols visibility into flash loans (CTX) without CPI cost
- ✅ Protocol-agnostic — doesn't favor any single lending protocol
VAEA is neutral on:
- ⚖️ Oracle manipulation risk — depends on protocol oracle design, not VAEA
- ⚖️ Liquidator concentration — liquidators still compete with each other
VAEA does not:
- ❌ Prevent all flash loan attacks — VAEA is plumbing, not a security layer
- ❌ Make Solana immune to exploits — protocols must still be well-designed
import { VaeaFlash, VaeaError } from '@vaea/flash';
try {
await flash.executeLocal(params);
} catch (err) {
if (err instanceof VaeaError) {
switch (err.code) {
case 'FEE_TOO_HIGH': // Fee > maxFeeBps
case 'INSUFFICIENT_LIQUIDITY': // Amount > available
case 'TX_EXPIRED': // Blockhash stale (auto-retried)
case 'ROUTE_UNAVAILABLE': // All sources down
case 'SIMULATION_FAILED': // TX simulation failed
case 'NETWORK_ERROR': // API unreachable
}
}
}import { VaeaFlash, VaeaError } from '@vaea/flash';
import { Connection, Keypair } from '@solana/web3.js';
import fs from 'fs';
async function main() {
const wallet = Keypair.fromSecretKey(
new Uint8Array(JSON.parse(fs.readFileSync('~/.config/solana/id.json', 'utf-8')))
);
const flash = new VaeaFlash({
connection: new Connection('https://api.mainnet-beta.solana.com'),
wallet,
preWarm: true,
});
try {
// Check profitability before sending
const profit = await flash.isProfitable({
token: 'SOL', amount: 100,
expectedRevenue: 0.1, jitoTip: 0.001,
});
if (profit.recommendation === 'abort') return;
// Execute: Turbo build + Jito bundle + adaptive retry
const sig = await flash.executeLocal({
token: 'SOL',
amount: 100,
onFunds: async () => [myArbIx],
}, {
sendVia: 'jito',
jito: { tip: 'competitive' },
retry: { maxAttempts: 3, strategy: 'adaptive' },
});
console.log('✅', sig);
} catch (err) {
if (err instanceof VaeaError) console.error(`[${err.code}]: ${err.message}`);
else throw err;
} finally {
flash.destroy();
}
}
main();Program ID: VAEAmcjQ5RB9yonyrCRSkRT8womX6uqm5PS7PXr528b
ALT: DjncKSi9KqtnFx6hFYa7ARmwJ7B4Y7UH3XpR2XEuXNJr
Mainnet: April 2026
vaea-flash/
├── plugins/
│ ├── solana-agent-kit/ # @vaea/solana-agent-kit-plugin — Agent Kit v2
│ └── goat-sdk/ # @vaea/goat-plugin — GOAT SDK
├── mcp/
│ ├── typescript/ # @vaea/mcp-server (npm) — MCP for Claude/Cursor
│ └── rust/ # vaea-mcp-server (Cargo) — MCP for Claude/Cursor
├── sdk/
│ ├── typescript/ # @vaea/flash (npm) — SDK for bot devs
│ └── rust/ # vaea-flash-sdk (crates.io) — SDK for bot devs
├── crates/
│ └── vaea-flash-ctx/ # CTX: Zero-CPI verification for protocols
├── programs/
│ └── vaea-flash/ # On-chain Anchor program
├── frontend/
│ └── web/ # vaea.fi/flash (Next.js)
├── examples/
│ ├── typescript/
│ └── rust/
└── LICENSE # MIT
Everything in this repository is MIT — use it, fork it, build on it, no restrictions.
| Component | License | Repo |
|---|---|---|
| On-chain program | MIT | This repo |
| SDK (TypeScript + Rust) | MIT | This repo |
| AI Agent Plugins | MIT | This repo |
| MCP Server | MIT | This repo |
| CTX Crate | MIT | This repo |
| Frontend | MIT | This repo |
| Smart Router / API | — | Hosted service |
The routing engine, the VTE, and the API backend are not in this repository — they run as a hosted service at api.vaea.fi. This is the same model used across the Solana ecosystem: Jupiter's routing engine is a hosted service, Jito's block engine is a hosted service, Helius's indexer is a hosted service. The SDKs, plugins, and interfaces are open — the infrastructure behind the API is operated as a service.
We believe this is the right tradeoff: you get full source access to every piece of code that touches your wallet, your transactions, and your programs. The routing logic that picks the cheapest protocol is an optimization detail — it doesn't affect the security model, and you can always call Kamino, Marginfi, or Jupiter Lend directly if you prefer.
The first investment from any funding will be a professional security audit of the on-chain program. Until then, the program is deployed on devnet. We recommend against using the CTX crate in production until the audit is complete.
