Invariants

Falsifiable properties the auditor actively tries to break every run — and reports a verdict on.

Overview

An invariant is a property of your protocol that must always hold — totalSupply always equals the sum of balances, a vault's share price never decreases on deposit, only the timelock can change parameters. Where a note gives the auditor context, an invariant gives it a target: each audit, the auditor actively tries to break every invariant you've defined and reports back whether it held.

That verdict is the value. You're not just hoping the auditor happened to check the property that matters most to your protocol — you're telling it exactly what to attack, and getting a run-by-run answer.

ℹ️

Invariants apply to smart-contract code only — Solidity, Vyper, Rust/Solana, Move (Aptos/Sui), Cairo. For repos with no smart-contract code (e.g. TypeScript tooling), the Invariants sub-tab is disabled.

Anatomy of an invariant

Each invariant has up to three parts:

FieldWhat it isExample
TargetThe entity the property is aboutVault, Token, StakingPool
PropertyThe falsifiable condition (required)Share price never decreases on a deposit
ExpectationOptional note on what "holding" looks likeRounding always favors the vault, never the depositor

The property is the heart of it, and it has to be falsifiable — a concrete claim that can be proven true or false by examining the code, not a vibe.

Strong propertyWeak property
Sum of all user balances equals totalSupply at all timesThe accounting is correct
Only the timelock contract can call setFeeRate()Access control is secure
Withdrawals revert when the contract is pausedThe pause mechanism works
💡

Write properties the way a fuzzer would want them: one specific, checkable claim per invariant. "The protocol is safe" can't be broken or held — it can only be ignored.

Properties are stored verbatim and must be a single line of at least 10 characters with no pipe (|) characters. Cygent rejects vague claims, design-suppression phrasing ("this is intentional", "by design"), and prompt-injection attempts on save.

Creating an invariant

Open the Knowledge tab → Invariants

On a smart-contract project, go to Knowledge → Invariants.

Set target, property, and (optionally) expectation

Keep the property atomic and falsifiable. If you're tempted to use "and", that's usually two invariants.

Choose the scope and save

Project for a property of one protocol; Global for one that holds across every smart-contract repo in the instance.

⚠️

Like notes, invariants are injected when an audit starts. Define them before you run the audit — adding one mid-run applies to the next audit, not the current one.

How invariants flow through an audit

Before audit:  you define invariants (global + project)
Audit start:   enabled invariants are written into the repo's properties for the auditor
During audit:  the auditor attempts to break each one and records the outcome
Audit end:     Cygent reads back a verdict per invariant and stores it for the run
After audit:   you see held / broken / inconclusive per invariant in the run's results

Nothing is left behind in your repo — the injected properties are reset after the audit completes.

Reading the verdicts

Each audit produces a verdict per invariant:

VerdictMeaningWhat to do
HeldThe auditor tried to break it and couldn'tGood. The property is enforced.
BrokenThe auditor found a way to violate itTreat as a finding — there's usually a linked finding with the details
InconclusiveThe auditor couldn't reach a confident answerTighten the wording and re-run — the property was likely ambiguous or unreachable
Not checkedThe auditor didn't get to it this runRe-run, or narrow scope so it's reachable

A Broken invariant is the highest-signal output Knowledge produces: you named the thing that must never happen, and the auditor showed it can. When there's an associated finding, the verdict links to it.

Best practice: treat invariants as a loop

Invariants get sharper with iteration. The first version of a property is often too broad or phrased in a way the auditor can't pin down — that's what Inconclusive is telling you.

Define your invariants before the first audit

Capture the properties that matter while the protocol design is fresh.

Run the audit and read the verdicts

Held, broken, inconclusive — each is feedback on the property and the code.

Refine and re-run

Rewrite inconclusive properties to be more specific. Split compound ones. Re-run and watch them resolve to held or broken.

Recommendations

Cygent proposes invariants too. When an audit surfaces a class of issue or a chat conversation reveals a property the protocol depends on, it shows up in the Recommendations banner. Accept to review and edit in a prefilled form before saving; Dismiss to drop it. As always, nothing is written without your review.