-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #321 from morpho-labs/certora/dispatch-tokens
[Certora] Dispatch tokens and verify safe transfer lib
- Loading branch information
Showing
8 changed files
with
212 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
import {ERC20} from "openzeppelin-contracts/token/ERC20/ERC20.sol"; | ||
|
||
contract ERC20Bad is ERC20 { | ||
constructor(string memory name, string memory symbol) ERC20(name, symbol) {} | ||
|
||
function _beforeTokenTransfer(address from, address to, uint256 amount) internal override { | ||
_mint(to, amount); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
import {ERC20} from "openzeppelin-contracts/token/ERC20/ERC20.sol"; | ||
|
||
contract ERC20Good is ERC20 { | ||
constructor(string memory name, string memory symbol) ERC20(name, symbol) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
contract ERC20NoRevert { | ||
string public name; | ||
string public symbol; | ||
uint256 public decimals; | ||
address owner; | ||
mapping(address => uint256) public balanceOf; | ||
mapping(address => mapping(address => uint256)) public allowed; | ||
|
||
constructor(string memory _name, string memory _symbol, uint256 _decimals) { | ||
name = _name; | ||
symbol = _symbol; | ||
decimals = _decimals; | ||
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) { | ||
allowed[_from][msg.sender] -= _amount; | ||
return _transfer(_from, _to, _amount); | ||
} | ||
|
||
function approve(address _spender, uint256 _amount) public { | ||
require(!((_amount != 0) && (allowed[msg.sender][_spender] != 0))); | ||
|
||
allowed[msg.sender][_spender] = _amount; | ||
} | ||
|
||
function allowance(address _owner, address _spender) public view returns (uint256 remaining) { | ||
return allowed[_owner][_spender]; | ||
} | ||
|
||
function mint(address _receiver, uint256 _amount) public onlyOwner { | ||
balanceOf[_receiver] += _amount; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
contract ERC20USDT { | ||
uint256 public constant MAX_UINT = 2 ** 256 - 1; | ||
|
||
string public name; | ||
string public symbol; | ||
uint256 public decimals; | ||
address owner; | ||
mapping(address => uint256) public balanceOf; | ||
mapping(address => mapping(address => uint256)) public allowed; | ||
|
||
constructor(string memory _name, string memory _symbol, uint256 _decimals) { | ||
name = _name; | ||
symbol = _symbol; | ||
decimals = _decimals; | ||
owner = msg.sender; | ||
} | ||
|
||
modifier onlyOwner() { | ||
require(msg.sender == owner); | ||
_; | ||
} | ||
|
||
function _transfer(address _from, address _to, uint256 _amount) public { | ||
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 (allowed[_from][msg.sender] < MAX_UINT) { | ||
allowed[_from][msg.sender] -= _amount; | ||
} | ||
_transfer(_from, _to, _amount); | ||
} | ||
|
||
function approve(address _spender, uint256 _amount) public { | ||
require(!((_amount != 0) && (allowed[msg.sender][_spender] != 0))); | ||
|
||
allowed[msg.sender][_spender] = _amount; | ||
} | ||
|
||
function allowance(address _owner, address _spender) public view returns (uint256 remaining) { | ||
return allowed[_owner][_spender]; | ||
} | ||
|
||
function mint(address _receiver, uint256 _amount) public onlyOwner { | ||
balanceOf[_receiver] += _amount; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
import "../../src/libraries/SafeTransferLib.sol"; | ||
import "../../src/interfaces/IERC20.sol"; | ||
|
||
interface IERC20Extended is IERC20 { | ||
function balanceOf(address) external view returns (uint256); | ||
function totalSupply() external view returns (uint256); | ||
} | ||
|
||
contract TransferHarness { | ||
using SafeTransferLib for IERC20; | ||
|
||
function doTransfer(address token, address from, address to, uint256 value) public { | ||
IERC20(token).safeTransferFrom(from, to, value); | ||
} | ||
|
||
function getBalance(address token, address user) public view returns (uint256) { | ||
return IERC20Extended(token).balanceOf(user); | ||
} | ||
|
||
function getTotalSupply(address token) public view returns (uint256) { | ||
return IERC20Extended(token).totalSupply(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#!/bin/bash | ||
|
||
set -euxo pipefail | ||
|
||
certoraRun \ | ||
certora/harness/TransferHarness.sol \ | ||
certora/dispatch/ERC20Good.sol \ | ||
certora/dispatch/ERC20USDT.sol \ | ||
certora/dispatch/ERC20NoRevert.sol \ | ||
--verify TransferHarness:certora/specs/BlueTransfer.spec \ | ||
--packages openzeppelin-contracts=lib/openzeppelin-contracts/contracts \ | ||
--loop_iter 3 \ | ||
--optimistic_loop \ | ||
--msg "Morpho Transfer Summary" \ | ||
"$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
methods { | ||
function doTransfer(address, address, address, uint256) external envfree; | ||
function getBalance(address, address) external returns (uint256) envfree; | ||
function getTotalSupply(address) external returns (uint256) envfree; | ||
|
||
function _.transferFrom(address, address, uint256) external => DISPATCHER(true); | ||
function _.balanceOf(address) external => DISPATCHER(true); | ||
function _.totalSupply() external => DISPATCHER(true); | ||
} | ||
|
||
ghost mapping(address => mathint) myBalances | ||
{ | ||
init_state axiom (forall address token. myBalances[token] == 0); | ||
} | ||
|
||
function summarySafeTransferFrom(address token, address from, address to, uint256 amount) { | ||
if (from == currentContract) { | ||
myBalances[token] = require_uint256(myBalances[token] - amount); | ||
} | ||
if (to == currentContract) { | ||
myBalances[token] = require_uint256(myBalances[token] + amount); | ||
} | ||
} | ||
|
||
rule checkTransfer(address token, address from, address to, uint256 amount) { | ||
require from == currentContract || to == currentContract; | ||
|
||
require from != to => getBalance(token, from) + getBalance(token, to) <= to_mathint(getTotalSupply(token)); | ||
|
||
mathint initialBalance = getBalance(token, currentContract); | ||
doTransfer(token, from, to, amount); | ||
mathint finalBalance = getBalance(token, currentContract); | ||
|
||
require myBalances[token] == initialBalance; | ||
summarySafeTransferFrom(token, from, to, amount); | ||
assert myBalances[token] == finalBalance; | ||
} |