-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathMintBurnEthereum.spec
129 lines (109 loc) · 7.1 KB
/
MintBurnEthereum.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
127
128
129
// This is spec is taken from the Open Zeppelin repositories at https://github.com/OpenZeppelin/openzeppelin-contracts/blob/448efeea6640bbbc09373f03fbc9c88e280147ba/certora/specs/ERC20.spec, and patched to support the DelegationToken.
import "Delegation.spec";
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rules: only the token holder or an approved third party can reduce an account's balance │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule onlyAuthorizedCanTransfer(env e, method f) {
requireInvariant totalSupplyIsSumOfBalances();
requireInvariant balancesLTEqTotalSupply();
requireInvariant twoBalancesLTEqTotalSupply();
calldataarg args;
address account;
uint256 allowanceBefore = allowance(account, e.msg.sender);
uint256 balanceBefore = balanceOf(account);
f(e, args);
uint256 balanceAfter = balanceOf(account);
assert (
balanceAfter < balanceBefore
) => (
e.msg.sender == account ||
f.selector == sig:transferFrom(address, address, uint256).selector && balanceBefore - balanceAfter <= to_mathint(allowanceBefore)
);
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rules: only mint and burn can change total supply │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule noChangeTotalSupply(env e) {
requireInvariant totalSupplyIsSumOfBalances();
requireInvariant balancesLTEqTotalSupply();
method f;
calldataarg args;
uint256 totalSupplyBefore = totalSupply();
f(e, args);
uint256 totalSupplyAfter = totalSupply();
assert totalSupplyAfter > totalSupplyBefore => f.selector == sig:mint(address,uint256).selector || f.selector == sig:initialize(address, address).selector;
assert totalSupplyAfter < totalSupplyBefore => f.selector == sig:burn(uint256).selector;
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rules: mint behavior and side effects │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule mint(env e) {
requireInvariant totalSupplyIsSumOfBalances();
requireInvariant balancesLTEqTotalSupply();
requireInvariant delegatedVotingPowerLTEqTotalVotingPower();
assert isTotalSupplyGTEqSumOfVotingPower();
requireInvariant zeroAddressNoVotingPower();
require nonpayable(e);
address to;
address other;
uint256 amount;
// cache state
uint256 toBalanceBefore = balanceOf(to);
uint256 toVotingPowerBefore = delegatedVotingPower(delegatee(to));
uint256 otherBalanceBefore = balanceOf(other);
uint256 totalSupplyBefore = totalSupply();
// run transaction
mint@withrevert(e, to, amount);
// check outcome
if (lastReverted) {
assert e.msg.sender != owner() || to == 0 || totalSupplyBefore + amount > max_uint256 ||
toVotingPowerBefore + amount > max_uint256;
} else {
// updates balance and totalSupply
assert e.msg.sender == owner();
assert to_mathint(balanceOf(to)) == toBalanceBefore + amount;
assert to_mathint(totalSupply()) == totalSupplyBefore + amount;
// no other balance is modified
assert balanceOf(other) != otherBalanceBefore => other == to;
}
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rules: burn behavior and side effects │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule burn(env e) {
requireInvariant balancesLTEqTotalSupply();
assert isTotalSupplyGTEqSumOfVotingPower();
requireInvariant zeroAddressNoVotingPower();
requireInvariant delegatedVotingPowerLTEqTotalVotingPower();
require nonpayable(e);
address from;
address other;
uint256 amount;
require from == e.msg.sender;
// cache state
uint256 fromBalanceBefore = balanceOf(from);
uint256 fromVotingPowerBefore = delegatedVotingPower(delegatee(from));
uint256 toVotingPowerBefore = delegatedVotingPower(delegatee(0x0));
uint256 otherBalanceBefore = balanceOf(other);
uint256 totalSupplyBefore = totalSupply();
// run transaction
burn@withrevert(e, amount);
// check outcome
if (lastReverted) {
assert e.msg.sender == 0x0 || fromBalanceBefore < amount || fromVotingPowerBefore < amount ;
} else {
// updates balance and totalSupply
assert to_mathint(balanceOf(from)) == fromBalanceBefore - amount;
assert to_mathint(totalSupply()) == totalSupplyBefore - amount;
// no other balance is modified
assert balanceOf(other) != otherBalanceBefore => other == from;
}
}