Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8d1d33f
chore: init
MathisGD Jan 21, 2025
c539a46
feat: merge matching functions
MathisGD Jan 21, 2025
4c4349f
test: test signatures
MathisGD Jan 22, 2025
b5be327
feat: liquidation poc
gd-colin Mar 13, 2025
a3a9725
chore: fmt
MathisGD Mar 18, 2025
3674061
Merge branch 'feat/first-implem' into feat/liquidation-poc
MathisGD Mar 18, 2025
3f69485
fix: implement review suggestions
gd-colin Mar 18, 2025
80c02fd
feat: test liquidation
gd-colin Mar 20, 2025
e406a6a
feat: fix rounding
gd-colin Mar 21, 2025
e18bf19
refactor: modularize tests
gd-colin Mar 21, 2025
37f2ef3
refactor: externalize liquidation tests
gd-colin Mar 21, 2025
93a6785
fix: check offers
gd-colin Mar 21, 2025
ac647cd
refactor: minor refacotring
gd-colin Mar 21, 2025
d4bd40c
fix: minor typo
gd-colin Mar 21, 2025
9168aef
test: measure variable k
gd-colin Mar 24, 2025
7de613b
refactor: improve test names
gd-colin Mar 24, 2025
55fec38
fix: bug in check offers
gd-colin Mar 24, 2025
b7433df
feat: implement bad debt realization
gd-colin Mar 31, 2025
1a44dd7
fix: apply suggestions from code review
gd-colin Mar 31, 2025
fe11159
refactor: expect a list of N seizures for N collaterals
gd-colin Apr 3, 2025
cd892c3
fix: update tests
gd-colin Apr 3, 2025
f967432
refactor: implement review suggestions
gd-colin Apr 3, 2025
29d9e7d
fix: withdrawable with bad debt
gd-colin Apr 3, 2025
f816401
fix: debt accounting
gd-colin Apr 3, 2025
f57cfdc
fix: various things on liquidations
MathisGD Apr 3, 2025
53c3973
perf: don't query the oracle price if you don't seize this asset
MathisGD Apr 3, 2025
0ea9540
Merge pull request #4 from morpho-org/refactor/liquidations
gd-colin Apr 7, 2025
5dc481c
docs: apply suggestions from code review
gd-colin Apr 7, 2025
7e2405a
refacotr: apply suggestions from code review
gd-colin Apr 10, 2025
e6b7460
feat: new bad debt realisation
MathisGD Apr 10, 2025
c34a72c
Merge pull request #5 from morpho-org/feat/new-bad-debt-realisation
gd-colin Apr 10, 2025
220bc9d
fix: rounding
gd-colin Apr 10, 2025
ab78e59
feat: withdraw with shares
gd-colin Apr 11, 2025
b5b6cbf
fix: apply suggestions from code review
gd-colin Apr 16, 2025
d9ac321
fix: exact computation to update assets and shares
gd-colin Apr 18, 2025
925feff
fix: shares rounding
gd-colin Apr 18, 2025
f440d91
fix: liquidation roundings
gd-colin May 21, 2025
cf26a45
fix: remove console log
gd-colin May 21, 2025
ad980f3
fix: remove console log
gd-colin May 21, 2025
4d1f891
Merge pull request #3 from morpho-org/feat/liquidation-poc
MathisGD Jun 13, 2025
a7f36f3
fix verif
MathisGD Jun 30, 2025
5dfa1d5
ci
MathisGD Jun 30, 2025
56583fc
ci fixes
MathisGD Jun 30, 2025
6160d5b
ci
MathisGD Jun 30, 2025
0ac3818
ci fix
MathisGD Jun 30, 2025
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
32 changes: 32 additions & 0 deletions .github/workflows/certora.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: certora

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

jobs:
verify:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
conf:
- Terms

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: Install certora
run: pip install certora-cli
- name: Install solc
run: |
wget https://github.com/ethereum/solidity/releases/download/v0.8.28/solc-static-linux
chmod +x solc-static-linux
sudo mv solc-static-linux /usr/local/bin/solc-0.8.28
- name: Verify ${{ matrix.conf }}
run: certoraRun certora/${{ matrix.conf }}.conf
env:
CERTORAKEY: ${{ secrets.CERTORAKEY }}
21 changes: 21 additions & 0 deletions .github/workflows/forge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: forge

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

jobs:
foundry:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
- name: Check formatting
run: forge fmt --check
- name: Run Forge tests
run: forge test
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cache
out
.certora_internal
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
16 changes: 16 additions & 0 deletions certora/Terms.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"files": [
"certora/helpers/TermsHelpers.sol",
"certora/dispatch/ERC20Standard.sol"
],
"solc": "solc-0.8.28",
"verify": "TermsHelpers:certora/Terms.spec",
"rule_sanity": "basic",
"server": "production",
"loop_iter": "2",
"optimistic_loop": true,
"optimistic_hashing": true,
"solc_via_ir": true,
"hashing_length_bound": "320",
"msg": "Terms"
}
56 changes: 56 additions & 0 deletions certora/Terms.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-or-later

/// METHODS ///

methods {
function withdrawable(bytes32 id) external returns uint256 envfree;
function balanceOf(address, address) external returns uint256 envfree;
function id(Terms.Term) external returns bytes32 envfree;

// function _.transfer(address, uint256) external => DISPATCHER(true);
// function _.transferFrom(address, address, uint256) external => DISPATCHER(true);
// function _.balanceOf(address) external => DISPATCHER(true);
}

