Skip to content

Conversation

@omerlerinman
Copy link

@omerlerinman omerlerinman commented Sep 9, 2025

Summary

This PR updates Certora’s CVL documentation with practical, field-tested examples and tips derived from a recent formal verification effort. The additions sharpen guidance around methods, hooks, functions, ghosts, rules, and invariants, with solver-friendly rounding envelopes and storage patterns aligned to modern ERC‑4626 and rewards-controller designs.

Origin

  • Source materials: aave-v3-horizon/tree/main/certora/stata/specs
  • This PR is part of an experiment to assess whether Codex (AI) is suitable for augmenting Certora’s documentation using concrete CVL patterns extracted from customer formal verification projects.

Motivation

  • Bridge the gap between reference docs and real-world CVL practice.
  • Codify precision patterns for rounding envelopes, storage hooks, and ghost aggregates that reduce timeouts and avoid common vacuity/unsoundness traps.
  • Share safe, reusable patterns for ERC‑4626-like wrappers and rewards logic.

Key Updates

  • Methods
  • Added practical patterns for:
    • Contract aliases via using to target receivers in multi-contract scenes.
    • Using envfree for pure/view-like calls.
    • Summarization to speed up verification (NONDET, DISPATCHER(true)).
    • When to prefer with (env e) vs. envfree to keep models small.
  • Hooks
    • Added real-world storage hooks:
      • Slot/offset Sload gating to reduce spurious models for aToken/underlying/reward token instances.
      • Sstore hook mirroring balance deltas into a ghost sum for scalable invariants.
      • Notes on when slot-level addressing is safe vs. named paths.
  • Functions
    • Added math and rounding summaries:
      • mulDivCVL with explicit rounding direction.
      • Tight mulDiv up/down abstractions that are solver-friendly.
      • WAD helpers and discrete ratio/quotient models to constrain typical cases (2x/5x/100x).
      • Caution on ghost-based math axioms under fixed-point rounding.
  • Ghosts
    • Added patterns from practice:
      • Single-ghost aggregations (sum of scaled balances) via Sstore updates.
      • Guidance on conservative axioms for ghost math to avoid over-constraining the model.
  • Rules
    • Added examples and edge cases drawn from ERC‑4626 + rewards:
      • Preview equals actual (stronger than EIP requirement) and preview independence from allowance.
      • Joining/splitting additivity bounds within ±1 envelope.
      • Deposit upper bounds by index (index > RAY: +1 aToken; index == RAY: +0.5 aToken).
      • Non-zero mint condition when assets*RAY >= index.
      • Mint envelopes: receiver gets in [shares, shares+1].
      • Duplicate reward claim prevention (both sufficient and insufficient pool balances).
  • Invariants
    • Added a practical solvency envelope invariant for ERC‑4626-style wrappers, with filtered/preserved blocks to avoid vacuity and control environment assumptions.
  • Patterns
    • New page: docs/user-guide/patterns/rounding-envelopes.md centralizes ERC‑4626 rounding bounds and examples.

Files Touched

Implementation Notes

  • Examples were integrated where they best contextualize the concepts (methods/hooks/functions/rules/invariants) and also centralized under a dedicated “rounding envelopes” pattern page.
  • Slot/offset hooks use stable storage layouts from the harness; included with cautions to prefer named access paths when available.

Build & QA

  • Local build: run in a venv
    • python -m venv .venv && source .venv/bin/activate
    • pip install -r requirements.txt
    • make html
  • Spelling: make spelling; add domain terms to spelling_wordlist.txt as needed.
  • Links: make linkcheck; we fixed broken links found in link_output.txt; remaining warnings are about missing local example files not present in this repo (non-blocking).
  • Open local site: open build/html/index.html

Risks

  • Slot/offset hooks must match exact storage layouts; usage is documented with cautions.
  • Aggressive math axioms can introduce unsoundness; guidance favors inequalities and conservative bounds.

Follow-ups

  • Extend “Rounding Envelopes” with withdraw/redeem envelopes if desired.
  • Add call-trace snippets to reinforce counterexample intuition.

Acknowledgements

  • Patterns adapted from Aave Stata Token specs: aave-v3-horizon/tree/main/certora/stata/specs
  • This PR assesses Codex’s suitability for AI-assisted doc updates by harvesting CVL best practices from customer FV projects.

