Skip to content

Commit 3d21e66

Browse files
authored
Merge pull request #465 from morpho-org/claude/certora-erc4626-roundtrip
2 parents a125c03 + 2906920 commit 3d21e66

3 files changed

Lines changed: 96 additions & 0 deletions

File tree

.github/workflows/certora.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ jobs:
1818
conf:
1919
- ConsistentState
2020
- DistinctIdentifiers
21+
- ERC4626
2122
- Enabled
2223
- Immutability
2324
- LastUpdated

certora/confs/ERC4626.conf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"files": [
3+
"certora/helpers/MetaMorphoHarness.sol"
4+
],
5+
"solc": "solc-0.8.21",
6+
"verify": "MetaMorphoHarness:certora/specs/ERC4626.spec",
7+
"loop_iter": "2",
8+
"optimistic_loop": true,
9+
"prover_args": [
10+
"-depth 0",
11+
"-timeout 300",
12+
"-smt_nonLinearArithmetic true",
13+
"-backendStrategy singlerace",
14+
"-solvers [z3:def{randomSeed=1},z3:def{randomSeed=2},z3:def{randomSeed=3},z3:def{randomSeed=4},z3:def{randomSeed=5},z3:def{randomSeed=6},z3:def{randomSeed=7},z3:def{randomSeed=8},z3:def{randomSeed=9},z3:def{randomSeed=10}]"
15+
],
16+
"rule_sanity": "basic",
17+
"server": "production",
18+
"msg": "MetaMorpho ERC4626"
19+
}

certora/specs/ERC4626.spec

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
methods {
4+
function convertToShares(uint256) external returns(uint256) envfree;
5+
function convertToAssets(uint256) external returns(uint256) envfree;
6+
function previewDeposit(uint256) external returns(uint256) envfree;
7+
function previewMint(uint256) external returns(uint256) envfree;
8+
function previewWithdraw(uint256) external returns(uint256) envfree;
9+
function previewRedeem(uint256) external returns(uint256) envfree;
10+
11+
function MetaMorpho._accruedFeeShares() internal returns (uint256, uint256) => summaryAccruedFeeShares();
12+
function ERC4626._decimalsOffset() internal returns (uint8) => summaryDecimalsOffset();
13+
function Math.mulDiv(uint256 x, uint256 y, uint256 denominator, Math.Rounding rounding) internal returns (uint256) => cvlMulDiv(x, y, denominator, rounding);
14+
}
15+
16+
ghost uint256 gTotalAssets;
17+
ghost uint256 gFeeShares;
18+
persistent ghost uint8 gDecimalsOffset;
19+
20+
function summaryAccruedFeeShares() returns (uint256, uint256) {
21+
return (gFeeShares, gTotalAssets);
22+
}
23+
24+
function summaryDecimalsOffset() returns uint8 {
25+
require to_mathint(gDecimalsOffset) <= 18;
26+
return gDecimalsOffset;
27+
}
28+
29+
// necessary because metamorpho uses unmodelable 512 bits math.
30+
function cvlMulDiv(uint256 x, uint256 y, uint256 denominator, Math.Rounding rounding) returns uint256 {
31+
if (rounding == Math.Rounding.Ceil || rounding == Math.Rounding.Expand) {
32+
return require_uint256((x * y + (denominator - 1)) / denominator);
33+
} else {
34+
return require_uint256((x * y) / denominator);
35+
}
36+
}
37+
38+
rule convertRoundTripAssets(uint256 assets) {
39+
assert convertToAssets(convertToShares(assets)) <= assets;
40+
}
41+
42+
rule convertRoundTripShares(uint256 shares) {
43+
assert convertToShares(convertToAssets(shares)) <= shares;
44+
}
45+
46+
rule roundTripDepositRedeem(uint256 assets) {
47+
assert previewRedeem(previewDeposit(assets)) <= assets;
48+
}
49+
50+
rule roundTripDepositWithdraw(uint256 assets) {
51+
assert previewWithdraw(assets) >= previewDeposit(assets);
52+
}
53+
54+
rule roundTripRedeemDeposit(uint256 shares) {
55+
assert previewDeposit(previewRedeem(shares)) <= shares;
56+
}
57+
58+
rule roundTripRedeemMint(uint256 shares) {
59+
assert previewMint(shares) >= previewRedeem(shares);
60+
}
61+
62+
rule roundTripMintWithdraw(uint256 shares) {
63+
assert previewWithdraw(previewMint(shares)) >= shares;
64+
}
65+
66+
rule roundTripMintRedeem(uint256 shares) {
67+
assert previewRedeem(shares) <= previewMint(shares);
68+
}
69+
70+
rule roundTripWithdrawMint(uint256 assets) {
71+
assert previewMint(previewWithdraw(assets)) >= assets;
72+
}
73+
74+
rule roundTripWithdrawDeposit(uint256 assets) {
75+
assert previewDeposit(assets) <= previewWithdraw(assets);
76+
}

0 commit comments

Comments
 (0)