/// HOOKS ///

persistent ghost mapping(bytes32 => mathint) sumBondSharesOf {
init_state axiom (forall bytes32 id. sumBondSharesOf[id] == 0);
}
hook Sload uint256 bondSharesOfOwner bondSharesOf[KEY address owner][KEY bytes32 id] {
require sumBondSharesOf[id] >= to_mathint(bondSharesOfOwner);
}
hook Sstore bondSharesOf[KEY address owner][KEY bytes32 id] uint256 newBondShares (uint256 oldBondShares) {
sumBondSharesOf[id] = sumBondSharesOf[id] - oldBondShares + newBondShares;
}

persistent ghost mapping(bytes32 => mathint) sumDebtOf {
init_state axiom (forall bytes32 id. sumDebtOf[id] == 0);
}
hook Sload uint256 debtOfOwner debtOf[KEY address owner][KEY bytes32 id] {
require sumDebtOf[id] >= to_mathint(debtOfOwner);
}
hook Sstore debtOf[KEY address owner][KEY bytes32 id] uint256 newDebt (uint256 oldDebt) {
sumDebtOf[id] = sumDebtOf[id] - oldDebt + newDebt;
}

/// SANITY ///

invariant sanitySumBondShares(bytes32 id)
sumBondSharesOf[id] >= 0;

invariant sanitySumDebt(bytes32 id)
sumDebtOf[id] >= 0;

rule satisfyMatch(env e, calldataarg args) {
MATCH(e, args);
satisfy true;
}

/// INVARIANTS ///

// strong invariant sums(bytes32 id)
// sumBondOf[id] == sumDebtOf[id] + withdrawable(id);

// invariant balances(TermsHelpers.Term term)
// balanceOf(term.loanToken, currentContract) >= withdrawable(id(term));
48 changes: 48 additions & 0 deletions certora/dispatch/ERC20NoRevert.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

contract ERC20NoRevert {
address public owner;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;

constructor() {
owner = msg.sender;
}

modifier onlyOwner() {
require(msg.sender == owner);
_;
}

function _transfer(address _from, address _to, uint256 _amount) internal returns (bool) {
if (balanceOf[_from] < _amount) {
return false;
}
balanceOf[_from] -= _amount;
balanceOf[_to] += _amount;
return true;
}

function transfer(address _to, uint256 _amount) public returns (bool) {
return _transfer(msg.sender, _to, _amount);
}

function transferFrom(address _from, address _to, uint256 _amount) public returns (bool) {
if (allowance[_from][msg.sender] < _amount) {
return false;
}
allowance[_from][msg.sender] -= _amount;
return _transfer(_from, _to, _amount);
}

function approve(address _spender, uint256 _amount) public {
allowance[msg.sender][_spender] = _amount;
}

function mint(address _receiver, uint256 _amount) public onlyOwner {
balanceOf[_receiver] += _amount;
totalSupply += _amount;
}
}
42 changes: 42 additions & 0 deletions certora/dispatch/ERC20Standard.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

contract ERC20Standard {
address public owner;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;

constructor() {
owner = msg.sender;
}

modifier onlyOwner() {
require(msg.sender == owner);
_;
}

function _transfer(address _from, address _to, uint256 _amount) internal returns (bool) {
balanceOf[_from] -= _amount;
balanceOf[_to] += _amount;
return true;
}

function transfer(address _to, uint256 _amount) public returns (bool) {
return _transfer(msg.sender, _to, _amount);
}

function transferFrom(address _from, address _to, uint256 _amount) public returns (bool) {
allowance[_from][msg.sender] -= _amount;
return _transfer(_from, _to, _amount);
}

function approve(address _spender, uint256 _amount) public {
allowance[msg.sender][_spender] = _amount;
}

function mint(address _receiver, uint256 _amount) public onlyOwner {
balanceOf[_receiver] += _amount;
totalSupply += _amount;
}
}
45 changes: 45 additions & 0 deletions certora/dispatch/ERC20USDT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

contract ERC20USDT {
address public owner;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;

constructor() {
owner = msg.sender;
}

modifier onlyOwner() {
require(msg.sender == owner);
_;
}

function _transfer(address _from, address _to, uint256 _amount) internal {
balanceOf[_from] -= _amount;
balanceOf[_to] += _amount;
}

function transfer(address _to, uint256 _amount) public {
_transfer(msg.sender, _to, _amount);
}

function transferFrom(address _from, address _to, uint256 _amount) public {
if (allowance[_from][msg.sender] < type(uint256).max) {
allowance[_from][msg.sender] -= _amount;
}
_transfer(_from, _to, _amount);
}

function approve(address _spender, uint256 _amount) public {
require(!((_amount != 0) && (allowance[msg.sender][_spender] != 0)));

allowance[msg.sender][_spender] = _amount;
}

function mint(address _receiver, uint256 _amount) public onlyOwner {
balanceOf[_receiver] += _amount;
totalSupply += _amount;
}
}
14 changes: 14 additions & 0 deletions certora/helpers/TermsHelpers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {Terms, Term, IERC20} from "../../src/Terms.sol";

contract TermsHelpers is Terms {
function balanceOf(address token, address account) external view returns (uint256) {
return IERC20(token).balanceOf(account);
}

function id(Term memory term) external pure returns (bytes32) {
return _id(term);
}
}
2 changes: 2 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[profile.default]
via_ir = true
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at b93cf4
Loading
Loading