Lean 4 Formal verification EVM
Verity
Write smart contracts in Lean. Compile to EVM. Prove them correct.
- Pipeline
- Spec → EDSL → IR → Yul
- Surface
- Storage, guards, events, typed externals
- Assurance
- Machine-checked claims, explicit assumptions
Side by side
The same contract, lifted into Lean.
Same shape as Solidity: guards, ABI, events, externals. All of it visible to proofs.
SolidityRuntime implementation surface
contract Escrow {
address public owner;
mapping(address => uint256) public balances;
event Deposited(address indexed account, uint256 amount);
event Withdrawn(address indexed account, uint256 amount);
modifier onlyOwner() {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
function deposit() external payable {
balances[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
function withdraw(uint256 amount) external onlyOwner {
require(amount <= balances[msg.sender], "INSUFFICIENT_BALANCE");
bool ok = auditHook(msg.sender, amount);
require(ok, "AUDIT_REJECTED");
balances[msg.sender] -= amount;
emit Withdrawn(msg.sender, amount);
}
}- Modifiers
- Plain verified helpers, no compiler magic.
- External calls
- Typed at the boundary; oracle assumptions are explicit.
- Storage & events
- Visible to specs, proofs, and compiler reports.
Start here
- I want to start a new contract from an ideaYour First Contract — storage → function → spec → proof → register.
- I want to port an existing Solidity contractSolidity → Verity — direct mappings, restructuring patterns, a worked port.
- I want to prove a contract that already existsProof Techniques — write the `_spec` and the `_meets_spec` theorem.
The Three-Layer Structure
Every contract is three things at once: a specification of what it should do, an implementation that runs, and a proof that ties them together.
Specification · what should be true
def mint_spec (to : Address) (amount : Uint256) (s s' : ContractState) : Prop :=
s'.storageMap 1 to = add (s.storageMap 1 to) amount ∧
s'.storage 2 = add (s.storage 2) amount ∧
storageMapUnchangedExceptKeyAtSlot 1 to s s' ∧
sameContext s s'Implementation · what runs on chain
def mint (to : Address) (amount : Uint256) : Contract Unit := do
onlyOwner
let currentBalance ← getMapping balances to
let newBalance ← requireSomeUint (safeAdd currentBalance amount) "Balance overflow"
let currentSupply ← getStorage totalSupply
let newSupply ← requireSomeUint (safeAdd currentSupply amount) "Supply overflow"
setMapping balances to newBalance
setStorage totalSupply newSupplyProof · they agree
theorem mint_meets_spec (s : ContractState) (to : Address) (amount : Uint256)
(h_owner : s.sender = s.storageAddr 0) :
let s' := ((mint to amount).run s).snd
mint_spec to amount s s' := by
simp only [mint, onlyOwner, getMapping, setMapping]
simp [h_owner]Lean checks every proof at compile time. A broken proof is a broken build.
Read next
- Architecture OverviewOne screen: EDSL → CompilationModel → IR → Yul → EVM.
- Getting StartedLocal setup and your first verification run.
- Your First ContractA guided spec-to-proof walkthrough.
- Solidity to VerityPorting an existing Solidity contract into the verity_contract surface.
- Capabilities & LimitsWhat the verified fragment expresses today, and what is partial or unsupported.
- Trust ModelWhat is verified, what is trusted, and the Layer 1/2/3 framing.
- Verification StatusTheorem inventory and proof status.
- FAQWhy Lean, why an EDSL, how Verity compares to other formal-methods tools.
For agents and tooling: see /llms.txt, or add .md to any
page URL for raw markdown.
Verity is a research project by LFG Labs .