๐Ÿ›ก๏ธ Security Model

Security Model

The Zero-CPI verification pattern relies on multiple layers of validation. This page documents each security guarantee and how it prevents specific attack vectors.

Security Guarantees

PropertyMechanismAttack Prevented
Owner verificationflash_state.owner == VAEA_PROGRAM_IDForged PDA from another program
PDA derivationSeeds validated: ["flash", payer, token_mint]Cross-user or cross-token collision
Instruction introspectionSysvar scan for begin_flash before + end_flash afterStandalone call without active loan
Slot freshnessslot_created checked against current slotReplaying a PDA from a prior transaction
AtomicityIf end_flash fails โ†’ entire TX revertsPartial execution / fund extraction
Non-custodialTokens flow: protocol โ†’ user โ†’ protocolVAEA holding user funds

Can the FlashState PDA Be Forged?

No. On Solana, only a program can modify accounts it owns. The FlashState PDA is owned by the VAEA program (HoYiwkNB7a3gmZXEkTqLkborNDc976vKEUAzBm8YpK5E). A malicious program cannot:

โ€ข Create an account owned by the VAEA program โ€” only VAEA can do that
โ€ข Modify an existing FlashState's data โ€” only the owner can write
โ€ข Set its own program as the owner and fool verify() โ€” the owner check rejects it

Can a FlashState Be Replayed?

No. The FlashState PDA is created by begin_flash and closed by end_flash in the same transaction. After the transaction, the account no longer exists. Even if it could persist:

โ€ข The slot_created field would not match the current slot
โ€ข The instruction introspection would not find begin_flash / end_flash in the current TX
โ€ข Both checks must pass โ€” either one prevents replay

Instruction Introspection

verify() uses the instructions sysvar (Sysvar1nstructions1111111111111111111111111) to scan the current transaction's instruction list. It verifies:

rust
// Pseudocode of what verify() checks internally:

// 1. Find begin_flash BEFORE current instruction index
for (ix_idx = 0; ix_idx < current_ix_idx; ix_idx++) {
    if ix.program_id == VAEA_PROGRAM_ID
       && ix.data starts with BEGIN_DISCRIMINATOR {
        found_begin = true;
        break;
    }
}

// 2. Find end_flash AFTER current instruction index
for (ix_idx = current_ix_idx + 1; ix_idx < total_ixs; ix_idx++) {
    if ix.program_id == VAEA_PROGRAM_ID
       && ix.data starts with END_DISCRIMINATOR {
        found_end = true;
        break;
    }
}

// Both must be true โ€” otherwise verify() returns Err
require!(found_begin && found_end, "No active flash loan");

CPI Depth Comparison

The key advantage of CTX over CPI-based verification:

ScenarioWith CPI VerificationWith VAEA CTX
Protocol โ†’ token_program2 levels โœ…1 level โœ…
Protocol โ†’ Jupiter โ†’ AMM โ†’ token4 levels โš ๏ธ3 levels โœ…
Proto_A โ†’ Proto_B โ†’ Jupiter โ†’ AMM5 levels โŒ4 levels โœ…
text
// With CPI verification โ€” 1 level consumed:
YourProtocol.execute()
  โ””โ†’ CPI: FlashLoanProgram.is_active()    โ† 1 CPI level gone
  โ””โ†’ CPI: Jupiter.route()
       โ””โ†’ CPI: AMM.swap()
            โ””โ†’ CPI: TokenProgram.transfer() โ† Level 4 โš ๏ธ

// With VAEA CTX โ€” 0 levels consumed:
YourProtocol.execute()
  โ””โ†’ READ: flash_state PDA                โ† 0 CPI, just a read
  โ””โ†’ CPI: Jupiter.route()
       โ””โ†’ CPI: AMM.swap()
            โ””โ†’ CPI: TokenProgram.transfer() โ† Level 3 โœ…

Program Immutability

Post-mainnet, the VAEA on-chain program will have no upgrade authority. Properties:

โ€ข No admin key โ€” nobody can pause or modify the program
โ€ข No upgrade authority โ€” the bytecode is frozen
โ€ข No fee changes โ€” the 2 bps rate is hardcoded in the config PDA
โ€ข Fully permissionless โ€” anyone can use it, no registration needed

โ„น๏ธ Note
The VAEA backend (Smart Router, API) is separate from the on-chain program. The verify() flow is fully decentralized โ€” it reads on-chain data only, with no dependency on VAEA servers.
โš ๏ธ Devnet Only
Mainnet โ€” April 2026
๐Ÿ”