Skip to main content

Security & testing

Leontief holds user funds, so the bar is: every row in the threat model maps to a named, passing test. Zero untested rows. The suite is 98+ tests across unit, property, integration, golden-vector, and fuzz layers.

Verification layers

  • Unit — each contract's entry points, typed errors, auth.
  • Property (proptest) — accounting fairness, round-trip deposited, inflation-attack bound, liquidation rounding and termination. Case counts scale from CI to nightly (Decision #4).
  • Golden vectors — a Python decimal generator emits JSON expected values; a Rust loader asserts the contracts reproduce them byte-for-byte. The @leontief/sdk previews are asserted against the same file, so client math can't drift from chain math.
  • Integration beats — the five beats run against the full contract graph with a real SEP-8 restricted asset.
  • Fuzz — two cargo-fuzz targets (vault sequences, pool sequences) run in the nightly workflow.

Threat tie-out (excerpt)

ThreatDefenseNamed test
First-depositor inflationvirtual offset VIRT, zero-share revertinflation_attack_bounded (property)
Caller lies about amount sentbalance-diff around every transfer-indeposit_measures_balance_diff
Stale / manipulated pricefail-closed staleness + deviation breakerget_nav_rejects_stale, override_rearms_after_deviation_halt
Depositor loses value at NAV ≠ 1value-consistent legs (Decision #3)golden vault vectors, deposit_fair_at_nav
Unauthorized liquidationwhitelist gatebeat 5b, liquidate_requires_whitelist
Liquidation drains a healthy userhealth < 1 guard + close factorliquidate_only_unhealthy
User trapped during incidentexits never pausablewithdraw_works_while_paused, repay_needs_no_oracle
Rounding leaks value to usersfloor → user, ceil → protocolgolden vectors across both legs

The full matrix lives in SECURITY-TESTING.md in the repo.

Delegated surface (Telegram + Autopilot — Tranche 2 scope)

The access layer and Autopilot add a new surface. Threats and mitigations, stated before any of it ships (updated August 2026):

ThreatMitigations
Delegated-signer compromise / abuseOn-chain constraint set bounds anything the key can do: contract allow-list, per-action + daily caps, HF floor 1.6, expiry ≤ 30 days; one-tap revoke kills the delegation at the ledger; engine is flag-gated and excluded from production bundles — verified in CI
Bot impersonation / phishingSingle published handle, verified from the site footer only; never-DM-first policy; the bot never asks for keys or funds; anti-phishing copy repeated verbatim in /start
Deep-link parameter tamperingSchema validation on every parameter; links are prefill-only and never auto-submit — the user signs in their own wallet, seeing the real values
Bot token compromiseWebhook secret; rotation owner named in the secrets matrix (docs/SECRETS.md); blast radius = notifications only — the bot package contains zero signing code, CI-grepped

Runbook procedures for this surface — Autopilot pause/revoke, bot-token rotation, and the engine kill-switch — live in the ops runbook (docs/MONITORING.md).

Golden vectors

The generator (tests/fixtures/golden_gen.py) computes every expected value in Python's arbitrary-precision decimal, writes golden.json, and both the Rust contracts and the TypeScript SDK assert against it. This is what guarantees the three surfaces — spec, contract, SDK — agree to the integer.

Gates & disclosure

  • CI gate: just check must be green — fmt, clippy -D warnings, tests, and a coverage floor of ≥ 90% lines on the funds-holding contracts (currently ~95.8%). Red CI never merges.
  • Toolchain pinned: soroban-sdk 27.0.0, rustc 1.97.1, wasm32v1-none; never bumped unasked.
  • Disclosure: coordinated, 90-day, security@leontief.tech. A loss-of-funds report goes straight to the pause playbook — deposits can be halted within the hour, and exits are never paused.