-
Notifications
You must be signed in to change notification settings - Fork 60
/
ExactMath.spec
126 lines (100 loc) · 5.8 KB
/
ExactMath.spec
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// SPDX-License-Identifier: GPL-2.0-or-later
using Util as Util;
methods {
function extSloads(bytes32[]) external returns bytes32[] => NONDET DELETE;
function feeRecipient() external returns address envfree;
function supplyShares(MorphoHarness.Id, address) external returns uint256 envfree;
function borrowShares(MorphoHarness.Id, address) external returns uint256 envfree;
function virtualTotalSupplyAssets(MorphoHarness.Id) external returns uint256 envfree;
function virtualTotalSupplyShares(MorphoHarness.Id) external returns uint256 envfree;
function virtualTotalBorrowAssets(MorphoHarness.Id) external returns uint256 envfree;
function virtualTotalBorrowShares(MorphoHarness.Id) external returns uint256 envfree;
function lastUpdate(MorphoHarness.Id) external returns uint256 envfree;
function fee(MorphoHarness.Id) external returns uint256 envfree;
function Util.maxFee() external returns uint256 envfree;
function Util.libId(MorphoHarness.MarketParams) external returns MorphoHarness.Id envfree;
function MathLib.mulDivDown(uint256 a, uint256 b, uint256 c) internal returns uint256 => summaryMulDivDown(a,b,c);
function MathLib.mulDivUp(uint256 a, uint256 b, uint256 c) internal returns uint256 => summaryMulDivUp(a,b,c);
function SafeTransferLib.safeTransfer(address token, address to, uint256 value) internal => NONDET;
function SafeTransferLib.safeTransferFrom(address token, address from, address to, uint256 value) internal => NONDET;
function _.onMorphoSupply(uint256 assets, bytes data) external => HAVOC_ECF;
}
function summaryMulDivUp(uint256 x, uint256 y, uint256 d) returns uint256 {
// Safe require because the reference implementation would revert.
return require_uint256((x * y + (d - 1)) / d);
}
function summaryMulDivDown(uint256 x, uint256 y, uint256 d) returns uint256 {
// Safe require because the reference implementation would revert.
return require_uint256((x * y) / d);
}
// Check that when not accruing interest, and when repaying all, the borrow exchange rate is at least reset to the initial exchange rate.
// More details on the purpose of this rule in ExchangeRate.spec.
rule repayAllResetsBorrowExchangeRate(env e, MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) {
MorphoHarness.Id id = Util.libId(marketParams);
// Safe require because this invariant is checked in ConsistentState.spec
require fee(id) <= Util.maxFee();
mathint assetsBefore = virtualTotalBorrowAssets(id);
mathint sharesBefore = virtualTotalBorrowShares(id);
// Assume no interest as it would increase the borrowed assets.
require lastUpdate(id) == e.block.timestamp;
mathint repaidAssets;
repaidAssets, _ = repay(e, marketParams, assets, shares, onBehalf, data);
// Check the case where the market is fully repaid.
require repaidAssets >= assetsBefore;
mathint assetsAfter = virtualTotalBorrowAssets(id);
mathint sharesAfter = virtualTotalBorrowShares(id);
assert assetsAfter == 1;
// There are at least as many shares as virtual shares, by definition of virtualTotalBorrowShares.
}
// There should be no profit from supply followed immediately by withdraw.
rule supplyWithdraw() {
MorphoHarness.MarketParams marketParams;
MorphoHarness.Id id = Util.libId(marketParams);
env e1;
env e2;
address onBehalf;
// Assume that interactions happen at the same block.
require e1.block.timestamp == e2.block.timestamp;
// Assume that the user starts without any supply position.
require supplyShares(id, onBehalf) == 0;
// Assume that the user is not the fee recipient, otherwise the gain can come from the fee.
require onBehalf != feeRecipient();
// Safe require because timestamps cannot realistically be that large.
require e1.block.timestamp < 2^128;
uint256 supplyAssets; uint256 supplyShares; bytes data;
uint256 suppliedAssets;
uint256 suppliedShares;
suppliedAssets, suppliedShares = supply(e1, marketParams, supplyAssets, supplyShares, onBehalf, data);
// Hints for the prover.
assert suppliedAssets * (virtualTotalSupplyShares(id) - suppliedShares) >= suppliedShares * (virtualTotalSupplyAssets(id) - suppliedAssets);
assert suppliedAssets * virtualTotalSupplyShares(id) >= suppliedShares * virtualTotalSupplyAssets(id);
uint256 withdrawAssets; uint256 withdrawShares; address receiver;
uint256 withdrawnAssets;
withdrawnAssets, _ = withdraw(e2, marketParams, withdrawAssets, withdrawShares, onBehalf, receiver);
assert withdrawnAssets <= suppliedAssets;
}
// There should be no profit from borrow followed immediately by repaying all.
rule borrowRepay() {
MorphoHarness.MarketParams marketParams;
MorphoHarness.Id id = Util.libId(marketParams);
address onBehalf;
env e1;
env e2;
// Assume interactions happen at the same block.
require e1.block.timestamp == e2.block.timestamp;
// Assume that the user starts without any borrow position.
require borrowShares(id, onBehalf) == 0;
// Safe require because timestamps cannot realistically be that large.
require e1.block.timestamp < 2^128;
uint256 borrowAssets; uint256 borrowShares; address receiver;
uint256 borrowedAssets;
uint256 borrowedShares;
borrowedAssets, borrowedShares = borrow(e2, marketParams, borrowAssets, borrowShares, onBehalf, receiver);
// Hints for the prover.
assert borrowedAssets * (virtualTotalBorrowShares(id) - borrowedShares) <= borrowedShares * (virtualTotalBorrowAssets(id) - borrowedAssets);
assert borrowedAssets * virtualTotalBorrowShares(id) <= borrowedShares * virtualTotalBorrowAssets(id);
bytes data;
uint256 repaidAssets;
repaidAssets, _ = repay(e1, marketParams, 0, borrowedShares, onBehalf, data);
assert borrowedAssets <= repaidAssets;
}