Aritmética e Overflow
Programas on-chain usam inteiros de tamanho fixo com aritmética com wrap em operações não verificadas. Bugs de overflow e arredondamento drenam cofres, cunham shares sem lastro ou travam contas permanentemente.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
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;Quando usar isso:
- Cunhagem/queima de shares em cofres.
- Cálculos de swap AMM dentro de programas.
- Acúmulo de índice de juros em empréstimos.
- Qualquer
+,-,*,/em quantidades de token.
Exemplo de Trabalho
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,
}O que isso demonstra:
- Divisão após multiplicação usa margem
u128. - Primeiro depositante mint 1:1 evita divisão por zero.
- Atualizações de estado usam
checked_addpara detectar overflow.
Mergulho Profundo
Como Funciona
- Builds BPF podem não trap em overflow para operadores simples em modo release.
checked_*retornaNoneem overflow - mapeie para erro de programa.- Divisão trunca em direção a zero - favorece protocolo na política de arredondamento.
- Cálculos de taxa devem usar basis points:
amount * bps / 10_000.
Política de Arredondamento
| Operação | Padrão seguro | Por quê |
|---|---|---|
| Mint shares | Arredondar para baixo | Prevenir over-mint |
| Burn shares | Arredondar dívida para cima | Prevenir sub-colateralização |
| Taxas | Arredondar taxa para cima | Solvência do protocolo |
| Pagamento ao usuário | Arredondar pagamento para baixo | Prevenir dreno de cofre |
Notas de Rust
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;Armadilhas
+simples em saldos - Wrap silencioso perto deu64::MAX. Correção:checked_addem todo lugar em saldos de usuário.- Dividir antes de multiplicar - Perde precisão e habilita ataques de dust. Correção: Multiplique primeiro em tipo mais largo.
- Total shares zero - Divisão por zero ou ataque de inflação no primeiro depósito. Correção: Lock de liquidez mínima ou offset virtual.
- Cast trunca -
u128 as u64trunca silenciosamente. Correção:u64::try_fromcom verificação de intervalo. - Confusão signed - Usar preços
i64sem limites. Correção: Documente intervalos; usechecked_*em signed também. - Decimais de token diferentes - Misturar mints de 6 e 9 decimais. Correção: Normalize para precisão comum antes da matemática.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
Intermediários u128 | Matemática de token padrão | Hot paths críticos em CU (ainda frequentemente vale a pena) |
| Crates fixed-point | Curvas complexas | Matemática de share simples |
| Cálculo off-chain | Somente visualização | Liquidação autoritativa |
FAQs
Rust debug mode detecta overflow on-chain?
Não - BPF implantado usa semântica release; nunca confie em panics de debug.
O que é o ataque do primeiro depositante?
Atacante manipula depósito minúsculo + doação para inflar preço de share - mitigue com reservas virtuais ou depósito mínimo.
Devo usar saturating_add?
Somente quando limitar é comportamento intencional - saldos de cofre devem errar em overflow, não saturar.
Como taxas bps interagem com overflow?
Calcule em u128: amount * bps / 10_000 com ops checked em cada etapa.
Floats são ok alguma vez?
Nunca em liquidação on-chain - floats são não determinísticos entre arquiteturas.
Como audito matemática?
Mapeie toda operação aritmética; grep por +, *, / sem checked_ no src do programa.
Anchor ajuda com matemática?
Não - você implementa matemática segura; veja Aritmética Segura.
Como expoentes de oráculo se encaixam?
Escale preços para expoente comum com pow10 checked antes de combinar quantidades.
Fuzzing encontra overflow?
Sim - use fuzzing Trident com quantidades de entrada extremas (Fuzzing de Programas).
Qual custo de CU u128 adiciona?
Modesto vs custo de exploit - prefira segurança a menos que profiling prove gargalo.
Relacionados
- Aritmética Segura - padrões Rust
- Ataques de Validação de Conta - exploits econômicos
- Catálogo de Ataques Sealevel - ataques de inflação
- Fuzzing de Programas - descoberta de casos extremos
Versões da stack: Esta página foi escrita para 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 e LiteSVM 0.6.x.