Static analysis and formal verification check your contract before deployment; runtime guards check it during every live transaction. TheDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Centurylong/sanctifier/llms.txt
Use this file to discover all available pages before exploring further.
sanctifier_guards crate provides a zero-dependency #[no_std] macro library that lets you express invariants inline in your contract code. When an invariant fails, the macro publishes a structured event to the on-chain ledger and then traps the transaction — giving you a permanent, indexer-queryable audit trail that survives the state rollback.
The guard_invariant! Macro
The core primitive exported by sanctifier_guards is the guard_invariant! macro:
| Argument | Type | Description |
|---|---|---|
env | &Env or Env | The Soroban environment handle (evaluated once). |
cond | any bool expression | The invariant to assert (evaluated exactly once). |
Error::X | any IntoVal<Env, Error> | The typed contract error to surface on violation. |
What Happens Step by Step
Evaluate condition once
The expression passed as
cond is bound to a local bool variable immediately. This guarantees the condition is evaluated exactly once, regardless of side effects. Soroban storage reads cost gas — silent double-evaluation would both miscount and overcharge:Publish the audit event
If the condition is
false, the macro publishes an inv_fail event before trapping. The event carries the condition source text as its data payload so off-chain indexers can identify which invariant fired without decoding contract state:Trap via panic_with_error!
After publishing the event, the macro calls
panic_with_error!(env, err). This surfaces the typed Error value to the Stellar host and rolls back all contract state changes. Crucially, the on-chain event published in the previous step is not rolled back — Soroban commits events before the trap, so the audit trail persists on the ledger.The event is published before the trap intentionally. Soroban’s host commits
the transaction’s event set even when the contract traps, so every invariant
violation leaves a permanent, indexer-queryable record on the ledger regardless
of whether the surrounding state changes roll back.
Topic Constants
sanctifier_guards exports two public constants used as event topics:
Adding the Crate Dependency
In your contract’sCargo.toml:
#![no_std], so it links into any Soroban WASM target without pulling in the standard library.
Complete Integration Example
The following example is drawn from the runtime-guards integration guide and shows how to wireguard_invariant! into a real vault contract that protects its deposit and withdraw entrypoints.
Step 1 — Define your error type
Step 2 — Import and use guard_invariant!
Step 3 — Pattern for pre/post state checks
For more comprehensive invariant enforcement, wrap mutating logic in a helper that checks state both before and after the operation. Define your own guard type and validation function, then callguard_invariant! with the appropriate conditions:
The inv_pass Topic for Monitoring
For contracts where you want to record successful guard checks alongside failures — useful for SLA monitoring dashboards — use the companion guard_invariant_pass! macro:
inv_fail and inv_pass topics on the same contract address to build a continuous invariant-health time series.
Result-Returning Variant
The trapping form ofguard_invariant! goes through panic_with_error!, which in the soroban-sdk testutils raises a non-unwinding panic that aborts the test runner before #[should_panic] or std::panic::catch_unwind can rescue it. For unit tests, use the Result-returning variant instead:
guard_invariant!. Only the exit mechanism differs: Err return instead of panic_with_error!.
Recommended Invariant Checklist
Balance / Supply
No negative balances or shares. Total supply equals the sum of all holder balances where applicable.
Ownership
Admin and owner addresses are initialized and non-zero before any privileged operation proceeds.
Bounded Values
Fee rates, utilization ratios, and cap values stay within their allowed ranges at all times.
State Consistency
When
total_shares == 0, assets_under_management must also equal zero, and vice versa.