Skip to content

Commit edec6b4

Browse files
authored
Merge pull request #345 from morpho-labs/certora/roundtrip
[Certora] Check supply followed by withdraw is not profitable
2 parents 9a3cd0e + c0df5f7 commit edec6b4

File tree

3 files changed

+88
-0
lines changed

3 files changed

+88
-0
lines changed

.github/workflows/certora.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,4 @@ jobs:
4848
- verifyBlueExitLiquidity.sh
4949
- verifyBlueTransfer.sh
5050
- verifyReentrancy.sh
51+
- verifyDifficultMath.sh
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/bash
2+
3+
set -euxo pipefail
4+
5+
certoraRun \
6+
certora/harness/MorphoHarness.sol \
7+
src/mocks/OracleMock.sol \
8+
--verify MorphoHarness:certora/specs/DifficultMath.spec \
9+
--loop_iter 3 \
10+
--optimistic_loop \
11+
--msg "Morpho Difficult Math" \
12+
"$@"

certora/specs/DifficultMath.spec

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
methods {
2+
function getMarketId(MorphoHarness.Market) external returns MorphoHarness.Id envfree;
3+
function getVirtualTotalSupply(MorphoHarness.Id) external returns uint256 envfree;
4+
function getVirtualTotalSupplyShares(MorphoHarness.Id) external returns uint256 envfree;
5+
function MathLib.mulDivDown(uint256 a, uint256 b, uint256 c) internal returns uint256 => summaryMulDivDown(a,b,c);
6+
function MathLib.mulDivUp(uint256 a, uint256 b, uint256 c) internal returns uint256 => summaryMulDivUp(a,b,c);
7+
function SafeTransferLib.safeTransfer(address token, address to, uint256 value) internal => NONDET;
8+
function SafeTransferLib.safeTransferFrom(address token, address from, address to, uint256 value) internal => NONDET;
9+
function _.onMorphoSupply(uint256 assets, bytes data) external => HAVOC_ECF;
10+
}
11+
12+
function summaryMulDivUp(uint256 x, uint256 y, uint256 d) returns uint256 {
13+
return require_uint256((x * y + (d - 1)) / d);
14+
}
15+
16+
function summaryMulDivDown(uint256 x, uint256 y, uint256 d) returns uint256 {
17+
return require_uint256((x * y) / d);
18+
}
19+
20+
/* There should be no profit from supply followed immediately by withdraw */
21+
rule supplyWithdraw() {
22+
MorphoHarness.Market market;
23+
uint256 assets;
24+
uint256 shares;
25+
uint256 suppliedAssets;
26+
uint256 withdrawnAssets;
27+
uint256 suppliedShares;
28+
uint256 withdrawnShares;
29+
address onbehalf;
30+
address receiver;
31+
bytes data;
32+
env e1;
33+
env e2;
34+
35+
require e1.block.timestamp == e2.block.timestamp;
36+
37+
suppliedAssets, suppliedShares = supply(e1, market, assets, shares, onbehalf, data);
38+
39+
MorphoHarness.Id id = getMarketId(market);
40+
assert suppliedAssets * (getVirtualTotalSupplyShares(id) - suppliedShares) >= suppliedShares * (getVirtualTotalSupply(id) - suppliedAssets);
41+
assert suppliedAssets * getVirtualTotalSupplyShares(id) >= suppliedShares * getVirtualTotalSupply(id);
42+
43+
withdrawnAssets, withdrawnShares = withdraw(e2, market, 0, suppliedShares, onbehalf, receiver);
44+
45+
assert withdrawnShares == suppliedShares;
46+
assert withdrawnAssets <= suppliedAssets;
47+
}
48+
49+
/* There should be no profit from withdraw followed immediately by supply */
50+
rule withdrawSupply() {
51+
MorphoHarness.Market market;
52+
uint256 assets;
53+
uint256 shares;
54+
uint256 suppliedAssets;
55+
uint256 withdrawnAssets;
56+
uint256 suppliedShares;
57+
uint256 withdrawnShares;
58+
address onbehalf;
59+
address receiver;
60+
bytes data;
61+
env e1;
62+
env e2;
63+
64+
require e1.block.timestamp == e2.block.timestamp;
65+
66+
withdrawnAssets, withdrawnShares = withdraw(e2, market, assets, shares, onbehalf, receiver);
67+
68+
MorphoHarness.Id id = getMarketId(market);
69+
assert withdrawnAssets * (getVirtualTotalSupplyShares(id) + withdrawnShares) <= withdrawnShares * (getVirtualTotalSupply(id) + withdrawnAssets);
70+
assert withdrawnAssets * getVirtualTotalSupplyShares(id) <= withdrawnShares * getVirtualTotalSupply(id);
71+
72+
suppliedAssets, suppliedShares = supply(e1, market, withdrawnAssets, 0, onbehalf, data);
73+
74+
assert suppliedAssets == withdrawnAssets && withdrawnShares >= suppliedShares;
75+
}

0 commit comments

Comments
 (0)