OpenBell security review — v0.4 program (pre-audit)
Reviewer: static review, no execution. Date: 12 Sep 2026. Scope: programs/openbell/src/lib.rs as of v0.4.
This is not a substitute for a professional audit. It is the list an auditor should start from.
Severity summary
| # | Severity | Finding | Status |
|---|---|---|---|
| 1 | Critical | register_asset is permissionless and the asset PDA is per-mint: whoever registers first binds a mint to any feed id. A squatter can pair AAPLx with the TSLA feed and every fill on that book settles at the wrong price. | Fixed: registrar-gated + admin update_asset_feed |
| 2 | Critical | deliver pays the integrator cut to whatever integrator_quote_ata the caller passes; ownership was never checked against the integrator's authority. Anyone running deliver could redirect all integrator fees to themselves. | Fixed: integrator account required; ATA owner must equal integrator.authority |
| 3 | High | Backstop vault shares are priced on USDC only. After a backstop or fill-now the vault holds stock, quote_balance drops, and a new depositor buys shares at a discount realised when the stock is relisted and sold. Existing LPs are diluted. | Fixed: deposits and withdrawals blocked while base_balance > 0 (relist + sync first). Proper fix (oracle-valued shares) deferred. |
| 4 | High | create_book is permissionless and the book PDA is per-pair: the canonical book for a mint can be squatted with a 5% premium and a hostile house. | Fixed: admin set_book_params |
| 5 | High | Token-2022 transfer hooks. All CPI transfers use transfer_checked without forwarding hook accounts. Issuer tokens with hooks (xStocks, Ondo) may fail or become stuck in the vault. | Open: needs remaining_accounts forwarding via spl_transfer_hook_interface. Must be tested against a real issuer mint before mainnet. |
| 6 | High | Oracle relayer controls two of three median inputs; within the deviation bound it can move settle price by up to ±2%. | Mitigated: deviation cap. Open: cap relayed influence to one source, or on-chain Chainlink verification (v0.4.1). |
| 7 | Medium | No pause switch. | Fixed: config.paused checked in place_order, settle, deliver, vault_fill_now, vault_deposit |
| 8 | Medium | Settlement-window DoS by placing an order every few hundred ms. | Mitigated: minimum order size; Pyth prints ~every 400 ms. Revisit with an epoch design if seen. |
| 9 | Medium | Fill-now uses a 300 s max price age; if Pyth equity feeds don't republish off-hours, fill-now is unavailable exactly when wanted. | Open: verify Pyth off-hours behaviour; add instant_max_age_secs if needed. |
| 10 | Medium | Fee-on-transfer base tokens would desync vault accounting. | Open: balance deltas or reject fee mints at register time. |
| 11 | Low | Vault-owned relisted asks cannot be cancelled. | Accepted; document. |
| 12 | Low | Integrator.referral_bps unused. | Cleanup. |
| 13 | Low | backstop_fill (house path) duplicates the vault path. | Recommend removing before audit. |
| 14 | Low | Per-order deliveries floor; totals never exceed round aggregates (verified); dust accumulates. | Accepted; sweep-dust ix later. |
Invariants (what tests and the auditor must prove)
- Conservation:
quote_vault.amount >= open_bid_quote + open_bid_quote_priority + undelivered quote owed; same for base. - No double delivery:
order.next_roundstrictly increasing; deliver(order, round) succeeds at most once. - Window: every round's
publish_time > placed_atof every order included. - Monotonic prints:
round[n].publish_time > round[n-1].publish_time. - Fill bounds: Σ delivered base ≤
round.base_crossed; Σ delivered quote ≤round.quote_crossed. - Premium split: seller + integrator + keeper + protocol == gross − print value, exactly.
- Vault solvency:
vault_quote_ata.amount >= quote_balance;vault_base_ata.amount >= base_balance. - Cancel safety: only after full delivery for all settled rounds, or after
round_lifeof feed silence. - Oracle sanity: settle price within
max_oracle_deviation_bpsof every fresh source used.
Reduced scope recommended for the first mainnet audit
Ship and audit only: init_config, update_config, register_asset (registrar), create_book, set_book_params, place_order, settle (Pyth only), deliver, cancel, pause. Defer vaults, fill-now, relist, boost/rewards, external prints, priority passes, house backstop; each returns as its own audited increment.
Operational controls before any real funds
- Upgrade authority in a 2-of-3 multisig with a 24h timelock.
- Per-book deposit caps at launch (initial $50k).
- Registrar, relayer, priority and boost authorities in the multisig.
- Public bug bounty before caps are lifted.
- Open-source
programs/before mainnet. - Fresh keys generated on an air-gapped machine; nothing from a chat session.
Process controls
- Feature freeze on
programs/until CI is green and this list is closed. - No AI agent edits to
programs/without a named human reviewer and a diff. - Every PR touching
programs/adds or updates an invariant test.
Found something? Report it privately to security@openbell.guru.