🔐 Security & Audits
Security

Advanced Measure

💡

This page documents AggreLend’s in-protocol security measures and shows how we implement CPI (Cross-Program Invocation) guards in the Anchor-based Solana program.

Overview

AggreLend has hired experienced, security-minded Rust engineers to implement top-of-the-line protections directly into the protocol. Below we outline the measures we take. While some details get technical (and show real smart-contract code), we’ve worked to keep explanations clear and practical.

At a high level:

  • Every instruction defensively checks the transaction’s instruction sysvar, enabling full visibility into the entire transaction context (CTX)—not just the current instruction.
  • If the program detects an unknown or untrusted program involved—either because the instruction is reached via unknown CPI or because the same transaction includes foreign program instructions (e.g., flash-loan orchestration)-the smart contract aborts the transaction.
  • This approach prevents classes of exploits seen in Solana DeFi (e.g., Mango Markets – Oct 11, 2022; Nirvana Finance – Jul 28, 2022; Solvent SVT Flash Loan – Aug 26, 2023) where attackers drive the victim program from their own malicious program.

CPI Check Macro (Rust)

The AggreLend program invokes a CPI guard macro at the beginning of every entrypoint, passing the full transaction context:

check_cpi!(ctx); What the macro does Enumerates the entire transaction’s instructions for diagnostics.

Captures metadata about the current instruction (index, program_id, stack height).

Rejects execution if the instruction is reached via CPI (or via self-reentry).

Rust
#[macro_export]
macro_rules! check_cpi {
    ($ctx:expr) => {{
        use ::anchor_lang::solana_program::{
            instruction::{get_stack_height, Instruction, TRANSACTION_LEVEL_STACK_HEIGHT},
            sysvar::instructions::{load_current_index_checked, load_instruction_at_checked},
            msg,
        };
 
        let sysvar_ai = &$ctx.accounts.instruction_sysvar_account;
 
        // Current-instruction metadata
        let cur_idx = load_current_index_checked(sysvar_ai)? as usize;
        let cur_ix: Instruction = load_instruction_at_checked(cur_idx, sysvar_ai)?;
        let stack = get_stack_height();
 
        // 1) Dump every instruction in the transaction (for observability)
        let mut ix_i = 0usize;
        loop {
            match load_instruction_at_checked(ix_i, sysvar_ai) {
                Ok(ix) => {
                    // Truncate data preview to 8 bytes to keep logs short
                    let data_preview = {
                        let mut buf = [0u8; 8];
                        for (i, b) in ix.data.iter().take(8).enumerate() {
                            buf[i] = *b;
                        }
                        buf
                    };
                    msg!(
                        "IX #{}, program: {}, accts: {}, data[0..8]: 0x{:02X?}",
                        ix_i,
                        ix.program_id,
                        ix.accounts.len(),
                        data_preview
                    );
                    ix_i += 1;
                }
                Err(::anchor_lang::solana_program::program_error::ProgramError::InvalidArgument) => {
                    // End of instruction list
                    break;
                }
                Err(e) => return Err(e.into()),
            }
        }
 
        // 2) Extra diagnostics about the current instruction
        msg!(
            "Current instruction index: {}, program_id: {}, stack height: {}",
            cur_idx,
            cur_ix.program_id,
            stack
        );
 
        // 3) CPI guard logic
        if cur_ix.program_id != crate::ID || stack > TRANSACTION_LEVEL_STACK_HEIGHT {
            msg!("FORBIDDEN: instruction came via CPI (or self-reentry)!");
            return ::anchor_lang::err!(crate::error::ErrorCode::DoNotHavePermission);
        }
    }};
}
 

Why this matters: Many historical exploits begin by driving the victim program from an attacker-controlled program. If the AggreLend instruction is reached as a sub-instruction (via CPI) by a foreign program, the smart contract halts execution immediately.

These checks are fully decentralized and automatically enforced by the on-chain program. There is no centralized third party involved.

Runtime Logs (Example) These are representative logs from AggreLend’s CPI checks—showing awareness of the entire transaction, not just AggreLend instruction:

Rust
(AGGREbma2Gi9unS1mPptAcG4HmkMTLNmqcunYaSSf46b) instruction
> Program log: Instruction: Withdraw
> Program log: IX #0, program: ComputeBudget111111111111111111111111111111, accts: 0, data[0..8]: 0x[03, BC, A3, 08, 00, 00, 00, 00]
> Program log: IX #1, program: ComputeBudget111111111111111111111111111111, accts: 0, data[0..8]: 0x[02, 20, A1, 07, 00, 00, 00, 00]
> Program log: IX #2, program: AGGREbma2Gi9unS1mPptAcG4HmkMTLNmqcunYaSSf46b, accts: 16, data[0..8]: 0x[B7, 12, 46, 9C, 94, 6D, A1, 22]
> Program log: Current instruction index: 2, program_id: AGGREbma2Gi9unS1mPptAcG4HmkMTLNmqcunYaSSf46b, stack height: 1
> Invoking
Drift V2 Program
  > Program log: Instruction: Withdraw

With this visibility, the program distinguishes a normal user flow (e.g., compute budget adjustments then AggreLend) from a malicious flow (a foreign program that tries to route into AggreLend). In the latter case, the program rejects the transaction before any state changes occur.

Preventing Same-Transaction Orchestration (e.g., Flash Loans) Attackers often orchestrate multi-step sequences within a single transaction, mixing the victim protocol with unrelated programs (e.g., a flash-loan source). The smart contract automatically defends by rejecting transactions that include any program other than the System Program, Token Program, and Solana’s ComputeBudget program.

Rust
use ::anchor_lang::solana_program::{
    msg,
    program_error::ProgramError,
    sysvar::instructions::load_instruction_at_checked,
};
 
pub fn reject_foreign_programs(sysvar_ai: &anchor_lang::prelude::AccountInfo) -> anchor_lang::Result<()> {
    let mut i = 0usize;
    loop {
        match load_instruction_at_checked(i, sysvar_ai) {
            Ok(ix) => {
                // Only allow our program and the ComputeBudget program
                if ix.program_id != crate::ID
                    && ix.program_id != ::anchor_lang::pubkey!("ComputeBudget111111111111111111111111111111")
                {
                    msg!("FORBIDDEN: tx contains instruction #{} from {}", i, ix.program_id); // [!code highlight]
                    return ::anchor_lang::err!(crate::error::ErrorCode::DoNotHavePermission);
                }
                i += 1;
            }
            Err(ProgramError::InvalidArgument) => break, // End of list
            Err(e) => return Err(e.into()),
        }
    }
    Ok(())
}

Effect: If the same transaction includes a flash-loan or any unrelated program call, we abort. This single rule alone would have prevented the vast majority of recent Solana DeFi attacks.

The AggreLend program thinks to itself there is no legitimate reason for a wallet to take a flash loan in the same transaction as interacting with a simple AggreLend lending operation. Transactions that attempt this are rejected by smart contract logic.

Recommendation to Other Protocols: Even rigorously audited code can be vulnerable at integration boundaries. We recommend every Solana DeFi program implement similar transaction-context guards as long as it doesn't interfere with your programs functionality. Your system is only as strong as its weakest interface; these checks eliminate entire exploit paths proactively.