Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/certora-prover.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ jobs:
job-name: "Verified Rules"
certora-key: ${{ secrets.CERTORAKEY }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
59 changes: 59 additions & 0 deletions certora/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
This folder contains the verification of EulerEarn using the Certora Prover.

The verification was inspired by the previous verification projects for Metamorpho and SiloVault (see https://github.com/morpho-org/metamorpho/tree/main/certora)

## Getting started

The code is compiled using 2 versions of solidity, which must be installed to run the verification as:

- `solc-0.8.17` for solidity compiler version 0.8.17 which is used for compiling Permit2
- `solc-0.8.19` for solidity compiler version 0.8.26 which is used for compiling the rest of the solidity files.

Follow Certora installation instructions: https://docs.certora.com/en/latest/docs/user-guide/install.html and the repository build installation for EulerEarn.

## Folders and file structure

The [`certora/specs`](specs) folder contains the following files:

- [`ConsistentState.spec`](specs/ConsistentState.spec) checks various properties specifying what is the consistent state of EulerEarn, what are the reachable setting configurations (such as caps and fee).
- [`Balances.spec`](specs/Balances.spec) checks that tokens are not kept on the EulerEarn contract. Any deposit ends up in the underlying vaults and any withdrawal is forwarded to the user.
- [`Enabled.spec`](specs/Enabled.spec) checks properties about enabled flag of market, notably that it correctly tracks the fact that the market is in the withdraw queue.
- [`Immutability.spec`](specs/Immutability.spec) checks that EulerEarn is immutable.
- [`Liveness.spec`](specs/Liveness.spec) checks some liveness properties of EulerEarn, notably that some emergency procedures are always available.
- [`PendingValues.spec`](specs/PendingValues.spec) checks properties on the values that are still under timelock. Those properties are notably useful to prove that actual storage variables, when set to the pending value, use a consistent value.
- [`Range.spec`](specs/Range.spec) checks the bounds (if any) of storage variables.
- [`Reentrancy.spec`](specs/Reentrancy.spec) checks that EulerEarn is reentrancy safe by making sure that there are no untrusted external calls.
- [`Reverts.spec`](specs/Reverts.spec) checks the revert conditions on entrypoints.
- [`Roles.spec`](specs/Roles.spec) checks the access control and authorization granted by the respective EulerEarn roles. In particular it checks the hierarchy of roles.
- [`Timelock.spec`](specs/Timelock.spec) verifies computations for periods during which we know the values are under timelock.

The [`certora/specs/summaries`](specs/summaries) folder contains summaries for different functions that simplify the behavior of different functions, namely mathematical functions in [`Math.spec`], and dispatching summaries that allow the prover to recognize the appropriate function to resolve to when we have external calls to underlying vaults.

The [`certora/confs`](confs) folder contains a configuration file for each corresponding specification file.

The [`certora/harnesses`](harnesses) folder contains harness contracts that enable the verification of EulerEarn, by exposing internal functions and fields.

The [`certora/mocks`](mocks) folder contains mock contracts that simplify the verification of EulerEarn, by fixing reference implementations for the underlying token and vaults.

The [`certora/scripts`](mocks) folder contains a patch that applies a simple modification to the EulerEarn contract that introduces ghost variables that simplify verification, and the `PatchAndCertoraRun.sh` script that is used to run rules after the patch is applied.

The [`certora/gambit`](gambit) folder contains mutation testing - modified versions of the EulerEarn contract that have been injected with bugs and configuration files to run different rules on EulerEarn and the mutations to show that these bugs can be found by the given rules.

## Specification imports graph

Most rules follow the same setup starting with `Range.spec`, with the exception of Roles, Reentrancy, Immutability, Balances, and MarketInteractions that require a different setup with non-standard summaries that have assertions or modifications to rule-specific flags.

```mermaid
graph
Liveness --> Reverts
Reverts --> ConsistentState
ConsistentState --> Timelock
Timelock --> Enabled
Enabled --> PendingValues
PendingValues --> Range
Roles
Reentrancy
Immutability
Balances
LostAssets --> Range
```
6 changes: 6 additions & 0 deletions certora/confs/Balances.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"override_base_config": "certora/confs/BaseConfForInheritance.conf",
"verify": "EulerEarnHarness:certora/specs/Balances.spec",
"msg": "Euler Earn - Rules about Balances",
"loop_iter": 2
}
54 changes: 54 additions & 0 deletions certora/confs/BaseConfForInheritance.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"optimistic_loop": true,
"global_timeout": "7200",
"parametric_contracts": "EulerEarnHarness",
"rule_sanity": "basic",
"files": [
"certora/harnesses/EulerEarnHarness.sol",
"lib/ethereum-vault-connector/src/EthereumVaultConnector.sol",
"lib/euler-vault-kit/lib/permit2/src/Permit2.sol",
"src/EulerEarnFactory.sol",
"test/mocks/PerspectiveMock.sol",
"certora/mocks/VaultMock0.sol",
"certora/mocks/VaultMock1.sol",
"certora/mocks/Token0.sol",
"certora/harnesses/ERC20Helper.sol"
],
"link": [
"EulerEarnHarness:evc=EthereumVaultConnector",
"EulerEarnHarness:permit2Address=Permit2",
"EulerEarnFactory:permit2Address=Permit2",
"EulerEarnFactory:perspective=PerspectiveMock",
"VaultMock0:_asset=Token0",
"VaultMock1:_asset=Token0",
"EulerEarnHarness:_asset=Token0"
],
"packages": [
"ethereum-vault-connector=lib/ethereum-vault-connector/src",
"euler-vault-kit=lib/euler-vault-kit/src",
"forge-std=lib/forge-std/src",
"solmate=lib/euler-vault-kit/lib/permit2/lib/solmate"
],
"compiler_map": {
"EulerEarnHarness": "solc-0.8.26",
"EthereumVaultConnector": "solc-0.8.26",
"Permit2": "solc-0.8.17",
"EulerEarnFactory": "solc-0.8.26",
"PerspectiveMock": "solc-0.8.26",
"IRMLinearKink": "solc-0.8.26",
"VaultMock0": "solc-0.8.26",
"VaultMock1": "solc-0.8.26",
"Token0": "solc-0.8.26",
"ERC20Helper": "solc-0.8.26"
},
"solc_optimize": "200",
"solc_via_ir": true,
"assert_autofinder_success": true,
"contract_recursion_limit": "1",
"process": "emv",
"prover_args": [
"-cvlFunctionRevert true"
],
"build_cache": true,
"smt_timeout": "6000"
}
12 changes: 12 additions & 0 deletions certora/confs/ConsistentState.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"verify": "EulerEarnHarness:certora/specs/ConsistentState.spec",
"msg": "Euler Earn - Rules about Consistency of State",
"loop_iter": "2",
"exclude_rule" :
[
"notInWithdrawQThenNoApproval",
"addedToSupplyQThenIsInWithdrawQ",
"sanity_notInWithdrawQThenNoApproval"
],
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
13 changes: 13 additions & 0 deletions certora/confs/ConsistentStateExtras.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"override_base_config": "certora/confs/BaseConfForInheritance.conf",
"verify": "EulerEarnHarness:certora/specs/ConsistentState.spec",
"msg": "Euler Earn - Rules about Consistency of State",
"loop_iter": "2",
"rule" : [
"addedToSupplyQThenIsInWithdrawQ"
],
"prover_args": [
"-cvlFunctionRevert true",
"-split false"
]
}
15 changes: 15 additions & 0 deletions certora/confs/ConsistentStateExtras2.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"override_base_config": "certora/confs/BaseConfForInheritance.conf",
"verify": "EulerEarnHarness:certora/specs/ConsistentState.spec",
"msg": "Euler Earn - Rules about Consistency of State",
"loop_iter": "2",
"rule" : [
"notInWithdrawQThenNoApproval",
],
"prover_args": [
"-cvlFunctionRevert true",
"-destructiveOptimizations twostage",
"-depth 0"
],
"rule_sanity": "none"
}
15 changes: 15 additions & 0 deletions certora/confs/ConsistentStateExtras3Sanity.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"override_base_config": "certora/confs/BaseConfForInheritance.conf",
"verify": "EulerEarnHarness:certora/specs/ConsistentState.spec",
"msg": "Euler Earn - Rules about Consistency of State",
"loop_iter": "2",
"rule" : [
"sanity_notInWithdrawQThenNoApproval"
],
"prover_args": [
"-cvlFunctionRevert true",
"-destructiveOptimizations twostage",
"-mediumTimeout 20 -lowTimeout 20 -tinyTimeout 20 -depth 20"
],
"rule_sanity": "none"
}
18 changes: 18 additions & 0 deletions certora/confs/Conversions.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"verify": "EulerEarnHarness:certora/specs/ConversionRules.spec",
"msg": "conversion rules",
"rule": [
"conversionOfZero",
"convertToAssetsWeakAdditivity",
"convertToSharesWeakAdditivity",
"conversionWeakMonotonicity",
"conversionWeakIntegrity"
],
"loop_iter": "1",
"optimistic_summary_recursion": true,
"prover_args": [
"-depth 20",
"-cvlFunctionRevert true"
],
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
17 changes: 17 additions & 0 deletions certora/confs/ERC4626.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"verify": "EulerEarnHarness:certora/specs/EulerEarnERC4626.spec",
"msg": "ERC4626-style properties and helper properties",
"rule": [
"totalSupplyIsSumOfBalances",
"noAssetsOnEuler",
"underlyingCannotChange",
"configBalanceAndTotalSupply",
"zeroDepositZeroShares",
"redeemingAllValidity",
"zeroAllowanceOnAssets",
"onlyContributionMethodsReduceAssets"
],
"loop_iter": "1",
"optimistic_summary_recursion": true,
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
6 changes: 6 additions & 0 deletions certora/confs/Enabled.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"verify": "EulerEarnHarness:certora/specs/Enabled.spec",
"msg": "Euler Earn - Rules about Enabled Markets",
"loop_iter": "2",
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
7 changes: 7 additions & 0 deletions certora/confs/Immutability.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"verify": "EulerEarnHarness:certora/specs/Immutability.spec",
"msg": "Euler Earn - Rules about Immutability",
"loop_iter": "2",
"contract_recursion_limit": "1",
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
11 changes: 11 additions & 0 deletions certora/confs/Liveness.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"verify": "EulerEarnHarness:certora/specs/Liveness.spec",
"msg": "Euler Earn - rules about Liveness",
"loop_iter": "2",
"prover_args": [
"-depth 20",
"-cvlFunctionRevert true"
],
"exclude_rule" : "canForceRemoveMarket",
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
10 changes: 10 additions & 0 deletions certora/confs/LostAssets.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"verify": "EulerEarnHarness:certora/specs/LostAssets.spec",
"msg": "Euler Earn - Properties on Lost Assets",
"loop_iter": "1",
"prover_args": [
"-cvlFunctionRevert true",
"-solvers [z3:def{randomSeed=110},z3:def{randomSeed=111},z3:def{randomSeed=112},z3:def{randomSeed=113},z3:def{randomSeed=114},z3:def{randomSeed=115},z3:def{randomSeed=116},z3:def{randomSeed=117},z3:def{randomSeed=118},z3:def{randomSeed=119}]"
],
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
7 changes: 7 additions & 0 deletions certora/confs/PendingValues.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"verify": "EulerEarnHarness:certora/specs/PendingValues.spec",
"msg": "Euler Earn - Rules about Pending Values",
"rule_sanity": "basic",
"loop_iter": "2",
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
6 changes: 6 additions & 0 deletions certora/confs/Range.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"verify": "EulerEarnHarness:certora/specs/Range.spec",
"msg": "Euler Earn - Rules about Ranges",
"loop_iter": "2",
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
6 changes: 6 additions & 0 deletions certora/confs/Reentrancy.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"verify": "EulerEarnHarness:certora/specs/Reentrancy.spec",
"msg": "Euler Earn - Rules about Reentrancy",
"loop_iter": "2",
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
6 changes: 6 additions & 0 deletions certora/confs/Reverts.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"verify": "EulerEarnHarness:certora/specs/Reverts.spec",
"msg": "Euler Earn - rules about Reverts",
"loop_iter": "2",
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
6 changes: 6 additions & 0 deletions certora/confs/Roles.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"verify": "EulerEarnHarness:certora/specs/Roles.spec",
"msg": "Euler Earn - Rules about Roles",
"loop_iter": "2",
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
16 changes: 16 additions & 0 deletions certora/confs/Solvency.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"verify": "EulerEarnHarness:certora/specs/EulerEarnERC4626.spec",
"msg": "Solvency for all methods other than withdraw/redeem/deposit/mint",
"rule": [
"TotalAssetsMoreThanSupplyAndFees"
],
"rule_sanity" : "none",
"loop_iter": "1",
"optimistic_summary_recursion": true,
"process": "emv",
"prover_args": [
"-cvlFunctionRevert true",
"-depth 0"
],
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
13 changes: 13 additions & 0 deletions certora/confs/SolvencyInternal.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"verify": "EulerEarnHarness:certora/specs/SolvencyInternal.spec",
"msg": "Euler Earn - internal solvency",
"multi_assert_check" : true,
"loop_iter": "1",
"contract_recursion_limit": "1",
"optimistic_summary_recursion" : true,
"prover_args": [
"-cvlFunctionRevert true",
"-depth 0"
],
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
7 changes: 7 additions & 0 deletions certora/confs/Timelock.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"verify": "EulerEarnHarness:certora/specs/Timelock.spec",
"msg": "Euler Earn - Rules about Timelocks",
"loop_iter": "2",
"exclude_rule" : "removableTime",
"override_base_config": "certora/confs/BaseConfForInheritance.conf"
}
13 changes: 13 additions & 0 deletions certora/confs/TimelockRemovableTime.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"override_base_config": "certora/confs/BaseConfForInheritance.conf",
"verify": "EulerEarnHarness:certora/specs/Timelock.spec",
"msg": "Euler Earn - Rules about Timelocks",
"loop_iter": "2",
"rule" : [
"removableTime"
],
"prover_args": [
"-cvlFunctionRevert true",
"-depth 0"
]
}
24 changes: 24 additions & 0 deletions certora/harnesses/ERC20Helper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.26;

import {SafeERC20, IERC20} from "../../lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";

contract ERC20Helper {
using SafeERC20 for IERC20;

function balanceOf(address token, address user) external view returns (uint256) {
return IERC20(token).balanceOf(user);
}

function totalSupply(address token) external view returns (uint256) {
return IERC20(token).totalSupply();
}

function safeTransferFrom(address token, address from, address to, uint256 amount) external {
IERC20(token).safeTransferFrom(from, to, amount);
}

function allowance(address token, address owner, address spender) external view returns (uint256) {
return IERC20(token).allowance(owner, spender);
}
}
Loading