@omerlerinman omerlerinman marked this pull request as draft September 9, 2025 07:47
@omerlerinman omerlerinman added the existing feature new documentation for an existing feature label Sep 9, 2025
@omerlerinman omerlerinman changed the title Docs: Enrich CVL guidance with real-world ERC‑4626 and rewards patterns (from Aave Stata specs) Enrich CVL guidance with Codex experimnet: ERC‑4626 and rewards patterns (from Aave Stata specs) Sep 9, 2025
@omerlerinman omerlerinman marked this pull request as ready for review September 9, 2025 10:04
Copy link
Contributor

@shellygr shellygr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dear lord. almost 100% AI slop

```cvl
// Rounds up or down depending on a Math.Rounding enum
function mulDivCVL(uint256 x, uint256 y, uint256 denominator, Math.Rounding rounding) returns uint256 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incomplete. Math is defined in OpenZeppelin's Math.sol, so if it is somehow not part of the project we work on, the Math.Rounding won't type check

}
```

- Tight, solver-friendly models for up/down `mulDiv` used across specs:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what "Tight" means

Comment on lines +67 to +85
function mulDivDownAbstractPlus(uint256 x, uint256 y, uint256 z) returns uint256 {
uint256 res;
require z != 0;
uint256 xy = require_uint256(x * y);
uint256 fz = require_uint256(res * z);
require xy >= fz;
require fz + z > to_mathint(xy);
return res;
}
function mulDivUpAbstractPlus(uint256 x, uint256 y, uint256 z) returns uint256 {
uint256 res;
require z != 0;
uint256 xy = require_uint256(x * y);
uint256 fz = require_uint256(res * z);
require xy >= fz;
require fz + z > to_mathint(xy);
if (xy == fz) { return res; }
return require_uint256(res + 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would appreciate someone using thses summaries daily to say whether they're really good

```

```{warning}
Ghost-based math models can be powerful but require care. For example, a ghost power function `_ghostPow` with axioms like `x^0==1`, monotonicity, and bounds is useful for reasoning, but equality-like axioms may be invalid under fixed-point rounding. Keep axioms conservative and prefer inequality bounds.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did not understand this part

Comment on lines +102 to +113
function discreteQuotientMulDiv(uint256 x, uint256 y, uint256 z) returns uint256 {
uint256 res;
require z != 0 && noOverFlowMul(x, y);
require(
((x == 0 || y == 0) && res == 0) ||
(x == z && res == y) ||
(y == z && res == x) ||
constQuotient(x, y, z, 2, res) || // 1/2 or 2
constQuotient(x, y, z, 5, res) || // 1/5 or 5
constQuotient(x, y, z, 100, res) // 1/100 or 100
);
return res;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constQuotient is not defined

- Isolate environment explicitly with `with (env e)` when you need to pin `msg.sender`, `block.timestamp`, or similar for a particular call, and use plain `envfree` elsewhere. A consistent pattern across a spec keeps models smaller and avoids accidental dependencies between calls.

```{tip}
Heavyweight dependencies (Lending pools, incentive controllers) typically benefit from coarse summaries in the spec and fine-grained assertions on observable effects (balances, indices) in rules and invariants.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nonsense

Comment on lines +227 to +238
- Preview equals actual (ERC-4626). EIP-4626 requires preview functions to not exceed the actual action in the same transaction. In some implementations they are equal; this stronger property is easy to assert:

```cvl
/// previewDeposit returns the exact deposit shares
rule previewDepositAmountCheck(){
env e1; env e2;
uint256 assets; address receiver;
uint256 previewShares = previewDeposit(e1, assets);
uint256 shares = deposit(e2, assets, receiver);
assert previewShares == shares, "preview equals actual";
}
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that can be put maybe in the examples repository but not in the official docs

}
```

- Deposit envelopes by index (Aave-style index RAY). Bound deposited aTokens relative to requested `assets` and index. For `index > RAY` the bound is `+1 aToken`; for `index == RAY` it tightens to `+0.5 aToken`:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what the hell is envelops and why does codex keep mentioning them?

}
```

- Duplicate reward claims do not amplify payout. Whether the contract has sufficient funds or not, listing the same reward twice must not increase net rewards to a user beyond the computed claimable amount:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a legitimate rule but it's in the wrong section. Again, I think it should be in an examples repository for reward-distributing code

@@ -0,0 +1,102 @@
# Rounding Envelopes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again bullshit terminology

but this needs to be reviewed by someone working daily on FV of protocols

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

existing feature new documentation for an existing feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants