new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Solana & Anchor Auditor · all subjects

Arithmetic and token math

6 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Does Rust release-mode wrapping hide overflow in this program?

Rust release builds wrap on integer overflow silently (two's complement) unless `overflow-checks = true` is set — and Anchor's `anchor init` template DOES enable it in `[profile.release]`, but hand-rolled programs, modified templates, and dependency crates doing their own arithmetic often don't. In BPF/SBF builds, a wrapping `u64` in balance math = classic underflow-mint: `balance - amount` where `amount > balance` wraps to a huge number if the check was `>=` on a different variable or missing. Rule: use `checked_add/sub/mul` with explicit errors for all value math regardless of profile flags (flags are a safety net, not the control); watch `as u64`/`as u128` casts that truncate (e.g., `u128` intermediate cast back to `u64`); and verify `cargo build-bpf`/`build-sbf` actually inherits the workspace profile. Detection signal: raw `+`/`-`/`*` on lamports, token amounts, or shares.

Is share/exchange-rate math exploitable by first-depositor inflation?

Solana vaults and lending pools implement the same shares pattern as EVM vaults and inherit the same bug: `shares = deposit * total_shares / total_assets`. First depositor donates directly to the asset account (SPL transfer straight to the vault token account — nothing stops this), inflating `total_assets` so the next depositor's share calculation rounds down to zero or near-zero, then the attacker redeems their majority shares for everything. Solana-specific wrinkle: because anyone can transfer tokens to any token account, the "donation" vector needs no protocol interaction at all — vaults MUST account for assets via internal accounting fields or a virtual offset, not raw `token_account.amount`, or enforce a minimum initial deposit/mint dead shares. Detection signal: exchange-rate computed from live token balances; division without `mul_div`-style precision; rounding direction not favoring the protocol.

Are token decimals and lamport scales conflated anywhere?

Solana value math spans at least three scales: lamports (9 decimals), token base units (mint-defined `decimals`, commonly 6 for USDC, 9 for SOL-wrapped, anything for attacker mints), and Pyth-style prices (value * 10^expo, expo NEGATIVE). Bugs: charging a "1 token" fee computed as `1_000_000` against a 9-decimal mint; converting Pyth price to token amount with `10u64.pow(price.expo as u32)` — which panics on negative expo instead of dividing; assuming `wsol` and `sol` lamport equality mid-CPI without accounting for rent in the wrapped account; and Token-2022 interest-bearing mints whose UI amount ≠ stored amount. Rule: every conversion between two scales gets a written invariant ("amount_x = amount_y * 10^(dx-dy)") and checked math; read `decimals` from the validated mint account, never from instruction args.

Is the Pyth/Switchboard price fresh, confident, and the right feed?

Oracle findings on Solana cluster in four checks. (1) STALENESS: `price.get_price_no_older_than(&clock, max_age)` — using the deprecated unchecked getters accepts hours-old prices. (2) CONFIDENCE: Pyth publishes `conf` alongside price; high-volatility or manipulated windows widen it — protocols that ignore `conf` (require e.g. `conf/price < 1-2%`) trade on garbage. (3) FEED IDENTITY: the price account's key must equal the expected feed (stored in config) — a wrong-but-valid Pyth account for a correlated or fake feed passes deserialization fine; also verify `owner == pyth_program`. (4) EMA vs spot misuse: `ema_price` for liquidations can lag or be gamed differently than spot. Switchboard equivalents: `AggregatorAccountData` staleness via `latest_confirmed_round` timestamps. As of early 2026, Pyth push (Solana-mainnet price-service accounts) and pull patterns coexist — check which model the program assumes.

Can MEV on Solana exploit this transaction flow?

Solana MEV differs from EVM: historically no public mempool, so classic sandwiching required spam/leader-based strategies; since Jito (~2022 onward) out-of-protocol block-engine auctions and bundles enable atomic backruns and sandwich-style ordering via tips, and as of early 2026 Jito tips and priority fees (compute-unit price) dominate ordering. Protocol-level exposures: single-tx oracle-update + action flows where an attacker bundles their trade immediately after a price update (mitigate with staleness tolerance and confidence checks, or commit-reveal); liquidations sniped by bundled backruns (acceptable, but check incentive math); AMM swaps without slippage bounds (`minimum_out = 0` is a finding every time); and dutch-auction/auction-settlement instructions callable by anyone where the keeper extracts the spread. Audit question for every price-sensitive instruction: "who profits from ordering this transaction, and what bounds did the user set?"

How should flash-loan-adjacent logic handle same-slot oracle prices?

Solana's atomic multi-instruction transactions make the flash-loan-then-act sequence free of execution risk, so any price-sensitive instruction must refuse prices updated in the CURRENT slot. Concretely: check the oracle's publish slot (Pyth's `curr_slot`/`valid_slot` vs `Clock::get()?.slot`); if the price was written this slot, it may reflect manipulation staged earlier in the SAME transaction — funded by a flash loan taken in a prior instruction and repaid in a later one. Requiring the price to be at least one slot old (updated in a PRIOR slot) forces manipulation to persist across a slot boundary, where arbitrageurs and liquidators can attack it. Combine with standard Pyth hygiene: confidence-interval gating (`conf/price` bounded), a staleness ceiling, and feed-address pinning. Audit signal: liquidation, mint/redeem, or borrow instructions reading an oracle with no slot-age check — especially in programs that also offer flash loans or accept uncollateralized same-tx composition.

Give your agent this brain