Chapter 4 / 15
Guards
only, when, given — declarative access control and invariants.
Covenant's guard system replaces ad-hoc require() chains with
declared pre- and post-conditions. Guards appear in the function
signature — they are part of the interface, not buried in the body.
Security note: OMEGA V4 finding KSR-CVN-011 (Critical)
discovered that a prior compiler version silently dropped only()
clauses during IR lowering — every deployed contract was wide open. V0.7 fixes
this and adds an integration test that verifies guard bytecode is emitted.
guards.cov
record Multisig {
owner: address;
guardian: address;
locked: bool;
nonce: u64;
error Unauthorized();
error AlreadyLocked();
// only(expr) — caller must satisfy expr, else revert
action change_owner(new_owner: address) only(msg.sender == self.owner) {
self.owner = new_owner;
}
// when(cond) — precondition checked before body runs
action lock() only(msg.sender == self.owner) when(!self.locked) {
self.locked = true;
}
// Multiple guards compose with AND semantics
action unlock(sig: bytes)
only(msg.sender == self.guardian)
when(self.locked)
{
self.locked = false;
self.nonce += 1;
}
// given(cond) — postcondition checked after body runs
// Reverts the entire action if the condition is false after execution
action set_guardian(g: address)
only(msg.sender == self.owner)
given(g != address(0))
given(g != self.owner)
{
self.guardian = g;
}
// Shorthand: only(self.owner) === only(msg.sender == self.owner)
action emergency_reset() only(self.owner) {
self.locked = false;
self.nonce += 1;
}
}Annotations
only(expr) | emits a JUMPI at function entry that reverts if expr is false. Shorthand only(self.owner) expands to only(msg.sender == self.owner). |
when(cond) | a precondition: checked at the start of the action body, before any state changes. |
given(cond) | a postcondition: checked after the body executes. Reverts (and undoes state changes) if false. |
| Multiple guards | compose with AND semantics — all conditions must hold. |
| KSR-CVN-011 | was critical because guards look present in source but produced zero bytecode. Always run covenant audit before deployment. |
Key takeaways
- Guards are part of the function signature — they appear in the generated ABI docs and are impossible to overlook.
given()postconditions let you express invariants that must hold after the action — useful for conservation laws.- The compiler verifies that every
only()clause produces bytecode (enforced since V0.7).