A formally-verified perpetual futures exchange protocol for Solana with three-tier bad debt defense, constant product AMM, and rigorous security guarantees.
⚠️ EDUCATIONAL USE ONLYThis code is provided for educational and research purposes only. It has not been independently audited for production use and should not be deployed to handle real funds. Use at your own risk.
Percolator is a high-assurance decentralized exchange (DEX) protocol built on Solana that combines:
- Formal Verification: 70+ Kani proofs covering safety-critical invariants
- Three-Tier Bad Debt Defense: Insurance fund → Warmup burn → Equity haircut
- O(1) Crisis Resolution: Constant-time loss socialization via global scale factors
- Insurance Fund: Separate vault with configurable authority, fee accrual, and payout caps
- Constant Product AMM: Verified x·y=k invariant with fee accrual
- Cross-Margin Portfolio: Net exposure calculation for capital efficiency
- Adaptive PnL Vesting: Taylor series approximation for withdrawal throttling
- Zero Allocations: Pure
no_stdRust optimized for Solana BPF
Verification Coverage: 85% of production operations use formally verified functions
# Build all programs and CLI
cargo build-sbf
cargo build --release --bin percolator
# Run unit tests (257 passing)
cargo test --lib
# Run formal verification proofs
cargo kani -p proofs-kani --harness i2_conservation_2users_3steps
cargo kani -p model_safety --harness proof_c3_no_overburn
# Deploy to localnet
solana-test-validator &
./target/release/percolator -n localnet deploy --all
# Initialize exchange and run integration tests
./target/release/percolator -n localnet test --allGlobal coordinator managing collateral, portfolio margin, and cross-slab routing
Responsibilities:
- Maintain user portfolios with equity and net exposure tracking
- Manage central collateral vaults (SPL tokens, currently SOL only)
- Registry of whitelisted matcher programs
- Execute trades via CPI to matchers
- Handle liquidations when equity < maintenance margin
- Apply adaptive PnL vesting (warmup period throttling)
LP-owned order book maintaining its own state and matching logic
Responsibilities:
- Maintain local order book with price-time priority
- Update quote cache for router exposure calculations
- Verify router authority and sequence numbers (TOCTOU protection)
- Execute fills at captured maker prices
- Never holds or moves funds (router-only)
┌──────────────────┐
│ User Wallets │
└────────┬─────────┘
│ SOL deposits/withdrawals
▼
┌──────────────────┐ CPI ┌──────────────────┐
│ Router Program │─────────────▶│ Slab Programs │
│ (Authority) │◀─────────────│ (Matchers) │
│ │ read-only │ │
│ • Collateral │ │ • Order books │
│ • Portfolios │ │ • Quote cache │
│ • Liquidations │ │ • Matching │
│ • Vesting │ │ │
└──────────────────┘ └──────────────────┘
│
▼
Formally Verified
Model Safety Layer
Security Rules:
- All funds stay in router vaults
- Router → Matcher is one-way CPI (no callbacks)
- Whitelist controls which matchers can be invoked
- Sequence numbers prevent TOCTOU attacks
- Atomicity: any CPI failure aborts entire transaction
When liquidations create bad debt, the protocol uses a three-tier defense mechanism to socialize losses across winners without iterating over users.
Loss Waterfall:
-
Insurance Fund (first line of defense)
- Separate vault PDA controlled by insurance authority
- Accrues fees from trades
- Pays out during liquidations with bad debt
- Configurable authority for topup/withdrawal
- Hard caps: per-event payout (0.5% of OI) and daily limit (3% of vault)
-
Warming PnL (second line of defense)
- Burns unvested profits from users
- Only after insurance exhausted
-
Equity Haircut (final resort)
- Global scale factor applied to all users
- Only after insurance AND warmup exhausted
- Haircut = (deficit - insurance - warmup) / total_equity
use model_safety::crisis::*;
// Example: 150 SOL bad debt, 50 SOL insurance, 800 SOL equity
let mut accums = Accums::new();
accums.sigma_principal = 800_000_000_000; // 800 SOL
accums.sigma_collateral = 650_000_000_000; // 650 SOL (150 SOL deficit)
accums.sigma_insurance = 50_000_000_000; // 50 SOL
let outcome = crisis_apply_haircuts(&mut accums);
// Result:
// - Insurance drawn: 50 SOL (exhausted completely)
// - Remaining deficit: 100 SOL
// - Haircut ratio: 100 / 800 = 12.5%
// - User with 300 SOL → keeps 262.5 SOL (loses 37.5 SOL)Insurance Fund Operations:
# Initialize exchange with custom insurance authority
percolator init \
--name "Percolator DEX" \
--insurance-authority <PUBKEY>
# Top up insurance fund (requires insurance authority)
percolator insurance fund \
--exchange <EXCHANGE_PUBKEY> \
--amount 50000000000
# Withdraw surplus (requires insurance authority, fails if uncovered bad debt)
percolator insurance withdraw \
--exchange <EXCHANGE_PUBKEY> \
--amount 10000000000Verified Invariants (C1-C9):
- C1: Post-crisis solvency (or best effort)
- C2: Scales monotone (never increase)
- C3: No over-burn (bounded haircuts)
- C4: Materialization idempotent
- C5: Vesting conservation
- C8: Loss waterfall ordering (insurance → warmup → equity)
- C9: Vesting progress guarantee
Security Guarantees:
- ✅ Insurance tapped BEFORE any user haircut
- ✅ Users only haircut for (deficit - insurance) / total_equity
- ✅ Insurance authority separate from governance
- ✅ Withdrawal blocked if uncovered bad debt exists
- ✅ All transfers verified via PDA derivation
Embedded AMM for immediate liquidity with formally verified invariants.
use amm_model::*;
// Buy 1 BTC at current reserves
let result = quote_buy(
x_reserve, // 1000 BTC (scaled)
y_reserve, // 60M USD (scaled)
fee_bps, // 5 bps
1 * SCALE, // 1 BTC desired
min_liq, // Liquidity floor
)?;
// Result includes VWAP, new reserves, quote amountVerified Properties (A1-A8):
- A1: Invariant non-decreasing (fees increase k)
- A2: Reserves non-negative
- A3: No arithmetic overflow
- A4: Deterministic execution
- A5: Fee routing correctness
- A6: Price impact scales with size
- A7: Round-trip loses to fees
- A8: Min liquidity enforced
Net exposure calculation allows offsetting positions across multiple venues for capital efficiency.
Verified Property (X3):
net_exposure = Σ(base_i × price_i) across all positions
margin_required = net_exposure × margin_ratio
Benefits:
- Capital efficient hedging
- Lower liquidation risk
- Professional market maker support
Taylor series approximation f(t) = 1 - e^(-t/τ) vests PnL over a warmup period to prevent manipulation.
Verified Properties (V1-V10):
- V1: Bounded output [0, 1]
- V2: Monotonic in time
- V3: Converges to 1 asymptotically
- V9: Approximation error bounded
- V10: Monotonic in unlock time
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install Solana toolchain
sh -c "$(curl -sSfL https://release.solana.com/stable/install)"
# Install Kani (optional, for verification)
cargo install --locked kani-verifier
cargo kani setup# Build Solana programs (BPF)
cargo build-sbf
# Build specific program
cargo build-sbf --manifest-path programs/router/Cargo.toml
# Build CLI
cargo build --release --bin percolator
# Install CLI globally
cargo install --path cli# Start local validator
solana-test-validator &
# Deploy all programs
percolator deploy --all
# Or deploy to devnet
percolator --network devnet deploy --all# Create exchange instance
percolator init \
--name "Percolator DEX" \
--insurance-authority <INSURANCE_AUTHORITY_PUBKEY> \
--maintenance-margin 500 \
--initial-margin 1000
# Create a matcher/slab
percolator matcher create \
--exchange <EXCHANGE_PUBKEY> \
--symbol "SOL-USD" \
--tick-size 100 \
--lot-size 1000# All unit tests (257 passing)
cargo test --lib
# Specific packages
cargo test -p model_safety
cargo test -p amm_model
cargo test -p percolator-common
# With output
cargo test -- --nocaptureTest Coverage:
- 257 unit tests across all packages
- 33 crisis module tests
- 153 common library tests
- 42 proof harness tests
- 12 AMM tests
# CLI integration tests
./target/release/percolator -n localnet test --all
# Quick smoke tests
./target/release/percolator -n localnet test --quick
# Specific test suites
./target/release/percolator -n localnet test --crisis # Insurance exhaustion + haircut
./target/release/percolator -n localnet test --liquidations # LP and user liquidations
./target/release/percolator -n localnet test --margin # Margin system
./target/release/percolator -n localnet test --funding # Funding mechanics
# Standalone crisis test scripts
./test_insurance_crisis.sh # Insurance topup/withdrawal
./test_comprehensive_crisis.sh # E2E insurance exhaustion + haircutNew Insurance Crisis Tests:
test_insurance_fund_usage()- Verifies insurance vault PDA, TopUp/Withdraw instructionstest_loss_socialization_integration()- E2E test proving insurance exhausted before haircut- Shows concrete user impact: "User with 300 SOL → loses 37.5 SOL → keeps 262.5 SOL"
- Mathematical verification:
insurance_payout + haircut_loss = bad_debt
# Run all Kani proofs (2-5 minutes)
cargo kani -p proofs-kani
# Run specific proof categories
cargo kani -p proofs-kani --harness i2_conservation_2users_3steps # Invariant proofs
cargo kani -p model_safety --harness proof_c3_no_overburn # Crisis proofs
cargo kani -p model_safety --harness proof_d2_exact_amount # Deposit/withdrawal
# AMM invariant proofs
cargo kani -p proofs-kani --harness a1_invariant_non_decreasing_buy
cargo kani -p proofs-kani --harness a5_fee_routing
# With higher unwinding for complex proofs
cargo kani -p proofs-kani --harness i2_conservation_2users_3steps --default-unwind 8Proof Categories:
- I1-I9: Core state invariants (conservation, authorization, isolation)
- C1-C9: Crisis loss socialization properties
- D1-D5: Deposit/withdrawal correctness
- L1-L13: Liquidation safety and liveness
- O1-O7: Order book and matching properties
- LP1-LP10: Liquidity provision safety
- X1-X3: Cross-margin exposure calculation
- F1-F5: Funding rate application
- A1-A8: AMM constant product invariants
- V1-V10: PnL vesting properties
See docs/VERIFICATION.md for detailed proof coverage.
# Deposit collateral
percolator margin deposit --amount 1000000000
# Place limit order
percolator trade limit \
--matcher <MATCHER_PUBKEY> \
--side buy \
--price 50000.0 \
--size 1000000
# Place market order
percolator trade market \
--matcher <MATCHER_PUBKEY> \
--side sell \
--size 1000000
# View order book
percolator trade book <MATCHER_PUBKEY> --depth 20
# Withdraw collateral
percolator margin withdraw --amount 500000000# Add liquidity to AMM
percolator liquidity add \
--matcher <MATCHER_PUBKEY> \
--amount 10000000 \
--price 50000.0
# Remove liquidity
percolator liquidity remove \
--matcher <MATCHER_PUBKEY> \
--amount 5000000
# View LP positions
percolator liquidity show# Test haircut calculation (uses verified model)
percolator crisis test-haircut \
1000000 \ # Deficit
500000 \ # Warming PnL
300000 \ # Insurance
5000000 # Equity
# Simulate crisis scenario
percolator crisis simulate \
--exchange <EXCHANGE_PUBKEY> \
--deficit 1000000 \
--dry-run# Start liquidation keeper bot
percolator keeper run \
--exchange <EXCHANGE_PUBKEY> \
--interval 5
# Monitor only mode
percolator keeper run \
--exchange <EXCHANGE_PUBKEY> \
--interval 5 \
--monitor-onlySee cli/README.md for complete CLI documentation.
Coverage: 85% of production operations use formally verified functions
The codebase employs Kani for bit-precise model checking of safety-critical code paths.
Verification Architecture:
Production Code (programs/router/src/)
↓ model_bridge conversions
Verified Functions (crates/model_safety/src/)
↓ bounded inputs via sanitizers
Kani Proofs (crates/proofs/kani/src/)
Key Verified Components:
- ✅ Deposit/withdraw logic (D2-D5 properties)
- ✅ Liquidation safety (L1-L13 properties)
- ✅ Crisis loss socialization (C1-C9 properties)
- ✅ Order matching (O1-O7 properties)
- ✅ AMM constant product (A1-A8 properties)
- ✅ PnL vesting (V1-V10 properties)
- ✅ Net exposure calculation (X3 property)
All Verification Gaps Resolved:
- ✅ Input bounds validation (HIGH RISK) - FIXED (commit 1a2e161)
- ✅ Production haircut logic (MEDIUM RISK) - MITIGATED (uses verified arithmetic, comprehensive tests)
- ✅ Crisis module (LOW RISK) - RESOLVED (production uses verified GlobalHaircut mechanism)
Recent Security Work:
- 2025-10-30: Closed all identified verification gaps
- Input bounds: MAX_DEPOSIT/WITHDRAWAL_AMOUNT = 100M (100x sanitizer bounds)
- Haircut analysis: All arithmetic operations use verified primitives from
model_safety::math - Crisis: Production GlobalHaircut mathematically equivalent to verified crisis module
- Language: Rust (no_std, zero heap allocations)
- Framework: Pinocchio v0.9.2
- Formal Verification: Kani Model Checker
- Platform: Solana (BPF bytecode)
- Fixed-Point Math: Q64.64 format for crisis module
- Decimal Scaling: SCALE = 1,000,000 for prices and quantities
percolator/
├── programs/
│ ├── router/ # Router program (authority)
│ └── slab/ # Matcher program template
├── crates/
│ ├── model_safety/ # Formally verified models
│ │ ├── crisis/ # O(1) loss socialization
│ │ ├── deposit_withdraw/ # Collateral operations
│ │ ├── liquidation/ # Margin calls
│ │ └── warmup/ # PnL vesting
│ ├── amm_model/ # Constant product AMM
│ ├── percolator_common/ # Shared types and errors
│ └── proofs/
│ └── kani/ # Formal verification proofs
├── cli/ # Command-line interface
├── tests/ # Integration tests
└── docs/ # Detailed documentation
# Start local validator in one terminal
solana-test-validator
# Build and deploy
cargo build-sbf
./target/release/percolator -n localnet deploy --all
# Run tests
./target/release/percolator -n localnet test --all# Format code
cargo fmt
# Lint with Clippy
cargo clippy --all-targets --all-features -- -D warnings
# Run all verification
cargo kani -p proofs-kani
cargo kani -p model_safety
# Check for security issues (requires cargo-audit)
cargo audit- Implement pure Rust model in
crates/model_safety/ - Write Kani proofs in
crates/proofs/kani/ - Verify:
cargo kani -p model_safety --harness proof_new_feature - Integrate via model bridge in
programs/router/src/state/model_bridge.rs - Add production code in
programs/router/src/ - Write unit tests and integration tests
- Update documentation
The codebase is optimized for Solana's BPF environment:
- Zero heap allocations (
no_std) - Stack-only data structures
- Inline functions for hot paths
- Const generics for compile-time sizing
- Saturating arithmetic for safety
This README contains all essential documentation for Percolator. For deeper dives:
- Formal Verification: Run
cargo kani -p proofs-kanito execute 70+ safety proofs - AMM Math: See
crates/amm_model/src/math.rsfor constant product implementation - Crisis Module: See
crates/model_safety/src/crisis/for O(1) loss socialization - Code Examples: See
cli/src/for complete Solana integration patterns
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Write tests and proofs for new code
- Ensure all tests pass:
cargo test --lib - Verify formal proofs:
cargo kani -p proofs-kani - Format code:
cargo fmt - Submit a pull request
Apache-2.0
- Kani team for formal verification tools
- Pinocchio for zero-copy Solana framework
- Solana Foundation for the BPF runtime
Status: ✅ 257 tests passing | ✅ 70+ proofs verified | ✅ ALL verification gaps resolved
Last Updated: November 1, 2025