Skip to main content
Sigvex

Token Balance Invariant

Detects token operations that violate balance invariants such as mint without supply update or burn without supply decrease.

Token Balance Invariant

Overview

Remediation Guide: How to Fix Token Balance Invariant

The token balance invariant detector identifies token operations (mint, burn, transfer) that do not maintain proper accounting invariants. The fundamental invariant is that sum(all_account_balances) == total_supply. Violations lead to supply inflation/deflation, accounting discrepancies, fund loss, or protocol insolvency.

Why This Is an Issue

Token operations must maintain mathematical invariants:

  • Mint: total_supply' = total_supply + amount AND account_balance' = account_balance + amount
  • Burn: total_supply' = total_supply - amount AND account_balance' = account_balance - amount
  • Transfer: from_balance' = from_balance - amount AND to_balance' = to_balance + amount

When programs implement custom token logic (rather than using SPL Token CPI), violations can occur:

  • Mint without supply update: Account balance increases but total supply stays the same, creating phantom tokens
  • Burn without supply decrease: Tokens are removed from an account but supply count remains inflated
  • Asymmetric transfer: Source decreases by a different amount than destination increases

CWE mapping: CWE-682 (Incorrect Calculation), CWE-754 (Improper Check for Unusual Conditions).

How to Resolve

pub fn mint_tokens(mint: &mut Mint, account: &mut Account, amount: u64) -> ProgramResult {
    // Update BOTH account balance and total supply atomically
    account.amount = account.amount
        .checked_add(amount)
        .ok_or(ProgramError::ArithmeticOverflow)?;
    mint.supply = mint.supply
        .checked_add(amount)
        .ok_or(ProgramError::ArithmeticOverflow)?;

    // Invariant: sum(balances) == supply
    Ok(())
}

pub fn burn_tokens(mint: &mut Mint, account: &mut Account, amount: u64) -> ProgramResult {
    account.amount = account.amount
        .checked_sub(amount)
        .ok_or(ProgramError::InsufficientFunds)?;
    mint.supply = mint.supply
        .checked_sub(amount)
        .ok_or(ProgramError::ArithmeticOverflow)?;
    Ok(())
}

Examples

Vulnerable Code

pub fn custom_mint(account: &mut Account, amount: u64) -> ProgramResult {
    // VULNERABLE: updates balance but not total supply
    account.amount = account.amount.checked_add(amount)?;
    // Missing: mint.supply = mint.supply.checked_add(amount)?;
    Ok(())
}

Fixed Code

pub fn custom_mint(mint: &mut Mint, account: &mut Account, amount: u64) -> ProgramResult {
    account.amount = account.amount.checked_add(amount)?;
    mint.supply = mint.supply.checked_add(amount)?; // Maintains invariant
    Ok(())
}

Sample Sigvex Output

{
  "detector_id": "token-balance-invariant",
  "severity": "critical",
  "confidence": 0.70,
  "description": "Detected a token balance increase resembling a mint operation, but no corresponding total supply update was found. This violates the fundamental accounting invariant.",
  "location": { "function": "custom_mint", "offset": 1 }
}

Detection Methodology

  1. Balance update tracking: Identifies assignment statements that use addition or subtraction operations, classifying them as potential balance or supply updates.
  2. Mint pattern detection: Heuristically identifies addition operations that resemble minting (balance increase without corresponding source decrease).
  3. Burn pattern detection: Identifies subtraction operations resembling burns (balance decrease without corresponding supply decrease).
  4. Transfer validation: For CPI-based transfers, checks for prior balance validation.
  5. Proximity analysis: Verifies that supply updates occur within reasonable proximity of balance updates.

Limitations

  • Balance vs supply update classification is heuristic-based and conservative; many potential violations are not flagged to avoid false positives.
  • Programs using SPL Token CPI for all operations are inherently safe (SPL Token maintains invariants) and should not trigger findings.
  • Custom token implementations with non-standard data layouts may not be recognized.
  • Cross-instruction invariant violations (where mint and supply update are in separate instructions) are not detected.

References