Skip to main content
Sigvex

Lamport Conservation Remediation

How to fix lamport conservation violations in Solana programs.

Lamport Conservation Remediation

Overview

Related Detector: Lamport Conservation

Programs must maintain lamport conservation invariants at instruction boundaries. The fix is to validate total lamport balances before and after transfers, ensure CPI calls that move lamports are followed by balance verification, and use checked arithmetic for all lamport calculations.

Before (Vulnerable)

// Vulnerable: no conservation check
pub fn transfer_reward(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    **accounts[0].lamports.borrow_mut() -= amount;
    **accounts[1].lamports.borrow_mut() += amount;
    Ok(())
}

After (Fixed)

// Fixed: explicit conservation check
pub fn transfer_reward(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let total_before = accounts[0].lamports() + accounts[1].lamports();
    require!(accounts[0].lamports() >= amount, ProgramError::InsufficientFunds);

    **accounts[0].lamports.borrow_mut() -= amount;
    **accounts[1].lamports.borrow_mut() += amount;

    let total_after = accounts[0].lamports() + accounts[1].lamports();
    require!(total_before == total_after, ProgramError::InvalidArgument);
    Ok(())
}

Alternative Mitigations

Use SPL Token Transfers

Where possible, use the SPL Token program for transfers instead of direct lamport manipulation. The Token program enforces its own conservation invariants.

token::transfer(
    CpiContext::new(token_program.to_account_info(), Transfer {
        from: source.to_account_info(),
        to: destination.to_account_info(),
        authority: authority.to_account_info(),
    }),
    amount,
)?;

Common Mistakes

Mistake: Checking Only One Side

// WRONG: only checks the source has enough, not that total is conserved
require!(vault.lamports() >= amount, Error::InsufficientFunds);
**vault.lamports.borrow_mut() -= amount;
**user.lamports.borrow_mut() += amount;

Always verify total conservation: sum_before == sum_after.

Mistake: Overflow in Addition

// WRONG: addition could overflow, masking conservation violation
let total = vault.lamports() + user.lamports();

Use checked_add to prevent silent overflow in balance sums.

References