Arithmetic & Overflow
On-chain programs use fixed-size integers with wrapping arithmetic in unchecked ops. Overflow and rounding bugs drain vaults, mint unbacked shares, or brick accounts permanently.
Search across all documentation pages
On-chain programs use fixed-size integers with wrapping arithmetic in unchecked ops. Overflow and rounding bugs drain vaults, mint unbacked shares, or brick accounts permanently.
Quick-reference recipe card - copy-paste ready.
let shares = deposit_amount
.checked_mul(total_shares)
.and_then(|v| v.checked_div(total_assets))
.ok_or(ErrorCode::MathOverflow)?;
// Prefer u128 intermediates for token math
let out = (amount_in as u128)
.checked_mul(reserve_out as u128)
.and_then(|n| n.checked_div((reserve_in as u128) + (amount_in as u128)))
.ok_or(ErrorCode::MathOverflow)? as u64;When to reach for this:
+, -, *, / on token amounts.use anchor_lang::prelude::*;
#[account]
pub struct Pool {
pub total_assets: u64,
pub total_shares: u64,
}
pub fn deposit(pool: &mut Pool, amount: u64) -> Result<u64> {
let shares = if pool.total_shares == 0 {
amount
} else {
(amount as u128)
.checked_mul(pool.total_shares as u128)
.and_then(|n| n.checked_div(pool.total_assets as u128))
.ok_or(ErrorCode::MathOverflow)? as u64
};
pool.total_assets = pool
.total_assets
.checked_add(amount)
.ok_or(ErrorCode::MathOverflow)?;
pool.total_shares = pool
.total_shares
.checked_add(shares)
.ok_or(ErrorCode::MathOverflow)?;
Ok(shares)
}
#[error_code]
pub enum ErrorCode {
MathOverflow,
}What this demonstrates:
u128 headroom.checked_add to catch overflow.[profile.release] overflow-checks = true in the audited workspace's Cargo.toml first - Anchor's template sets it, but it is routinely stripped for CU savings. This is the cheapest concrete audit action on this page.checked_*: an overflow panic aborts the instruction with a generic error instead of your typed one.checked_* returns None on overflow - map to program error.amount * bps / 10_000.| Operation | Safe default | Why |
|---|---|---|
| Mint shares | Round down | Prevent over-mint |
| Burn shares | Round up debt | Prevent under-collateral |
| Fees | Round up fee | Protocol solvency |
| User payout | Round down payout | Prevent vault drain |
use anchor_lang::solana_program::native_token::LAMPORTS_PER_SOL;
const BPS: u128 = 10_000;
let fee = (amount as u128)
.checked_mul(fee_bps as u128)
.and_then(|v| v.checked_add(BPS - 1)) // ceil div trick
.and_then(|v| v.checked_div(BPS))
.ok_or(ErrorCode::MathOverflow)? as u64;+ on balances - Silent wrap near u64::MAX. Fix: checked_add everywhere on user balances.u128 as u64 silently truncates. Fix: u64::try_from with range check.i64 prices without bounds. Fix: Document ranges; use checked_* on signed too.| Alternative | Use When | Don't Use When |
|---|---|---|
u128 intermediates | Standard token math | CU-critical hot paths (still often worth it) |
| Fixed-point crates | Complex curves | Simple share math |
| Off-chain calculation | View only | Authoritative settlement |
Deployed programs use release semantics, so it depends on [profile.release] overflow-checks. Anchor 0.32.1's generated workspace sets it to true (plain operators panic); a raw cargo build-sbf release build without it wraps silently. Verify the flag rather than relying on debug panics.
Attacker manipulates tiny deposit + donation to inflate share price - mitigate with virtual reserves or min deposit.
Only when capping is intended behavior - vault balances should error on overflow, not saturate.
Compute in u128: amount * bps / 10_000 with checked ops at each step.
Never in on-chain settlement - floats are non-deterministic across architectures.
Map every arithmetic op; grep for +, *, / without checked_ in program src.
No - you implement safe math; see Safe Arithmetic.
Scale prices to common exponent with checked pow10 before combining amounts.
Yes - use Trident fuzzing with extreme input amounts (Fuzzing Programs).
Modest vs exploit cost - prefer safety unless profiling proves bottleneck.
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.
Reviewed by Chris St. John·Last updated Jul 16, 2026