mirror of
https://github.com/luxfi/markets.git
synced 2026-10-30 16:19:55 +00:00
139 lines
6.0 KiB
RPMSpec
139 lines
6.0 KiB
RPMSpec
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
methods {
|
|
function extSloads(bytes32[]) external returns bytes32[] => NONDET DELETE;
|
|
|
|
function Util.maxFee() external returns uint256 envfree;
|
|
|
|
function MathLib.mulDivDown(uint256 a, uint256 b, uint256 c) internal returns uint256 => ghostMulDivDown(a, b, c);
|
|
function MathLib.mulDivUp(uint256 a, uint256 b, uint256 c) internal returns uint256 => ghostMulDivUp(a, b, c);
|
|
function MathLib.wTaylorCompounded(uint256 a, uint256 b) internal returns uint256 => ghostTaylorCompounded(a, b);
|
|
// We assume here that all external functions will not access storage, since we cannot show commutativity otherwise.
|
|
// We also need to assume that the price and borrow rate return always the same value (and do not depend on msg.origin), so we use ghost functions for them.
|
|
function _.borrowRate(MorphoHarness.MarketParams marketParams, MorphoHarness.Market market) external with (env e) => ghostBorrowRate(marketParams.irm, e.block.timestamp) expect uint256;
|
|
function _.price() external with (env e) => ghostOraclePrice(e.block.timestamp) expect uint256;
|
|
function _.transfer(address to, uint256 amount) external => ghostTransfer(to, amount) expect bool;
|
|
function _.transferFrom(address from, address to, uint256 amount) external => ghostTransferFrom(from, to, amount) expect bool;
|
|
function _.onMorphoLiquidate(uint256, bytes) external => NONDET;
|
|
function _.onMorphoRepay(uint256, bytes) external => NONDET;
|
|
function _.onMorphoSupply(uint256, bytes) external => NONDET;
|
|
function _.onMorphoSupplyCollateral(uint256, bytes) external => NONDET;
|
|
function _.onMorphoFlashLoan(uint256, bytes) external => NONDET;
|
|
}
|
|
|
|
ghost ghostMulDivUp(uint256, uint256, uint256) returns uint256;
|
|
ghost ghostMulDivDown(uint256, uint256, uint256) returns uint256;
|
|
ghost ghostTaylorCompounded(uint256, uint256) returns uint256;
|
|
ghost ghostBorrowRate(address, uint256) returns uint256;
|
|
ghost ghostOraclePrice(uint256) returns uint256;
|
|
ghost ghostTransfer(address, uint256) returns bool;
|
|
ghost ghostTransferFrom(address, address, uint256) returns bool;
|
|
|
|
|
|
// Check that calling accrueInterest first has no effect.
|
|
// This is because supply should call accrueInterest itself.
|
|
rule supplyAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) {
|
|
// Safe require because timestamps cannot realistically be that large.
|
|
require e.block.timestamp < 2^128;
|
|
|
|
storage init = lastStorage;
|
|
|
|
accrueInterest(e, marketParams);
|
|
supply(e, marketParams, assets, shares, onBehalf, data);
|
|
storage afterBoth = lastStorage;
|
|
|
|
supply(e, marketParams, assets, shares, onBehalf, data) at init;
|
|
storage afterOne = lastStorage;
|
|
|
|
assert afterBoth == afterOne;
|
|
}
|
|
|
|
// Check that calling accrueInterest first has no effect.
|
|
// This is because withdraw should call accrueInterest itself.
|
|
rule withdrawAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) {
|
|
// Safe require because timestamps cannot realistically be that large.
|
|
require e.block.timestamp < 2^128;
|
|
|
|
storage init = lastStorage;
|
|
|
|
accrueInterest(e, marketParams);
|
|
withdraw(e, marketParams, assets, shares, onBehalf, receiver);
|
|
storage afterBoth = lastStorage;
|
|
|
|
withdraw(e, marketParams, assets, shares, onBehalf, receiver) at init;
|
|
storage afterOne = lastStorage;
|
|
|
|
assert afterBoth == afterOne;
|
|
}
|
|
|
|
// Check that calling accrueInterest first has no effect.
|
|
// This is because borrow should call accrueInterest itself.
|
|
rule borrowAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) {
|
|
// Safe require because timestamps cannot realistically be that large.
|
|
require e.block.timestamp < 2^128;
|
|
|
|
storage init = lastStorage;
|
|
|
|
accrueInterest(e, marketParams);
|
|
borrow(e, marketParams, assets, shares, onBehalf, receiver);
|
|
storage afterBoth = lastStorage;
|
|
|
|
borrow(e, marketParams, assets, shares, onBehalf, receiver) at init;
|
|
storage afterOne = lastStorage;
|
|
|
|
assert afterBoth == afterOne;
|
|
}
|
|
|
|
// Check that calling accrueInterest first has no effect.
|
|
// This is because repay should call accrueInterest itself.
|
|
rule repayAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) {
|
|
// Safe require because timestamps cannot realistically be that large.
|
|
require e.block.timestamp < 2^128;
|
|
|
|
storage init = lastStorage;
|
|
|
|
accrueInterest(e, marketParams);
|
|
repay(e, marketParams, assets, shares, onBehalf, data);
|
|
storage afterBoth = lastStorage;
|
|
|
|
repay(e, marketParams, assets, shares, onBehalf, data) at init;
|
|
storage afterOne = lastStorage;
|
|
|
|
assert afterBoth == afterOne;
|
|
}
|
|
|
|
// Show that accrueInterest commutes with other state changing rules.
|
|
// We exclude view functions, because:
|
|
// (a) we cannot check the return value and for storage commutativity is trivial, and
|
|
// (b) most view functions, e.g. totalSupplyShares, are not commutative, i.e. they return a different value if called before accrueInterest is called.
|
|
// We also exclude setFeeRecipient, as it is known to be not commutative.
|
|
rule accrueInterestCommutesExceptForSetFeeRecipient(method f, calldataarg args)
|
|
filtered {
|
|
f -> !f.isView && f.selector != sig:setFeeRecipient(address).selector
|
|
}
|
|
{
|
|
env e1;
|
|
env e2;
|
|
MorphoHarness.MarketParams marketParams;
|
|
|
|
// Assume interactions to happen at the same block.
|
|
require e1.block.timestamp == e2.block.timestamp;
|
|
// Safe require because timestamps cannot realistically be that large.
|
|
require e1.block.timestamp < 2^128;
|
|
|
|
storage init = lastStorage;
|
|
|
|
accrueInterest(e1, marketParams);
|
|
f@withrevert(e2, args);
|
|
bool revert1 = lastReverted;
|
|
storage store1 = lastStorage;
|
|
|
|
|
|
f@withrevert(e2, args) at init;
|
|
bool revert2 = lastReverted;
|
|
accrueInterest(e1, marketParams);
|
|
storage store2 = lastStorage;
|
|
|
|
assert revert1 <=> revert2;
|
|
assert store1 == store2;
|
|
}
|