Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contracts + Certora specs #1

Merged
merged 40 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
11a1d2b
Add initial set of contracts + Certora specs
sunbreak1211 May 3, 2023
6d735aa
Upgrade to CVL2 + Expand specs and follow latest set standards
sunbreak1211 May 9, 2023
0e3469f
Add usr param to MkrNgt + extending events
sunbreak1211 May 9, 2023
3445cef
Add MkrNgt test for different usr
sunbreak1211 May 9, 2023
f702297
Add test different from
sunbreak1211 May 9, 2023
904c69b
Remove unnecessary conditional
sunbreak1211 May 9, 2023
ada0960
Remove unnecessary warp
sunbreak1211 May 9, 2023
489c331
Add test permit owner zero
sunbreak1211 May 9, 2023
249d1ed
Make rate immutable
sunbreak1211 May 10, 2023
dbcf089
Change event variable names
sunbreak1211 May 10, 2023
ba4dcf5
Remove unused interface
sunbreak1211 May 10, 2023
7f96c1e
Remove space
sunbreak1211 May 10, 2023
ddbf96d
Keep consistency on variable names between contract and its test
sunbreak1211 May 10, 2023
d4b3c8d
Use ngtAmt in ngtToMkr (#2)
sunbreak1211 May 11, 2023
f05f383
Remove space
sunbreak1211 May 11, 2023
1fa573e
MkrNgt Certora specs
sunbreak1211 May 15, 2023
61b48dd
Add README
sunbreak1211 May 15, 2023
540428c
Workaround for Certora typechecker change
sunbreak1211 May 16, 2023
d031fb3
forge install: token-tests
May 29, 2023
d6f16ec
Use token-tests
May 29, 2023
10b3168
Merge pull request #3 from makerdao/use-token-tests
telome May 30, 2023
ba3bc3e
Use production Certora for CI
sunbreak1211 Jun 7, 2023
3fa2dbd
Fix Makefile for Certora CVL2
sunbreak1211 Sep 5, 2023
24610ef
Change test directory location + Add deployment scripts (#4)
sunbreak1211 Sep 11, 2023
c9d4e04
Change chainlog keyword (#5)
sunbreak1211 Sep 11, 2023
3830c77
Use foundry.toml + run foundry directly with its own commands
sunbreak1211 Sep 12, 2023
506bff6
Add rate validation in init script
oldchili Oct 9, 2023
664e1f1
Merge pull request #7 from makerdao/CS-audit-responses-deploy-scripts
oldchili Oct 10, 2023
2d675f8
Audit fixes (#6)
sunbreak1211 Oct 12, 2023
e047381
Add CS audit reports (#8)
sunbreak1211 Nov 7, 2023
8b2b04d
add cantina report
oldchili Dec 4, 2023
3d5d602
Merge pull request #9 from makerdao/add-cantina-report
oldchili Dec 4, 2023
53f1e3c
Update Certora
sunbreak1211 Apr 9, 2024
9aa13e3
Update token-tests (#10)
telome Apr 9, 2024
705b40a
Fix tests CI
sunbreak1211 Apr 9, 2024
5c7c55c
Certora CI: clean make call
sunbreak1211 Apr 10, 2024
6f2b8d2
Use solc 0.8.21 + upgrade token-tests and fix warnings due to it (#11)
sunbreak1211 Apr 26, 2024
3ddfa5e
Renaming (#12)
sunbreak1211 Aug 27, 2024
162e00e
Update CS audit (#13)
oldchili Sep 5, 2024
196d816
Add Sherlock Report (#15)
oldchili Sep 19, 2024
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
43 changes: 43 additions & 0 deletions .github/workflows/certora.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Certora

on: [push, pull_request]

jobs:
certora:
name: Certora
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sky:
- sky
- mkr-sky

steps:
- name: Checkout
uses: actions/checkout@v3

- uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: '11'
java-package: jre

- name: Set up Python 3.8
uses: actions/setup-python@v3
with:
python-version: 3.8

- name: Install solc-select
run: pip3 install solc-select

- name: Solc Select 0.8.21
run: solc-select install 0.8.21

- name: Install Certora
run: pip3 install certora-cli

- name: Verify ${{ matrix.sky }}
run: make certora-${{ matrix.sky }}
env:
CERTORAKEY: ${{ secrets.CERTORAKEY }}
20 changes: 20 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
on: [push, pull_request]

jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Checkout repository and submodules
uses: actions/checkout@v3
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Run tests
run: forge test
env:
ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }}
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "lib/dss-test"]
path = lib/dss-test
url = https://github.com/makerdao/dss-test.git
[submodule "lib/token-tests"]
path = lib/token-tests
url = https://github.com/makerdao/token-tests
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
all :; forge build --use solc:0.8.16
clean :; forge clean
test :; forge test -vvv --use solc:0.8.16
PATH := ~/.solc-select/artifacts/solc-0.8.21:~/.solc-select/artifacts:$(PATH)
certora-sky :; PATH=${PATH} certoraRun certora/Sky.conf$(if $(rule), --rule $(rule),)
certora-mkr-sky :; PATH=${PATH} certoraRun certora/MkrSky.conf$(if $(rule), --rule $(rule),)
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# SKY Token and contract associated

This repository includes 2 smart contracts:

- SKY token
- MkrSky Converter

### SKY token

This is a standard erc20 implementation with regular `permit` functionality + EIP-1271 smart contract signature validation.
In principle `PauseProxy` and `MkrSky` would be the only two contracts set as `wards(address)`.

### MkrSky

It is a converter between `Mkr` and `Sky` (both ways). Using the `mint` and `burn` capabilities of both tokens it is possible to exchange one to the other. The exchange rate is 1:`rate` (value defined as `immutable`).

**Note:** if one of the tokens removes `mint` capabilities to this contract, it means that the path which gives that token to the user won't be available.

**Note 2:** In the MKR -> SKY conversion, if the user passes a `wad` amount not multiple of `rate`, it causes that a dusty value will be lost.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added audit/cantina-report-review-makerdao-ngt.pdf
Binary file not shown.
61 changes: 61 additions & 0 deletions certora/Auxiliar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
pragma solidity 0.8.21;

interface IERC1271 {
function isValidSignature(
bytes32,
bytes memory
) external view returns (bytes4);
}

contract Auxiliar {
function computeDigestForToken(
bytes32 domain_separator,
bytes32 permit_typehash,
address owner,
address spender,
uint256 value,
uint256 nonce,
uint256 deadline
) public pure returns (bytes32 digest){
digest =
keccak256(abi.encodePacked(
"\x19\x01",
domain_separator,
keccak256(abi.encode(
permit_typehash,
owner,
spender,
value,
nonce,
deadline
))
));
}

function call_ecrecover(
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) public pure returns (address signer) {
signer = ecrecover(digest, v, r, s);
}

function signatureToVRS(bytes memory signature) public returns (uint8 v, bytes32 r, bytes32 s) {
if (signature.length == 65) {
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
}
}

function VRSToSignature(uint8 v, bytes32 r, bytes32 s) public returns (bytes memory signature) {
signature = abi.encodePacked(r, s, v);
}

function size(bytes memory data) public returns (uint256 size_) {
size_ = data.length;
}
}
7 changes: 7 additions & 0 deletions certora/MkrMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pragma solidity ^0.8.21;

import "../src/Sky.sol";

contract MkrMock is Sky {

}
25 changes: 25 additions & 0 deletions certora/MkrSky.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"files": [
"src/MkrSky.sol",
"src/Sky.sol",
"certora/MkrMock.sol"
],
"link": [
"MkrSky:sky=Sky",
"MkrSky:mkr=MkrMock"
],
"rule_sanity": "basic",
"solc": "solc-0.8.21",
"solc_optimize_map": {
"MkrSky": "200",
"Sky": "200",
"MkrMock": "0"
},
"verify": "MkrSky:certora/MkrSky.spec",
"prover_args": [
"-mediumTimeout 180"
],
"optimistic_loop": true,
"multi_assert_check": true,
"wait_for_results": "all"
}
183 changes: 183 additions & 0 deletions certora/MkrSky.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// MkrSky.spec

using Sky as sky;
using MkrMock as mkr;

methods {
function rate() external returns (uint256) envfree;
function sky.wards(address) external returns (uint256) envfree;
function sky.totalSupply() external returns (uint256) envfree;
function sky.balanceOf(address) external returns (uint256) envfree;
function sky.allowance(address, address) external returns (uint256) envfree;
function mkr.wards(address) external returns (uint256) envfree;
function mkr.totalSupply() external returns (uint256) envfree;
function mkr.balanceOf(address) external returns (uint256) envfree;
function mkr.allowance(address, address) external returns (uint256) envfree;
}

ghost balanceSumSky() returns mathint {
init_state axiom balanceSumSky() == 0;
}

hook Sstore sky.balanceOf[KEY address a] uint256 balance (uint256 old_balance) {
havoc balanceSumSky assuming balanceSumSky@new() == balanceSumSky@old() + balance - old_balance && balanceSumSky@new() >= 0;
}

invariant balanceSumSky_equals_totalSupply() balanceSumSky() == to_mathint(sky.totalSupply());

ghost balanceSumMkr() returns mathint {
init_state axiom balanceSumMkr() == 0;
}

hook Sstore mkr.balanceOf[KEY address a] uint256 balance (uint256 old_balance) {
havoc balanceSumMkr assuming balanceSumMkr@new() == balanceSumMkr@old() + balance - old_balance && balanceSumMkr@new() >= 0;
}

invariant balanceSumMkr_equals_totalSupply() balanceSumMkr() == to_mathint(mkr.totalSupply());

// Verify correct storage changes for non reverting mkrToSky
rule mkrToSky(address usr, uint256 wad) {
env e;

require e.msg.sender != currentContract;

requireInvariant balanceSumSky_equals_totalSupply();
requireInvariant balanceSumMkr_equals_totalSupply();

address other;
require other != usr;
address other2;
require other2 != e.msg.sender;

mathint rate = rate();
mathint skyTotalSupplyBefore = sky.totalSupply();
mathint skyBalanceOfUsrBefore = sky.balanceOf(usr);
mathint skyBalanceOfOtherBefore = sky.balanceOf(other);
mathint mkrTotalSupplyBefore = mkr.totalSupply();
mathint mkrBalanceOfSenderBefore = mkr.balanceOf(e.msg.sender);
mathint mkrBalanceOfOtherBefore = mkr.balanceOf(other2);

mkrToSky(e, usr, wad);

mathint skyTotalSupplyAfter = sky.totalSupply();
mathint skyBalanceOfUsrAfter = sky.balanceOf(usr);
mathint skyBalanceOfOtherAfter = sky.balanceOf(other);
mathint mkrTotalSupplyAfter = mkr.totalSupply();
mathint mkrBalanceOfSenderAfter = mkr.balanceOf(e.msg.sender);
mathint mkrBalanceOfOtherAfter = mkr.balanceOf(other2);

assert skyTotalSupplyAfter == skyTotalSupplyBefore + wad * rate, "mkrToSky did not increase sky.totalSupply by wad * rate";
assert skyBalanceOfUsrAfter == skyBalanceOfUsrBefore + wad * rate, "mkrToSky did not increase sky.balanceOf[usr] by wad * rate";
assert skyBalanceOfOtherAfter == skyBalanceOfOtherBefore, "mkrToSky did not keep unchanged the rest of sky.balanceOf[x]";
assert mkrTotalSupplyAfter == mkrTotalSupplyBefore - wad, "mkrToSky did not decrease mkr.totalSupply by wad";
assert mkrBalanceOfSenderAfter == mkrBalanceOfSenderBefore - wad, "mkrToSky did not decrease mkr.balanceOf[sender] by wad";
assert mkrBalanceOfOtherAfter == mkrBalanceOfOtherBefore, "mkrToSky did not keep unchanged the rest of mkr.balanceOf[x]";
}

// Verify revert rules on mkrToSky
rule mkrToSky_revert(address usr, uint256 wad) {
env e;

requireInvariant balanceSumSky_equals_totalSupply();
requireInvariant balanceSumMkr_equals_totalSupply();

require e.msg.sender != currentContract;

mathint rate = rate();
mathint mkrBalanceOfSender = mkr.balanceOf(e.msg.sender);
mathint mkrAllowanceSenderMkrSky = mkr.allowance(e.msg.sender, currentContract);
mathint skyWardsMkrSky = sky.wards(currentContract);
mathint skyTotalSupply = sky.totalSupply();

mkrToSky@withrevert(e, usr, wad);

bool revert1 = e.msg.value > 0;
bool revert2 = mkrBalanceOfSender < to_mathint(wad);
bool revert3 = mkrAllowanceSenderMkrSky < to_mathint(wad);
bool revert4 = skyWardsMkrSky != 1;
bool revert5 = skyTotalSupply + wad * rate > max_uint256;
bool revert6 = usr == 0 || usr == sky;

assert revert1 => lastReverted, "revert1 failed";
assert revert2 => lastReverted, "revert2 failed";
assert revert3 => lastReverted, "revert3 failed";
assert revert4 => lastReverted, "revert4 failed";
assert revert5 => lastReverted, "revert5 failed";
assert revert6 => lastReverted, "revert6 failed";
assert lastReverted => revert1 || revert2 || revert3 ||
revert4 || revert5 || revert6, "Revert rules are not covering all the cases";
}

// Verify correct storage changes for non reverting skyToMkr
rule skyToMkr(address usr, uint256 wad) {
env e;

require e.msg.sender != currentContract;

requireInvariant balanceSumSky_equals_totalSupply();
requireInvariant balanceSumMkr_equals_totalSupply();

address other;
require other != e.msg.sender;
address other2;
require other2 != usr;

mathint rate = rate();
mathint skyTotalSupplyBefore = sky.totalSupply();
mathint skyBalanceOfSenderBefore = sky.balanceOf(e.msg.sender);
mathint skyBalanceOfOtherBefore = sky.balanceOf(other);
mathint mkrTotalSupplyBefore = mkr.totalSupply();
mathint mkrBalanceOfUsrBefore = mkr.balanceOf(usr);
mathint mkrBalanceOfOtherBefore = mkr.balanceOf(other2);

skyToMkr(e, usr, wad);

mathint skyTotalSupplyAfter = sky.totalSupply();
mathint skyBalanceOfSenderAfter = sky.balanceOf(e.msg.sender);
mathint skyBalanceOfOtherAfter = sky.balanceOf(other);
mathint mkrTotalSupplyAfter = mkr.totalSupply();
mathint mkrBalanceOfUsrAfter = mkr.balanceOf(usr);
mathint mkrBalanceOfOtherAfter = mkr.balanceOf(other2);

assert skyTotalSupplyAfter == skyTotalSupplyBefore - wad, "skyToMkr did not decrease sky.totalSupply by wad";
assert skyBalanceOfSenderAfter == skyBalanceOfSenderBefore - wad, "skyToMkr did not decrease sky.balanceOf[sender] by wad";
assert skyBalanceOfOtherAfter == skyBalanceOfOtherBefore, "skyToMkr did not keep unchanged the rest of sky.balanceOf[x]";
assert mkrTotalSupplyAfter == mkrTotalSupplyBefore + wad / rate, "skyToMkr did not increase mkr.totalSupply by wad / rate";
assert mkrBalanceOfUsrAfter == mkrBalanceOfUsrBefore + wad / rate, "skyToMkr did not increase mkr.balanceOf[usr] by wad / rate";
assert mkrBalanceOfOtherAfter == mkrBalanceOfOtherBefore, "skyToMkr did not keep unchanged the rest of mkr.balanceOf[x]";
}

// Verify revert rules on skyToMkr
rule skyToMkr_revert(address usr, uint256 wad) {
env e;

requireInvariant balanceSumSky_equals_totalSupply();
requireInvariant balanceSumMkr_equals_totalSupply();

require e.msg.sender != currentContract;

mathint rate = rate();
require rate > 0;
mathint skyBalanceOfSender = sky.balanceOf(e.msg.sender);
mathint skyAllowanceSenderMkrSky = sky.allowance(e.msg.sender, currentContract);
mathint mkrWardsMkrSky = mkr.wards(currentContract);
mathint mkrTotalSupply = mkr.totalSupply();

skyToMkr@withrevert(e, usr, wad);

bool revert1 = e.msg.value > 0;
bool revert2 = skyBalanceOfSender < to_mathint(wad);
bool revert3 = skyAllowanceSenderMkrSky < to_mathint(wad);
bool revert4 = mkrWardsMkrSky != 1;
bool revert5 = mkrTotalSupply + wad / rate > max_uint256;
bool revert6 = usr == 0 || usr == mkr;

assert revert1 => lastReverted, "revert1 failed";
assert revert2 => lastReverted, "revert2 failed";
assert revert3 => lastReverted, "revert3 failed";
assert revert4 => lastReverted, "revert4 failed";
assert revert5 => lastReverted, "revert5 failed";
assert revert6 => lastReverted, "revert6 failed";
assert lastReverted => revert1 || revert2 || revert3 ||
revert4 || revert5 || revert6, "Revert rules are not covering all the cases";
}
11 changes: 11 additions & 0 deletions certora/SignerMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.21;

contract SignerMock {
bytes32 sig;

function isValidSignature(bytes32, bytes memory) external view returns (bytes32) {
return sig;
}
}
Loading
Loading