Core invariant¶
ELI5: The flywheel promises that votes, keepers, and routers cannot steal your collateral or change your loan. They can only move money the protocol already earned or already seized from insolvent positions.
The invariant (exact wording)¶
No vote, keeper, admin, or caller may move a solvent user's collateral, mint on a bad price, or alter live debt. Routers only move value already released or earned.
What this covers¶
| Actor | Allowed | Forbidden |
|---|---|---|
| Liquidator | liquidate when HF < 1 |
Touch solvent collateral |
settle_bad_debt |
Seize insolvent collateral | Change solvent debt |
| CoilFeeRouter | Split harvested fees | Pull from CDP |
| GaugeWeightRouter | set_allocations within caps |
Move user CDP collateral |
| SurplusSplitter | Split donated/earned surplus | Mint on bad oracle |
| TareEngine owner | List collateral, pause mint | Move solvent user deposits |
Scope limits — read carefully¶
TARE engine ≠ Keep vault
The "no admin in value path" claim applies to TareEngine — no owner function reaches a solvent position's collateral or debt.
Keep has DEFAULT_ADMIN_ROLE and STRATEGY_MANAGER_ROLE on the depositor value path. Those roles must be timelock/multisig. Documented residual risk.
Gauge scope (COMP-2)
GaugeWeightRouter holds ALLOCATOR_ROLE — gates only set_allocations, not full strategy manager powers.
Oracle halt = no bad mints¶
Pricing halts when feeds disagree or go stale (oracle_lib). The engine reverts rather than minting against a manipulated price.
Flywheel connection¶
Every router doc links back here:
- CoilFeeRouter — fees only
- SurplusSplitter — surplus only
- GaugeWeightRouter — allocation weights only
See Trust model and Composition seams.
What can go wrong¶
What the invariant does NOT promise
- Keep strategy manager compromise → depositor fund risk
- Solver misbehavior on Coil → settlement failures, not CDP theft
- Smart contract bugs → invariant is intent, not formal verification on mainnet
Deep dive: seized collateral only
withdraw_seized moves collateral already taken via settle_bad_debt, tracked in seized_collateral — not live user deposits.