From 2c08f85744a18a2cd631b71c6719e48674ae9088 Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Sun, 26 Sep 2021 00:21:08 +0300 Subject: [PATCH 001/254] start work on governor --- .../GovernorCountingSimpleHarness.sol | 29 +++++++ certora/harnesses/GovernorHarness.sol | 58 ++++++++++++++ .../GovernorProposalThresholdHarness.sol | 58 ++++++++++++++ .../GovernorTimelockCompoundHarness.sol | 58 ++++++++++++++ certora/harnesses/GovernorVotesHarness.sol | 52 ++++++++++++ .../GovernorVotesQuorumFractionHarness.sol | 46 +++++++++++ certora/scripts/Governor.sh | 2 + certora/scripts/GovernorCountingSimple.sh | 2 + certora/scripts/GovernorProposalThreshold.sh | 2 + certora/scripts/GovernorTimelockCompound.sh | 2 + certora/scripts/GovernorVotes.sh | 2 + .../GovernorVotesQuorumFractionHarness.sh | 2 + certora/scripts/check.sh | 7 ++ certora/specs/GovernorBase.spec | 79 +++++++++++++++++++ certora/specs/Privileged.spec | 31 ++++++++ 15 files changed, 430 insertions(+) create mode 100644 certora/harnesses/GovernorCountingSimpleHarness.sol create mode 100644 certora/harnesses/GovernorHarness.sol create mode 100644 certora/harnesses/GovernorProposalThresholdHarness.sol create mode 100644 certora/harnesses/GovernorTimelockCompoundHarness.sol create mode 100644 certora/harnesses/GovernorVotesHarness.sol create mode 100644 certora/harnesses/GovernorVotesQuorumFractionHarness.sol create mode 100755 certora/scripts/Governor.sh create mode 100755 certora/scripts/GovernorCountingSimple.sh create mode 100755 certora/scripts/GovernorProposalThreshold.sh create mode 100755 certora/scripts/GovernorTimelockCompound.sh create mode 100755 certora/scripts/GovernorVotes.sh create mode 100755 certora/scripts/GovernorVotesQuorumFractionHarness.sh create mode 100755 certora/scripts/check.sh create mode 100644 certora/specs/GovernorBase.spec create mode 100644 certora/specs/Privileged.spec diff --git a/certora/harnesses/GovernorCountingSimpleHarness.sol b/certora/harnesses/GovernorCountingSimpleHarness.sol new file mode 100644 index 000000000..52e980a81 --- /dev/null +++ b/certora/harnesses/GovernorCountingSimpleHarness.sol @@ -0,0 +1,29 @@ +import "../../contracts/governance/extensions/GovernorCountingSimple.sol"; + +contract GovernorCountingSimpleHarness is GovernorCountingSimple { + + mapping(uint256 => uint256) _quorum; + + function quorum(uint256 blockNumber) public view override virtual returns (uint256) { + return _quorum[blockNumber]; + } + + mapping (address => mapping (uint256 => uint256)) _getVotes; + + function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { + return _getVotes[account][blockNumber]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + constructor(string memory name) Governor(name) {} + +} \ No newline at end of file diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol new file mode 100644 index 000000000..d2df3d450 --- /dev/null +++ b/certora/harnesses/GovernorHarness.sol @@ -0,0 +1,58 @@ +import "../../contracts/governance/Governor.sol"; + +contract GovernorHarness is Governor { + + mapping(uint256 => uint256) _quorum; + + function quorum(uint256 blockNumber) public view override virtual returns (uint256) { + return _quorum[blockNumber]; + } + + mapping (address => mapping (uint256 => uint256)) _getVotes; + + function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { + return _getVotes[account][blockNumber]; + } + + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + return __quoromReached[proposalId]; + } + + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + return __voteSucceeded[proposalId]; + } + + //string _COUNTING_MODE; + function COUNTING_MODE() public pure override virtual returns (string memory) { + return "dummy"; + } + + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { + return _hasVoted[proposalId][account]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name) Governor(name) {} + +} \ No newline at end of file diff --git a/certora/harnesses/GovernorProposalThresholdHarness.sol b/certora/harnesses/GovernorProposalThresholdHarness.sol new file mode 100644 index 000000000..8301a76d8 --- /dev/null +++ b/certora/harnesses/GovernorProposalThresholdHarness.sol @@ -0,0 +1,58 @@ +import "../../contracts/governance/extensions/GovernorProposalThreshold.sol"; + +contract GovernorProposalThresholdHarness is GovernorProposalThreshold { + + mapping(uint256 => uint256) _quorum; + + function quorum(uint256 blockNumber) public view override virtual returns (uint256) { + return _quorum[blockNumber]; + } + + mapping (address => mapping (uint256 => uint256)) _getVotes; + + function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { + return _getVotes[account][blockNumber]; + } + + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + return __quoromReached[proposalId]; + } + + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + return __voteSucceeded[proposalId]; + } + + //string _COUNTING_MODE; + function COUNTING_MODE() public pure override virtual returns (string memory) { + return "dummy"; + } + + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { + return _hasVoted[proposalId][account]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name) Governor(name) {} + +} \ No newline at end of file diff --git a/certora/harnesses/GovernorTimelockCompoundHarness.sol b/certora/harnesses/GovernorTimelockCompoundHarness.sol new file mode 100644 index 000000000..5513546ab --- /dev/null +++ b/certora/harnesses/GovernorTimelockCompoundHarness.sol @@ -0,0 +1,58 @@ +import "../../contracts/governance/extensions/GovernorTimelockCompound.sol"; + +contract GovernorTimelockCompoundHarness is GovernorTimelockCompound { + + mapping(uint256 => uint256) _quorum; + + function quorum(uint256 blockNumber) public view override virtual returns (uint256) { + return _quorum[blockNumber]; + } + + mapping (address => mapping (uint256 => uint256)) _getVotes; + + function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { + return _getVotes[account][blockNumber]; + } + + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + return __quoromReached[proposalId]; + } + + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + return __voteSucceeded[proposalId]; + } + + //string _COUNTING_MODE; + function COUNTING_MODE() public pure override virtual returns (string memory) { + return "dummy"; + } + + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { + return _hasVoted[proposalId][account]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name, ICompoundTimelock timelock) Governor(name) GovernorTimelockCompound(timelock) {} + +} \ No newline at end of file diff --git a/certora/harnesses/GovernorVotesHarness.sol b/certora/harnesses/GovernorVotesHarness.sol new file mode 100644 index 000000000..65a095156 --- /dev/null +++ b/certora/harnesses/GovernorVotesHarness.sol @@ -0,0 +1,52 @@ +import "../../contracts/governance/extensions/GovernorVotes.sol"; + +contract GovernorVotesHarness is GovernorVotes { + + mapping(uint256 => uint256) _quorum; + + function quorum(uint256 blockNumber) public view override virtual returns (uint256) { + return _quorum[blockNumber]; + } + + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + return __quoromReached[proposalId]; + } + + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + return __voteSucceeded[proposalId]; + } + + //string _COUNTING_MODE; + function COUNTING_MODE() public pure override virtual returns (string memory) { + return "dummy"; + } + + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { + return _hasVoted[proposalId][account]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name) Governor(name) {} + +} \ No newline at end of file diff --git a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol new file mode 100644 index 000000000..3c7015eb9 --- /dev/null +++ b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol @@ -0,0 +1,46 @@ +import "../../contracts/governance/extensions/GovernorVotesQuorumFractionGovernor.sol"; + +contract GovernorVotesQuorumFractionHarness is GovernorVotesQuorumFraction { + + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + return __quoromReached[proposalId]; + } + + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + return __voteSucceeded[proposalId]; + } + + //string _COUNTING_MODE; + function COUNTING_MODE() public pure override virtual returns (string memory) { + return "dummy"; + } + + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { + return _hasVoted[proposalId][account]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name) Governor(name) {} + +} \ No newline at end of file diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh new file mode 100755 index 000000000..4caada718 --- /dev/null +++ b/certora/scripts/Governor.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorHarness.sol \ + --verify GovernorHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorCountingSimple.sh b/certora/scripts/GovernorCountingSimple.sh new file mode 100755 index 000000000..95c2c2551 --- /dev/null +++ b/certora/scripts/GovernorCountingSimple.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorCountingSimpleHarness.sol \ + --verify GovernorCountingSimpleHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorProposalThreshold.sh b/certora/scripts/GovernorProposalThreshold.sh new file mode 100755 index 000000000..cb10572fc --- /dev/null +++ b/certora/scripts/GovernorProposalThreshold.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorProposalThresholdHarness.sol \ + --verify GovernorProposalThresholdHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorTimelockCompound.sh b/certora/scripts/GovernorTimelockCompound.sh new file mode 100755 index 000000000..9cbdd1ea0 --- /dev/null +++ b/certora/scripts/GovernorTimelockCompound.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorTimelockCompoundHarness.sol \ + --verify GovernorTimelockCompoundHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorVotes.sh b/certora/scripts/GovernorVotes.sh new file mode 100755 index 000000000..1dc476445 --- /dev/null +++ b/certora/scripts/GovernorVotes.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorVotesHarness.sol \ + --verify GovernorVotesHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorVotesQuorumFractionHarness.sh b/certora/scripts/GovernorVotesQuorumFractionHarness.sh new file mode 100755 index 000000000..239339ade --- /dev/null +++ b/certora/scripts/GovernorVotesQuorumFractionHarness.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorVotesQuorumFractionHarness.sol \ + --verify GovernorVotesQuorumFractionHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/check.sh b/certora/scripts/check.sh new file mode 100755 index 000000000..0cb980dc3 --- /dev/null +++ b/certora/scripts/check.sh @@ -0,0 +1,7 @@ +echo "Usage: Contract Spec" +echo "e.g. GovernorVotes Privileged" +Contract=$1 +Spec=$2 +shift 2 +certoraRun certora/harnesses/${Contract}Harness.sol \ + --verify ${Contract}Harness:certora/specs/${Spec}.spec "$@" \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec new file mode 100644 index 000000000..58c07582f --- /dev/null +++ b/certora/specs/GovernorBase.spec @@ -0,0 +1,79 @@ +// Governor.sol base definitions +methods { + proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart +} +ghost proposalVoteStart(uint256) returns uint64 { + init_state axiom forall uint256 pId. proposalVoteStart(pId) == 0; +} +ghost proposalVoteEnd(uint256) returns uint64 { + init_state axiom forall uint256 pId. proposalVoteEnd(pId) == 0; +} +ghost proposalExecuted(uint256) returns bool { + init_state axiom forall uint256 pId. !proposalExecuted(pId); +} +ghost proposalCanceled(uint256) returns bool { + init_state axiom forall uint256 pId. !proposalCanceled(pId); +} + +hook Sstore _proposals[KEY uint256 pId].(offset 0) uint64 newValue STORAGE { + havoc proposalVoteStart assuming ( + proposalVoteStart@new(pId) == newValue + && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) + ); +} + +hook Sload uint64 value _proposals[KEY uint256 pId].(offset 0) STORAGE { + require proposalVoteStart(pId) == value; +} + + +hook Sstore _proposals[KEY uint256 pId].(offset 32).(offset 0) uint64 newValue STORAGE { + havoc proposalVoteEnd assuming ( + proposalVoteEnd@new(pId) == newValue + && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2)) + ); +} + +hook Sload uint64 value _proposals[KEY uint256 pId].(offset 32).(offset 0) STORAGE { + require proposalVoteEnd(pId) == value; +} + +rule sanityCheckVoteStart(method f, uint256 pId) { + uint64 preGhost = proposalVoteStart(pId); + uint256 pre = proposalSnapshot(pId); + + env e; + calldataarg arg; + f(e, arg); + + uint64 postGhost = proposalVoteStart(pId); + uint256 post = proposalSnapshot(pId); + + assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; + assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; +} + +rule sanityCheckVoteEnd(method f, uint256 pId) { + uint64 preGhost = proposalVoteEnd(pId); + uint256 pre = proposalSnapshot(pId); + + env e; + calldataarg arg; + f(e, arg); + + uint64 postGhost = proposalVoteEnd(pId); + uint256 post = proposalSnapshot(pId); + + assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; + assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; +} + +/** + * A proposal cannot end unless it started. + */ +invariant voteStartBeforeVoteEnd(uint256 pId) proposalVoteStart(pId) < proposalVoteEnd(pId) + +/** + * A proposal cannot be both executed and canceled. + */ +invariant noBothExecutedAndCanceled(uint256 pId) !proposalExecuted(pId) || !proposalCanceled(pId) \ No newline at end of file diff --git a/certora/specs/Privileged.spec b/certora/specs/Privileged.spec new file mode 100644 index 000000000..f9615a619 --- /dev/null +++ b/certora/specs/Privileged.spec @@ -0,0 +1,31 @@ +definition knownAsNonPrivileged(method f) returns bool = false +/* ( f.selector == isWhitelistedOtoken(address).selector || + f.selector == isWhitelistedProduct(address,address,address,bool).selector || + f.selector == owner().selector || + f.selector == isWhitelistedCallee(address).selector || + f.selector == whitelistOtoken(address).selector || + f.selector == addressBook().selector || + f.selector == isWhitelistedCollateral(address).selector )*/; + + + +rule privilegedOperation(method f, address privileged) +description "$f can be called by more than one user without reverting" +{ + env e1; + calldataarg arg; + require !knownAsNonPrivileged(f); + require e1.msg.sender == privileged; + + storage initialStorage = lastStorage; + invoke f(e1, arg); // privileged succeeds executing candidate privileged operation. + bool firstSucceeded = !lastReverted; + + env e2; + calldataarg arg2; + require e2.msg.sender != privileged; + invoke f(e2, arg2) at initialStorage; // unprivileged + bool secondSucceeded = !lastReverted; + + assert !(firstSucceeded && secondSucceeded), "${f.selector} can be called by both ${e1.msg.sender} and ${e2.msg.sender}, so it is not privileged"; +} From f239fa56dd35c7b5b52303646311c07dcef15117 Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Sun, 26 Sep 2021 00:25:59 +0300 Subject: [PATCH 002/254] Back to expected pattern? --- certora/specs/GovernorBase.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 58c07582f..85c20162b 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -15,14 +15,14 @@ ghost proposalCanceled(uint256) returns bool { init_state axiom forall uint256 pId. !proposalCanceled(pId); } -hook Sstore _proposals[KEY uint256 pId].(offset 0) uint64 newValue STORAGE { +hook Sstore _proposals[KEY uint256 pId].(offset 0).(offset 0) uint64 newValue STORAGE { havoc proposalVoteStart assuming ( proposalVoteStart@new(pId) == newValue && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) ); } -hook Sload uint64 value _proposals[KEY uint256 pId].(offset 0) STORAGE { +hook Sload uint64 value _proposals[KEY uint256 pId].(offset 0).(offset 0) STORAGE { require proposalVoteStart(pId) == value; } From fdc4b0cf239df941aaf5fb8ebdad5c6c8981a2c9 Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Sun, 26 Sep 2021 01:39:27 +0300 Subject: [PATCH 003/254] fixes --- certora/specs/GovernorBase.spec | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 85c20162b..99c712872 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -2,6 +2,7 @@ methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart } + ghost proposalVoteStart(uint256) returns uint64 { init_state axiom forall uint256 pId. proposalVoteStart(pId) == 0; } @@ -15,27 +16,27 @@ ghost proposalCanceled(uint256) returns bool { init_state axiom forall uint256 pId. !proposalCanceled(pId); } -hook Sstore _proposals[KEY uint256 pId].(offset 0).(offset 0) uint64 newValue STORAGE { +hook Sstore _proposals[KEY uint256 pId] uint64 newValue STORAGE { havoc proposalVoteStart assuming ( - proposalVoteStart@new(pId) == newValue + proposalVoteStart@new(pId) == newValue & (max_uint64-1) && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) ); } -hook Sload uint64 value _proposals[KEY uint256 pId].(offset 0).(offset 0) STORAGE { - require proposalVoteStart(pId) == value; +hook Sload uint64 value _proposals[KEY uint256 pId] STORAGE { + require proposalVoteStart(pId) == value & (max_uint64-1); } hook Sstore _proposals[KEY uint256 pId].(offset 32).(offset 0) uint64 newValue STORAGE { havoc proposalVoteEnd assuming ( - proposalVoteEnd@new(pId) == newValue + proposalVoteEnd@new(pId) == newValue & (max_uint64-1) && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2)) ); } hook Sload uint64 value _proposals[KEY uint256 pId].(offset 32).(offset 0) STORAGE { - require proposalVoteEnd(pId) == value; + require proposalVoteEnd(pId) == value & (max_uint64-1); } rule sanityCheckVoteStart(method f, uint256 pId) { From 4c1d5e01c636ad8a0e822a6f1249317900c4f59c Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Sun, 26 Sep 2021 01:43:16 +0300 Subject: [PATCH 004/254] fixes --- certora/specs/GovernorBase.spec | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 99c712872..32b0fd21b 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -16,27 +16,29 @@ ghost proposalCanceled(uint256) returns bool { init_state axiom forall uint256 pId. !proposalCanceled(pId); } +definition mask_uint64() returns uint256 = max_uint64 - 1; + hook Sstore _proposals[KEY uint256 pId] uint64 newValue STORAGE { havoc proposalVoteStart assuming ( - proposalVoteStart@new(pId) == newValue & (max_uint64-1) + proposalVoteStart@new(pId) == newValue & mask_uint64() && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) ); } hook Sload uint64 value _proposals[KEY uint256 pId] STORAGE { - require proposalVoteStart(pId) == value & (max_uint64-1); + require proposalVoteStart(pId) == value & mask_uint64(); } hook Sstore _proposals[KEY uint256 pId].(offset 32).(offset 0) uint64 newValue STORAGE { havoc proposalVoteEnd assuming ( - proposalVoteEnd@new(pId) == newValue & (max_uint64-1) + proposalVoteEnd@new(pId) == newValue & mask_uint64() && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2)) ); } hook Sload uint64 value _proposals[KEY uint256 pId].(offset 32).(offset 0) STORAGE { - require proposalVoteEnd(pId) == value & (max_uint64-1); + require proposalVoteEnd(pId) == value & mask_uint64(); } rule sanityCheckVoteStart(method f, uint256 pId) { From 22030f2fd31ac126d1ecedf99391ca9a21b934dd Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Thu, 7 Oct 2021 11:58:47 +0300 Subject: [PATCH 005/254] rule drafts --- certora/harnesses/GovernorHarness.sol | 4 +- certora/specs/GovernorBase.spec | 72 ++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index d2df3d450..332f8b863 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -15,12 +15,12 @@ contract GovernorHarness is Governor { } mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 32b0fd21b..60949c987 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -1,6 +1,11 @@ // Governor.sol base definitions methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart + hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree + + // internal functions made public in harness: + _quorumReached(uint256) returns bool envfree + _voteSucceeded(uint256) returns bool envfree } ghost proposalVoteStart(uint256) returns uint64 { @@ -79,4 +84,69 @@ invariant voteStartBeforeVoteEnd(uint256 pId) proposalVoteStart(pId) < proposalV /** * A proposal cannot be both executed and canceled. */ -invariant noBothExecutedAndCanceled(uint256 pId) !proposalExecuted(pId) || !proposalCanceled(pId) \ No newline at end of file +invariant noBothExecutedAndCanceled(uint256 pId) !proposalExecuted(pId) || !proposalCanceled(pId) + +/** + * A proposal cannot be executed nor canceled before it starts + */ +invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.timestamp < proposalVoteStart(pId) => !proposalExecuted(pId) && !proposalCanceled(pId) + +/** + * The voting must start not before the proposal’s creation time + */ +rule noStartBeforeCreation(uint256 pId) { + uint previousStart = proposalVoteStart(pId); + require previousStart == 0; + env e; + calldataarg arg; + propose(e, arg); + + uint newStart = proposalVoteStart(pId); + // if created, start is after creation + assert newStart != 0 => newStart > e.block.timestamp; +} + +/** + * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) + */ +rule checkHashProposal { + address[] t1; + address[] t2; + uint256[] v1; + uint256[] v2; + bytes[] c1; + bytes[] c2; + bytes32 d1; + bytes32 d2; + + uint256 h1 = hashProposal(t1,v1,c1,d1); + uint256 h2 = hashProposal(t2,v2,c2,d2); + bool equalHashes = h1 == h2; + assert equalHashes => t1.length == t2.length; + assert equalHashes => v1.length == v2.length; + assert equalHashes => c1.length == c2.length; + assert equalHashes => d1 == d2; +} + +/** + * A proposal could be executed only if quorum was reached and vote succeeded + */ +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) proposalExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) + +/** + * Once a proposal is created, voteStart and voteEnd are immutable + */ +rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint _voteStart = proposalVoteStart(pId); + uint _voteEnd = proposalVoteEnd(pId); + require _voteStart > 0; // proposal was created + + env e; + calldataarg arg; + f(e, arg); + + uint voteStart_ = proposalVoteStart(pId); + uint voteEnd_ = proposalVoteEnd(pId); + assert _voteStart == voteStart_; + assert _voteEnd == voteEnd_; +} \ No newline at end of file From 6776cc6ee47ac0109e24c86af5951958adfd082c Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Tue, 2 Nov 2021 14:01:48 +0200 Subject: [PATCH 006/254] ignore certora's generated files --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 0a62cf0b3..c60c5d945 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,8 @@ allFiredEvents # hardhat cache artifacts + +# Certora +.certora* +.last_confs +certora_* From cac49bfc2ee335aae66689598a799e1c2ecf9db7 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Wed, 3 Nov 2021 17:05:06 +0200 Subject: [PATCH 007/254] sanity rule preparations --- .../harnesses/GovernorProposalThresholdHarness.sol | 9 +++++++-- .../harnesses/GovernorTimelockCompoundHarness.sol | 4 ++-- certora/harnesses/GovernorVotesHarness.sol | 7 +++---- .../GovernorVotesQuorumFractionHarness.sol | 6 +++--- certora/scripts/check.sh | 4 +++- certora/scripts/sanity.sh | 9 +++++++++ certora/specs/sanity.spec | 14 ++++++++++++++ contracts/governance/Governor.sol | 4 ++-- .../extensions/GovernorCountingSimple.sol | 4 ++-- 9 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 certora/scripts/sanity.sh create mode 100644 certora/specs/sanity.spec diff --git a/certora/harnesses/GovernorProposalThresholdHarness.sol b/certora/harnesses/GovernorProposalThresholdHarness.sol index 8301a76d8..1d0559a60 100644 --- a/certora/harnesses/GovernorProposalThresholdHarness.sol +++ b/certora/harnesses/GovernorProposalThresholdHarness.sol @@ -15,12 +15,12 @@ contract GovernorProposalThresholdHarness is GovernorProposalThreshold { } mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } @@ -53,6 +53,11 @@ contract GovernorProposalThresholdHarness is GovernorProposalThreshold { // havoc something } + uint256 _proposalThreshold; + function proposalThreshold() public view override virtual returns (uint256) { + return _proposalThreshold; + } + constructor(string memory name) Governor(name) {} } \ No newline at end of file diff --git a/certora/harnesses/GovernorTimelockCompoundHarness.sol b/certora/harnesses/GovernorTimelockCompoundHarness.sol index 5513546ab..f8a85e53f 100644 --- a/certora/harnesses/GovernorTimelockCompoundHarness.sol +++ b/certora/harnesses/GovernorTimelockCompoundHarness.sol @@ -15,12 +15,12 @@ contract GovernorTimelockCompoundHarness is GovernorTimelockCompound { } mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } diff --git a/certora/harnesses/GovernorVotesHarness.sol b/certora/harnesses/GovernorVotesHarness.sol index 65a095156..8ed638e41 100644 --- a/certora/harnesses/GovernorVotesHarness.sol +++ b/certora/harnesses/GovernorVotesHarness.sol @@ -9,12 +9,12 @@ contract GovernorVotesHarness is GovernorVotes { } mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } @@ -47,6 +47,5 @@ contract GovernorVotesHarness is GovernorVotes { // havoc something } - constructor(string memory name) Governor(name) {} - + constructor(ERC20Votes tokenAddr) GovernorVotes(tokenAddr) {} } \ No newline at end of file diff --git a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol index 3c7015eb9..65be741d4 100644 --- a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol +++ b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol @@ -1,14 +1,14 @@ -import "../../contracts/governance/extensions/GovernorVotesQuorumFractionGovernor.sol"; +import "../../contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; contract GovernorVotesQuorumFractionHarness is GovernorVotesQuorumFraction { mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } diff --git a/certora/scripts/check.sh b/certora/scripts/check.sh index 0cb980dc3..cdb356e90 100755 --- a/certora/scripts/check.sh +++ b/certora/scripts/check.sh @@ -4,4 +4,6 @@ Contract=$1 Spec=$2 shift 2 certoraRun certora/harnesses/${Contract}Harness.sol \ - --verify ${Contract}Harness:certora/specs/${Spec}.spec "$@" \ No newline at end of file + --verify ${Contract}Harness:certora/specs/${Spec}.spec "$@" \ + --solc solc8.0 + \ No newline at end of file diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh new file mode 100644 index 000000000..4db90ebff --- /dev/null +++ b/certora/scripts/sanity.sh @@ -0,0 +1,9 @@ +for f in certora/harnesses/*.sol +do + echo "Processing $f" + file=$(basename $f) + echo ${file%.*} + certoraRun certora/harnesses/$file \ + --verify ${file%.*}:certora/specs/sanity.spec "$@" \ + --solc solc8.0 +done \ No newline at end of file diff --git a/certora/specs/sanity.spec b/certora/specs/sanity.spec new file mode 100644 index 000000000..e08f68845 --- /dev/null +++ b/certora/specs/sanity.spec @@ -0,0 +1,14 @@ +/* +This rule looks for a non-reverting execution path to each method, including those overridden in the harness. +A method has such an execution path if it violates this rule. +How it works: + - If there is a non-reverting execution path, we reach the false assertion, and the sanity fails. + - If all execution paths are reverting, we never call the assertion, and the method will pass this rule vacuously. +*/ + +rule sanity(method f) { + env e; + calldataarg arg; + f(e, arg); + assert false; +} \ No newline at end of file diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index 741e02f7d..12aa1950b 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -146,12 +146,12 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) internal view virtual returns (bool); + function _quorumReached(uint256 proposalId) public view virtual returns (bool); /** * @dev Is the proposal successful or not. */ - function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool); + function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); /** * @dev Register a vote with a given support and voting weight. diff --git a/contracts/governance/extensions/GovernorCountingSimple.sol b/contracts/governance/extensions/GovernorCountingSimple.sol index 399f8d7fe..00f4d4aa8 100644 --- a/contracts/governance/extensions/GovernorCountingSimple.sol +++ b/contracts/governance/extensions/GovernorCountingSimple.sol @@ -63,7 +63,7 @@ abstract contract GovernorCountingSimple is Governor { /** * @dev See {Governor-_quorumReached}. */ - function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { + function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { ProposalVote storage proposalvote = _proposalVotes[proposalId]; return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes; @@ -72,7 +72,7 @@ abstract contract GovernorCountingSimple is Governor { /** * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. */ - function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { + function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { ProposalVote storage proposalvote = _proposalVotes[proposalId]; return proposalvote.forVotes > proposalvote.againstVotes; From 72d4e9c29c0acbf757c295e85d79b46049eda785 Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Wed, 3 Nov 2021 17:09:27 +0200 Subject: [PATCH 008/254] multiple inheritance is tricky --- certora/harnesses/GovernorVotesHarness.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/harnesses/GovernorVotesHarness.sol b/certora/harnesses/GovernorVotesHarness.sol index 8ed638e41..3e06c8daa 100644 --- a/certora/harnesses/GovernorVotesHarness.sol +++ b/certora/harnesses/GovernorVotesHarness.sol @@ -47,5 +47,5 @@ contract GovernorVotesHarness is GovernorVotes { // havoc something } - constructor(ERC20Votes tokenAddr) GovernorVotes(tokenAddr) {} + constructor(ERC20Votes tokenAddr, string memory name) GovernorVotes(tokenAddr) Governor(name) {} } \ No newline at end of file From a7104355359f2b64eae8acf39ac2c7377ee30317 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Wed, 3 Nov 2021 17:24:35 +0200 Subject: [PATCH 009/254] multiple inheritance is tricky x2 --- certora/harnesses/GovernorVotesQuorumFractionHarness.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol index 65be741d4..86ae202ad 100644 --- a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol +++ b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol @@ -41,6 +41,7 @@ contract GovernorVotesQuorumFractionHarness is GovernorVotesQuorumFraction { // havoc something } - constructor(string memory name) Governor(name) {} + constructor(ERC20Votes tokenAddr, string memory name, uint256 quorumNumeratorValue) + GovernorVotesQuorumFraction(quorumNumeratorValue) GovernorVotes(tokenAddr) Governor(name) {} } \ No newline at end of file From 69f87ad916810b9bd99c30ae9d1465a01932f1f7 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 11:27:44 +0200 Subject: [PATCH 010/254] slight script changes and ghost fix --- certora/harnesses/GovernorHarness.sol | 13 +++++++++++++ certora/scripts/Governor.sh | 8 +++++++- certora/scripts/check.sh | 2 +- certora/scripts/sanity.sh | 4 +++- certora/specs/GovernorBase.spec | 28 +++++++++++++++++---------- contracts/governance/Governor.sol | 4 ++-- 6 files changed, 44 insertions(+), 15 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index 332f8b863..f12a3ab4e 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -8,42 +8,55 @@ contract GovernorHarness is Governor { return _quorum[blockNumber]; } + mapping (address => mapping (uint256 => uint256)) _getVotes; function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { return _getVotes[account][blockNumber]; } + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } + //string _COUNTING_MODE; function COUNTING_MODE() public pure override virtual returns (string memory) { return "dummy"; } + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { return _hasVoted[proposalId][account]; } + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { return _votingDelay; } + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { return _votingPeriod; } + function _countVote( uint256 proposalId, address account, diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index 4caada718..cba7edf6b 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -1,2 +1,8 @@ certoraRun certora/harnesses/GovernorHarness.sol \ - --verify GovernorHarness:certora/specs/Privileged.spec \ No newline at end of file + --verify GovernorHarness:certora/specs/GovernorBase.spec \ + --solc solc8.0 \ + --staging \ + --msg $1 \ + --disableLocalTypeChecking \ + --rule voteStartBeforeVoteEnd + diff --git a/certora/scripts/check.sh b/certora/scripts/check.sh index cdb356e90..e3291eb1c 100755 --- a/certora/scripts/check.sh +++ b/certora/scripts/check.sh @@ -5,5 +5,5 @@ Spec=$2 shift 2 certoraRun certora/harnesses/${Contract}Harness.sol \ --verify ${Contract}Harness:certora/specs/${Spec}.spec "$@" \ - --solc solc8.0 + --solc solc8.0 --staging --rule noBothExecutedAndCanceled \ No newline at end of file diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index 4db90ebff..0da31d15e 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -5,5 +5,7 @@ do echo ${file%.*} certoraRun certora/harnesses/$file \ --verify ${file%.*}:certora/specs/sanity.spec "$@" \ - --solc solc8.0 + --solc solc8.0 \ + --staging \ + --msg "sanity ${file}" done \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 60949c987..b54f8c0e8 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -21,31 +21,34 @@ ghost proposalCanceled(uint256) returns bool { init_state axiom forall uint256 pId. !proposalCanceled(pId); } -definition mask_uint64() returns uint256 = max_uint64 - 1; - -hook Sstore _proposals[KEY uint256 pId] uint64 newValue STORAGE { +hook Sstore _proposals[KEY uint256 pId].voteStart._deadline uint64 newValue STORAGE { havoc proposalVoteStart assuming ( - proposalVoteStart@new(pId) == newValue & mask_uint64() + proposalVoteStart@new(pId) == newValue && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) ); } -hook Sload uint64 value _proposals[KEY uint256 pId] STORAGE { - require proposalVoteStart(pId) == value & mask_uint64(); +hook Sload uint64 value _proposals[KEY uint256 pId].voteStart._deadline STORAGE { + require proposalVoteStart(pId) == value; } -hook Sstore _proposals[KEY uint256 pId].(offset 32).(offset 0) uint64 newValue STORAGE { +hook Sstore _proposals[KEY uint256 pId].voteEnd._deadline uint64 newValue STORAGE { havoc proposalVoteEnd assuming ( - proposalVoteEnd@new(pId) == newValue & mask_uint64() + proposalVoteEnd@new(pId) == newValue && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2)) ); } -hook Sload uint64 value _proposals[KEY uint256 pId].(offset 32).(offset 0) STORAGE { - require proposalVoteEnd(pId) == value & mask_uint64(); +hook Sload uint64 value _proposals[KEY uint256 pId].voteEnd._deadline STORAGE { + require proposalVoteEnd(pId) == value; } +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// SANITY CHECKS /////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// + rule sanityCheckVoteStart(method f, uint256 pId) { uint64 preGhost = proposalVoteStart(pId); uint256 pre = proposalSnapshot(pId); @@ -76,6 +79,11 @@ rule sanityCheckVoteEnd(method f, uint256 pId) { assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; } +////////////////////////////////////////////////////////////////////////////// +////////////////////////////// INVARIANTS //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// + /** * A proposal cannot end unless it started. */ diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index 12aa1950b..c8c8e709c 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -146,12 +146,12 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) public view virtual returns (bool); + function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public /** * @dev Is the proposal successful or not. */ - function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); + function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public /** * @dev Register a vote with a given support and voting weight. From e888ea4ccbafcd2533cd47a3fd5c3bc5c608f5b9 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 11:48:55 +0200 Subject: [PATCH 011/254] Hooks fixed --- certora/specs/GovernorBase.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index b54f8c0e8..ae7d0a073 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -82,7 +82,7 @@ rule sanityCheckVoteEnd(method f, uint256 pId) { ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -// +// /** * A proposal cannot end unless it started. From 6876df00aef0a676f5cb149eca84eeba4e3ff8d5 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 11:50:40 +0200 Subject: [PATCH 012/254] slight changes change for convenience + disableLocalTypeChecking flag for the hooks --- certora/scripts/Governor.sh | 1 - certora/scripts/sanity.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index cba7edf6b..8450e7d03 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -5,4 +5,3 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --msg $1 \ --disableLocalTypeChecking \ --rule voteStartBeforeVoteEnd - diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index 0da31d15e..d157d6d42 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -8,4 +8,4 @@ do --solc solc8.0 \ --staging \ --msg "sanity ${file}" -done \ No newline at end of file +done From c00d951e06ee7bf0bd1865a580b43f91397f7b1c Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 11:51:14 +0200 Subject: [PATCH 013/254] Harness private to public --- contracts/governance/Governor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index c8c8e709c..1040f9e5b 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -146,7 +146,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public + function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public /** * @dev Is the proposal successful or not. From d6e79f4366b3629efc94723ae377e448e4087a07 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:29:04 +0200 Subject: [PATCH 014/254] Harness private to public --- contracts/governance/Governor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index 1040f9e5b..c8c8e709c 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -146,7 +146,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public + function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public /** * @dev Is the proposal successful or not. From 21b84349d4396cc1295be7fd9529aab50ae21617 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:35:11 +0200 Subject: [PATCH 015/254] slight changes in scripts + disableLocalTypeChecking --- certora/scripts/Governor.sh | 2 +- certora/scripts/sanity.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index 8450e7d03..97486c2fa 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -4,4 +4,4 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --staging \ --msg $1 \ --disableLocalTypeChecking \ - --rule voteStartBeforeVoteEnd + --rule voteStartBeforeVoteEnd \ No newline at end of file diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index d157d6d42..0da31d15e 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -8,4 +8,4 @@ do --solc solc8.0 \ --staging \ --msg "sanity ${file}" -done +done \ No newline at end of file From a2960e22b9a5357cc9d8082beda6b23505254c8f Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:35:37 +0200 Subject: [PATCH 016/254] hooks fixed --- certora/specs/GovernorBase.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index ae7d0a073..b54f8c0e8 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -82,7 +82,7 @@ rule sanityCheckVoteEnd(method f, uint256 pId) { ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -// +// /** * A proposal cannot end unless it started. From f7cc2548f38c4d763f64c479d682e52d1670482a Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:54:23 +0200 Subject: [PATCH 017/254] scripts settings added --- certora/scripts/Governor.sh | 4 +++- certora/scripts/sanity.sh | 7 ++++--- certora/specs/GovernorBase.spec | 1 - 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index 97486c2fa..8b177300d 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -4,4 +4,6 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --staging \ --msg $1 \ --disableLocalTypeChecking \ - --rule voteStartBeforeVoteEnd \ No newline at end of file + --optimistic_loop \ + --settings -copyLoopUnroll=4 + --rule sanityCheckVoteStart diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index 0da31d15e..7ffaf52f7 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -5,7 +5,8 @@ do echo ${file%.*} certoraRun certora/harnesses/$file \ --verify ${file%.*}:certora/specs/sanity.spec "$@" \ - --solc solc8.0 \ - --staging \ - --msg "sanity ${file}" + --solc solc8.0 --staging \ + --optimistic_loop \ + --msg "checking sanity on ${file%.*}" + --settings -copyLoopUnroll=4 done \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index b54f8c0e8..17a37b88e 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -32,7 +32,6 @@ hook Sload uint64 value _proposals[KEY uint256 pId].voteStart._deadline STORAGE require proposalVoteStart(pId) == value; } - hook Sstore _proposals[KEY uint256 pId].voteEnd._deadline uint64 newValue STORAGE { havoc proposalVoteEnd assuming ( proposalVoteEnd@new(pId) == newValue From bfa1dd3756ced51d00a5963f3940fe56cf0c7077 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 15:03:28 +0200 Subject: [PATCH 018/254] quotes on var in msg --- certora/scripts/Governor.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index 8b177300d..c31b43cd9 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -2,8 +2,8 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --verify GovernorHarness:certora/specs/GovernorBase.spec \ --solc solc8.0 \ --staging \ - --msg $1 \ - --disableLocalTypeChecking \ --optimistic_loop \ - --settings -copyLoopUnroll=4 - --rule sanityCheckVoteStart + --disableLocalTypeChecking \ + --settings -copyLoopUnroll=4 \ + --rule voteStartBeforeVoteEnd \ + --msg "$1" From f08ee568b9c7159b98bbd0c0f816f648f90cdbed Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Thu, 4 Nov 2021 17:54:26 +0200 Subject: [PATCH 019/254] checkingInvariantsWithoutGhosts --- certora/harnesses/GovernorHarness.sol | 8 ++++++++ certora/scripts/Governor.sh | 3 +-- certora/specs/GovernorBase.spec | 22 +++++++++++++++------- contracts/governance/Governor.sol | 2 +- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index f12a3ab4e..28e556556 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -2,6 +2,14 @@ import "../../contracts/governance/Governor.sol"; contract GovernorHarness is Governor { + function isExecuted(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].executed; + } + + function isCanceled(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].canceled; + } + mapping(uint256 => uint256) _quorum; function quorum(uint256 blockNumber) public view override virtual returns (uint256) { diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index c31b43cd9..ccdf90203 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -3,7 +3,6 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --solc solc8.0 \ --staging \ --optimistic_loop \ - --disableLocalTypeChecking \ --settings -copyLoopUnroll=4 \ - --rule voteStartBeforeVoteEnd \ + --rule noExecuteOrCancelBeforeStarting \ --msg "$1" diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 17a37b88e..cbe376bf0 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -2,25 +2,32 @@ methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree + isExecuted(uint256) returns bool envfree + isCanceled(uint256) returns bool envfree // internal functions made public in harness: _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree } +/* ghost proposalVoteStart(uint256) returns uint64 { init_state axiom forall uint256 pId. proposalVoteStart(pId) == 0; } ghost proposalVoteEnd(uint256) returns uint64 { init_state axiom forall uint256 pId. proposalVoteEnd(pId) == 0; } +*/ + +/* ghost proposalExecuted(uint256) returns bool { init_state axiom forall uint256 pId. !proposalExecuted(pId); } ghost proposalCanceled(uint256) returns bool { init_state axiom forall uint256 pId. !proposalCanceled(pId); } - +*/ +/* hook Sstore _proposals[KEY uint256 pId].voteStart._deadline uint64 newValue STORAGE { havoc proposalVoteStart assuming ( proposalVoteStart@new(pId) == newValue @@ -42,21 +49,22 @@ hook Sstore _proposals[KEY uint256 pId].voteEnd._deadline uint64 newValue STORAG hook Sload uint64 value _proposals[KEY uint256 pId].voteEnd._deadline STORAGE { require proposalVoteEnd(pId) == value; } +*/ ////////////////////////////////////////////////////////////////////////////// //////////////////////////// SANITY CHECKS /////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // - +/* rule sanityCheckVoteStart(method f, uint256 pId) { - uint64 preGhost = proposalVoteStart(pId); + uint64 preGhost = _proposals(pId).voteStart._deadline; uint256 pre = proposalSnapshot(pId); env e; calldataarg arg; f(e, arg); - uint64 postGhost = proposalVoteStart(pId); + uint64 postGhost = _proposals(pId).voteStart._deadline; uint256 post = proposalSnapshot(pId); assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; @@ -77,7 +85,7 @@ rule sanityCheckVoteEnd(method f, uint256 pId) { assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; } - +*/ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -91,12 +99,12 @@ invariant voteStartBeforeVoteEnd(uint256 pId) proposalVoteStart(pId) < proposalV /** * A proposal cannot be both executed and canceled. */ -invariant noBothExecutedAndCanceled(uint256 pId) !proposalExecuted(pId) || !proposalCanceled(pId) +invariant noBothExecutedAndCanceled(uint256 pId) !isExecuted(pId) || !isCanceled(pId) /** * A proposal cannot be executed nor canceled before it starts */ -invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.timestamp < proposalVoteStart(pId) => !proposalExecuted(pId) && !proposalCanceled(pId) +invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.timestamp < proposalVoteStart(pId) => !isExecuted(pId) && !isCanceled(pId) /** * The voting must start not before the proposal’s creation time diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index c8c8e709c..1c433f6cc 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -37,7 +37,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { string private _name; - mapping(uint256 => ProposalCore) private _proposals; + mapping(uint256 => ProposalCore) public _proposals; /** * @dev Restrict access to governor executing address. Some module might override the _executor function to make From b133fee3763a7b60d00affe2c601497c67c8b0e1 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Sun, 7 Nov 2021 17:55:03 +0200 Subject: [PATCH 020/254] WorkInProgress --- certora/specs/GovernorBase.spec | 50 +++++++++++++++++++++---------- contracts/governance/Governor.sol | 2 +- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index cbe376bf0..f70bb5154 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -1,6 +1,7 @@ // Governor.sol base definitions methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart + proposalDeadline(uint256) returns uint256 envfree hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree isExecuted(uint256) returns bool envfree isCanceled(uint256) returns bool envfree @@ -91,39 +92,60 @@ rule sanityCheckVoteEnd(method f, uint256 pId) { ////////////////////////////////////////////////////////////////////////////// // +invariant inizialized() + forall uint256 pId. proposalSnapshot(pId) != 0 && proposalDeadline(pId) != 0 + => pId != 0 + +invariant uninizialized(uint256 pId) + proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0 + /** * A proposal cannot end unless it started. */ -invariant voteStartBeforeVoteEnd(uint256 pId) proposalVoteStart(pId) < proposalVoteEnd(pId) +//invariant voteStartBeforeVoteEnd1(uint256 pId) proposalSnapshot(pId) < proposalDeadline(pId) +// ALARM +invariant voteStartBeforeVoteEnd(uint256 pId) + (proposalSnapshot(pId) == 0 <=> proposalDeadline(pId) == 0) && + proposalSnapshot(pId) < proposalDeadline(pId) /** * A proposal cannot be both executed and canceled. */ + // @AK - no violations invariant noBothExecutedAndCanceled(uint256 pId) !isExecuted(pId) || !isCanceled(pId) /** - * A proposal cannot be executed nor canceled before it starts + * A proposal cannot be neither executed nor canceled before it starts */ -invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.timestamp < proposalVoteStart(pId) => !isExecuted(pId) && !isCanceled(pId) + // @AK - violations convert to a rule +invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.number < proposalSnapshot(pId) + => !isExecuted(pId) && !isCanceled(pId) + +/** + * A proposal could be executed only if quorum was reached and vote succeeded + */ + // @AK - no violations +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) /** * The voting must start not before the proposal’s creation time */ rule noStartBeforeCreation(uint256 pId) { - uint previousStart = proposalVoteStart(pId); + uint previousStart = proposalSnapshot(pId); require previousStart == 0; env e; calldataarg arg; propose(e, arg); - uint newStart = proposalVoteStart(pId); + uint newStart = proposalSnapshot(pId); // if created, start is after creation - assert newStart != 0 => newStart > e.block.timestamp; + assert newStart != 0 => newStart >= e.block.number; } /** * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) */ + /* rule checkHashProposal { address[] t1; address[] t2; @@ -142,26 +164,24 @@ rule checkHashProposal { assert equalHashes => c1.length == c2.length; assert equalHashes => d1 == d2; } +*/ -/** - * A proposal could be executed only if quorum was reached and vote succeeded - */ -invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) proposalExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) /** * Once a proposal is created, voteStart and voteEnd are immutable */ + // @AK - no violations rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalVoteStart(pId); - uint _voteEnd = proposalVoteEnd(pId); + uint _voteStart = proposalSnapshot(pId); + uint _voteEnd = proposalDeadline(pId); require _voteStart > 0; // proposal was created env e; calldataarg arg; f(e, arg); - uint voteStart_ = proposalVoteStart(pId); - uint voteEnd_ = proposalVoteEnd(pId); + uint voteStart_ = proposalSnapshot(pId); + uint voteEnd_ = proposalDeadline(pId); assert _voteStart == voteStart_; assert _voteEnd == voteEnd_; -} \ No newline at end of file +} diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index 1c433f6cc..df85e2bbb 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -38,7 +38,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { string private _name; mapping(uint256 => ProposalCore) public _proposals; - + /** * @dev Restrict access to governor executing address. Some module might override the _executor function to make * sure this modifier is consistant with the execution model. From 0ebc0d584452c643479c0d0ad0135721adf41bfd Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 8 Nov 2021 11:44:04 +0200 Subject: [PATCH 021/254] someCleaning --- certora/specs/GovernorBase.spec | 95 +++------------------------------ 1 file changed, 7 insertions(+), 88 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index f70bb5154..b37ba6e22 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -11,99 +11,15 @@ methods { _voteSucceeded(uint256) returns bool envfree } -/* -ghost proposalVoteStart(uint256) returns uint64 { - init_state axiom forall uint256 pId. proposalVoteStart(pId) == 0; -} -ghost proposalVoteEnd(uint256) returns uint64 { - init_state axiom forall uint256 pId. proposalVoteEnd(pId) == 0; -} -*/ - -/* -ghost proposalExecuted(uint256) returns bool { - init_state axiom forall uint256 pId. !proposalExecuted(pId); -} -ghost proposalCanceled(uint256) returns bool { - init_state axiom forall uint256 pId. !proposalCanceled(pId); -} -*/ -/* -hook Sstore _proposals[KEY uint256 pId].voteStart._deadline uint64 newValue STORAGE { - havoc proposalVoteStart assuming ( - proposalVoteStart@new(pId) == newValue - && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) - ); -} - -hook Sload uint64 value _proposals[KEY uint256 pId].voteStart._deadline STORAGE { - require proposalVoteStart(pId) == value; -} - -hook Sstore _proposals[KEY uint256 pId].voteEnd._deadline uint64 newValue STORAGE { - havoc proposalVoteEnd assuming ( - proposalVoteEnd@new(pId) == newValue - && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2)) - ); -} - -hook Sload uint64 value _proposals[KEY uint256 pId].voteEnd._deadline STORAGE { - require proposalVoteEnd(pId) == value; -} -*/ - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////// SANITY CHECKS /////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -// -/* -rule sanityCheckVoteStart(method f, uint256 pId) { - uint64 preGhost = _proposals(pId).voteStart._deadline; - uint256 pre = proposalSnapshot(pId); - - env e; - calldataarg arg; - f(e, arg); - - uint64 postGhost = _proposals(pId).voteStart._deadline; - uint256 post = proposalSnapshot(pId); - - assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; - assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; -} - -rule sanityCheckVoteEnd(method f, uint256 pId) { - uint64 preGhost = proposalVoteEnd(pId); - uint256 pre = proposalSnapshot(pId); - - env e; - calldataarg arg; - f(e, arg); - - uint64 postGhost = proposalVoteEnd(pId); - uint256 post = proposalSnapshot(pId); - - assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; - assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; -} -*/ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -// -invariant inizialized() - forall uint256 pId. proposalSnapshot(pId) != 0 && proposalDeadline(pId) != 0 - => pId != 0 - -invariant uninizialized(uint256 pId) - proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0 /** * A proposal cannot end unless it started. */ //invariant voteStartBeforeVoteEnd1(uint256 pId) proposalSnapshot(pId) < proposalDeadline(pId) -// ALARM invariant voteStartBeforeVoteEnd(uint256 pId) (proposalSnapshot(pId) == 0 <=> proposalDeadline(pId) == 0) && proposalSnapshot(pId) < proposalDeadline(pId) @@ -111,22 +27,26 @@ invariant voteStartBeforeVoteEnd(uint256 pId) /** * A proposal cannot be both executed and canceled. */ - // @AK - no violations invariant noBothExecutedAndCanceled(uint256 pId) !isExecuted(pId) || !isCanceled(pId) /** * A proposal cannot be neither executed nor canceled before it starts */ - // @AK - violations convert to a rule invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.number < proposalSnapshot(pId) => !isExecuted(pId) && !isCanceled(pId) /** * A proposal could be executed only if quorum was reached and vote succeeded */ - // @AK - no violations invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) + + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// RULES //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + /** * The voting must start not before the proposal’s creation time */ @@ -170,7 +90,6 @@ rule checkHashProposal { /** * Once a proposal is created, voteStart and voteEnd are immutable */ - // @AK - no violations rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { uint _voteStart = proposalSnapshot(pId); uint _voteEnd = proposalDeadline(pId); From ac729e0ecf8006dd8b0c0a28cdf4d89965918be5 Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Mon, 8 Nov 2021 14:57:51 +0200 Subject: [PATCH 022/254] fix simple vote end before start --- certora/specs/GovernorBase.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index b37ba6e22..69d1fd85e 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -21,8 +21,8 @@ methods { */ //invariant voteStartBeforeVoteEnd1(uint256 pId) proposalSnapshot(pId) < proposalDeadline(pId) invariant voteStartBeforeVoteEnd(uint256 pId) - (proposalSnapshot(pId) == 0 <=> proposalDeadline(pId) == 0) && - proposalSnapshot(pId) < proposalDeadline(pId) + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) + && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) /** * A proposal cannot be both executed and canceled. From 7a5bd86ef4dfa34dcdcdb8392d69231b69609a2b Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 15:57:19 +0200 Subject: [PATCH 023/254] added invariants if executed or canceled always revert --- certora/specs/GovernorBase.spec | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 69d1fd85e..41ec3e735 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -40,6 +40,18 @@ invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.number < p */ invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) +/* + * No functions should be allowed to run after a job is deemed as canceled + */ +invariant cannotSetIfCanceled(uint256 pId) + isCanceled(pId) => lastReverted == true + +/* + * No functions should be allowed to run after a job is deemed as executed + */ +invariant cannotSetIfExecuted(uint256 pId) + isExecuted(pId) => lastReverted == true + ////////////////////////////////////////////////////////////////////////////// From ad7993d7d5234170f8445d982e28b5bab161cea0 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 15:57:53 +0200 Subject: [PATCH 024/254] idea for sum of votes --- certora/harnesses/GovernorHarness.sol | 54 ++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index 28e556556..27a5598e1 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -64,6 +64,19 @@ contract GovernorHarness is Governor { return _votingPeriod; } + constructor(string memory name) Governor(name) {} + + // _countVots == Sum of castVote + // + // RHS: + // 1) use counter_vote_power as a counter + // 2) use counter_vote_power as a temp var for a ghost + // + // LHS: + // mapping of count + // countMap + + mapping(uint256 => mapping(address => uint256)) counted_weight_by_id; function _countVote( uint256 proposalId, @@ -71,9 +84,46 @@ contract GovernorHarness is Governor { uint8 support, uint256 weight ) internal override virtual { - // havoc something + counted_weight_by_id[proposalId][account] += weight; } - constructor(string memory name) Governor(name) {} + mapping(uint256 => uint256) counter_vote_power_by_id; + + function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { + address voter = _msgSender(); + // 1) + counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, ""); + return _castVote(proposalId, voter, support, ""); + // 2) + // counter_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); + // return counter_vote_power; + } + + function castVoteWithReason( + uint256 proposalId, + uint8 support, + string calldata reason + ) public virtual override returns (uint256) { + address voter = _msgSender(); + counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, reason); + return _castVote(proposalId, voter, support, reason); + } + + function castVoteBySig( + uint256 proposalId, + uint8 support, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override returns (uint256) { + address voter = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))), + v, + r, + s + ); + counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, ""); + return _castVote(proposalId, voter, support, ""); + } } \ No newline at end of file From 751277a1ab20f3536f83859df12081e9a4759b60 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 8 Nov 2021 17:18:36 +0200 Subject: [PATCH 025/254] MoreRulesToTheGodOfRules --- certora/harnesses/GovernorHarness.sol | 32 +++++++++++++++------ certora/scripts/GovernorCountingSimple.sh | 8 +++++- certora/specs/GovernorBase.spec | 34 +++++++++++++++++++++++ 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index 27a5598e1..880f706d6 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -10,6 +10,15 @@ contract GovernorHarness is Governor { return _proposals[proposalId].canceled; } + + function initialized(uint256 proposalId) public view returns (bool){ + if (_proposals[proposalId].voteStart._deadline != 0 && _proposals[proposalId].voteEnd._deadline != 0) { + return true; + } + return false; + } + + mapping(uint256 => uint256) _quorum; function quorum(uint256 blockNumber) public view override virtual returns (uint256) { @@ -64,6 +73,7 @@ contract GovernorHarness is Governor { return _votingPeriod; } + constructor(string memory name) Governor(name) {} // _countVots == Sum of castVote @@ -76,28 +86,32 @@ contract GovernorHarness is Governor { // mapping of count // countMap - mapping(uint256 => mapping(address => uint256)) counted_weight_by_id; + mapping(uint256 => uint256) counted_weight; + // uint decision; + // uint numberOfOptions; function _countVote( uint256 proposalId, address account, uint8 support, uint256 weight ) internal override virtual { - counted_weight_by_id[proposalId][account] += weight; + counted_weight[proposalId] += weight; } - - mapping(uint256 => uint256) counter_vote_power_by_id; + mapping(uint256 => uint256) public counter_vote_power_by_id; + mapping(uint256 => uint256) public ghost_vote_power_by_id; function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { address voter = _msgSender(); - // 1) - counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, ""); - return _castVote(proposalId, voter, support, ""); // 2) - // counter_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); - // return counter_vote_power; + ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); + + // 1) + counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; + + // return _castVote(proposalId, voter, support, ""); + return ghost_vote_power_by_id[proposalId]; } function castVoteWithReason( diff --git a/certora/scripts/GovernorCountingSimple.sh b/certora/scripts/GovernorCountingSimple.sh index 95c2c2551..da013a4ef 100755 --- a/certora/scripts/GovernorCountingSimple.sh +++ b/certora/scripts/GovernorCountingSimple.sh @@ -1,2 +1,8 @@ certoraRun certora/harnesses/GovernorCountingSimpleHarness.sol \ - --verify GovernorCountingSimpleHarness:certora/specs/Privileged.spec \ No newline at end of file + --verify GovernorCountingSimpleHarness:certora/specs/GovernorBase.spec \ + --solc solc8.0 \ + --staging \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule doubleVoting \ + --msg "$1" diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 41ec3e735..ba8b9c93e 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -5,6 +5,11 @@ methods { hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree isExecuted(uint256) returns bool envfree isCanceled(uint256) returns bool envfree + initialized(uint256) returns bool envfree + + hasVoted(uint256, address) returns bool + + castVote(uint256, uint8) returns uint256 // internal functions made public in harness: _quorumReached(uint256) returns bool envfree @@ -23,6 +28,12 @@ methods { invariant voteStartBeforeVoteEnd(uint256 pId) (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) + /* + proposalSnapshot(pId) < proposalDeadline(pId) || (proposalSnapshot(pId) == 0 && proposalDeadline(pId) == 0) + { preserved { + require initialized(pId) == true; + }} + */ /** * A proposal cannot be both executed and canceled. @@ -116,3 +127,26 @@ rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { assert _voteStart == voteStart_; assert _voteEnd == voteEnd_; } + +/** +* Check if it's possible to vote two time. Relevant to GovernorCountingSimpleHarness.sol contract +*/ +rule doubleVoting(uint256 pId, uint8 sup) { + env e; + address user = e.msg.sender; + + bool votedCheck = hasVoted(e, pId, user); + require votedCheck == true; + + castVote@withrevert(e, pId, sup); + bool reverted = lastReverted; + + assert reverted, "double voting accured"; +} + +/** +* +*/ +rule votingSumAndPower(uint256 pId, uint8 sup, method f) { + +} From 37a4975544e34194134432ccdeeb0a0fd07adcb7 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 17:51:28 +0200 Subject: [PATCH 026/254] fixed function revert if executed --- certora/specs/GovernorBase.spec | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index ba8b9c93e..ac1dfb6b3 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -61,7 +61,13 @@ invariant cannotSetIfCanceled(uint256 pId) * No functions should be allowed to run after a job is deemed as executed */ invariant cannotSetIfExecuted(uint256 pId) - isExecuted(pId) => lastReverted == true + isExecuted(pId) => lastReverted == true + { + preserved execute(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) with (env e) + { + require(isExecuted(pId)); + } + } From c819e0b06358ca2bc5f28da7b10070dea70a46c8 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 17:57:53 +0200 Subject: [PATCH 027/254] added ghost and counter implementation for castWithReason and castBySig --- certora/harnesses/GovernorHarness.sol | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index 880f706d6..e6182ff8e 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -120,8 +120,13 @@ contract GovernorHarness is Governor { string calldata reason ) public virtual override returns (uint256) { address voter = _msgSender(); - counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, reason); - return _castVote(proposalId, voter, support, reason); + // 2) + ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, reason); + + // 1) + counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; + + return ghost_vote_power_by_id[proposalId]; } function castVoteBySig( @@ -137,7 +142,12 @@ contract GovernorHarness is Governor { r, s ); - counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, ""); - return _castVote(proposalId, voter, support, ""); + // 2) + ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); + + // 1) + counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; + + return ghost_vote_power_by_id[proposalId]; } } \ No newline at end of file From 34cb4bdc9c91778eda4ea591f5830f2162fc8ec4 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 19:00:22 +0200 Subject: [PATCH 028/254] ghosts and invariant unfinished --- certora/specs/GovernorBase.spec | 46 +++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index ac1dfb6b3..dd0df01cb 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -1,4 +1,6 @@ -// Governor.sol base definitions +////////////////////////////////////////////////////////////////////////////// +///////////////////// Governor.sol base definitions ////////////////////////// +////////////////////////////////////////////////////////////////////////////// methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart proposalDeadline(uint256) returns uint256 envfree @@ -14,8 +16,24 @@ methods { // internal functions made public in harness: _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree + + // getter for checking the sums + counter_vote_power_by_id(uint256) returns uint256 envfree + ghost_vote_power_by_id(uint256) returns uint256 envfree + counted_weight(uint256) returns uint256 envfree } +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// GHOSTS ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +ghost vote_power_ghost() returns uint256; + +hook Sstore ghost_vote_power_by_id[KEY uint256 pId] uint256 current_power STORAGE{ + havoc vote_power_ghost assuming vote_power_ghost@new() == vote_power_ghost@old() + current_power; +} + + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -38,18 +56,21 @@ invariant voteStartBeforeVoteEnd(uint256 pId) /** * A proposal cannot be both executed and canceled. */ -invariant noBothExecutedAndCanceled(uint256 pId) !isExecuted(pId) || !isCanceled(pId) +invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) /** * A proposal cannot be neither executed nor canceled before it starts */ -invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.number < proposalSnapshot(pId) - => !isExecuted(pId) && !isCanceled(pId) +invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) + e.block.number < proposalSnapshot(pId) + => !isExecuted(pId) && !isCanceled(pId) /** * A proposal could be executed only if quorum was reached and vote succeeded */ -invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) + isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) /* * No functions should be allowed to run after a job is deemed as canceled @@ -69,7 +90,13 @@ invariant cannotSetIfExecuted(uint256 pId) } } - +/* + * sum of all votes casted is equal to the sum of voting power of those who voted + */ +invariant SumOfVotesCastEqualSumOfPowerOfVoted(uint256 pId) + counted_weight(pId) == counter_vote_power_by_id(pId) && + counted_weight(pId) == vote_power_ghost && + counter_vote_power_by_id(pId) == vote_power_ghost ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////// RULES //////////////////////////////////// @@ -149,10 +176,3 @@ rule doubleVoting(uint256 pId, uint8 sup) { assert reverted, "double voting accured"; } - -/** -* -*/ -rule votingSumAndPower(uint256 pId, uint8 sup, method f) { - -} From 44a8fed4105595040a3a29a4785f9d8add357b59 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 20:16:53 +0200 Subject: [PATCH 029/254] cannot set if executed and canceled as rules (not working) --- certora/specs/GovernorBase.spec | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index dd0df01cb..aa0bcfc5b 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -75,28 +75,28 @@ invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) /* * No functions should be allowed to run after a job is deemed as canceled */ -invariant cannotSetIfCanceled(uint256 pId) - isCanceled(pId) => lastReverted == true +rule cannotSetIfCanceled(uint256 pId, method f) filtered { f-> !f.isView }{ + require(isCanceled(pId)); + env e; calldataarg args; + f(e, args); + assert(isCanceled(pId) => lastReverted == true, "Function did not revert when canceled"); +} /* * No functions should be allowed to run after a job is deemed as executed */ -invariant cannotSetIfExecuted(uint256 pId) - isExecuted(pId) => lastReverted == true - { - preserved execute(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) with (env e) - { - require(isExecuted(pId)); - } - } +rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{ + require(isExecuted(pId)); + env e; calldataarg args; + f(e, args); + assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed"); +} /* * sum of all votes casted is equal to the sum of voting power of those who voted */ invariant SumOfVotesCastEqualSumOfPowerOfVoted(uint256 pId) - counted_weight(pId) == counter_vote_power_by_id(pId) && - counted_weight(pId) == vote_power_ghost && - counter_vote_power_by_id(pId) == vote_power_ghost + counted_weight(pId) == vote_power_ghost() ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////// RULES //////////////////////////////////// From 07d637980c25b280da159491b10f1856024981ea Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 9 Nov 2021 11:18:23 +0200 Subject: [PATCH 030/254] FirstWizardHarness --- certora/harnesses/GovernorBasicHarness.sol | 124 +++++++++++++++++++++ certora/scripts/Governor.sh | 2 +- certora/scripts/GovernorBasic.sh | 8 ++ certora/specs/GovernorBase.spec | 7 +- 4 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 certora/harnesses/GovernorBasicHarness.sol create mode 100644 certora/scripts/GovernorBasic.sh diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol new file mode 100644 index 000000000..1b9dce2a6 --- /dev/null +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../contracts/governance/Governor.sol"; +import "../../contracts/governance/extensions/GovernorCountingSimple.sol"; +import "../../contracts/governance/extensions/GovernorVotes.sol"; +import "../../contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../contracts/governance/extensions/GovernorTimelockCompound.sol"; + +/* +Wizard options: +ERC20Votes +TimelockCompound +*/ + +contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockCompound { + constructor(ERC20Votes _token, ICompoundTimelock _timelock, string memory name, uint256 quorumFraction) + Governor(name) + GovernorVotes(_token) + GovernorVotesQuorumFraction(quorumFraction) + GovernorTimelockCompound(_timelock) + {} + + function isExecuted(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].executed; + } + + function isCanceled(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].canceled; + } + + uint256 _votingDelay; + + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + +/* + function votingDelay() public pure override returns (uint256) { + return _votingDelay; + } + + + function votingPeriod() public pure override returns (uint256) { + return _votingPeriod; + } +*/ + + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function getVotes(address account, uint256 blockNumber) + public + view + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function state(uint256 proposalId) + public + view + override(Governor, GovernorTimelockCompound) + returns (ProposalState) + { + return super.state(proposalId); + } + + function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) + public + override(Governor, IGovernor) + returns (uint256) + { + return super.propose(targets, values, calldatas, description); + } + + function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockCompound) + { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockCompound) + returns (uint256) + { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() + internal + view + override(Governor, GovernorTimelockCompound) + returns (address) + { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, GovernorTimelockCompound) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index ccdf90203..d3b7f9b3f 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -4,5 +4,5 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --staging \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule noExecuteOrCancelBeforeStarting \ + --rule voteStartBeforeVoteEnd \ --msg "$1" diff --git a/certora/scripts/GovernorBasic.sh b/certora/scripts/GovernorBasic.sh new file mode 100644 index 000000000..b720c28ec --- /dev/null +++ b/certora/scripts/GovernorBasic.sh @@ -0,0 +1,8 @@ +certoraRun certora/harnesses/GovernorBasicHarness.sol \ + --verify GovernorBasicHarness:certora/specs/GovernorBase.spec \ + --solc solc8.2 \ + --staging \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule doubleVoting \ + --msg "$1" \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index aa0bcfc5b..b0952a534 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -7,7 +7,7 @@ methods { hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree isExecuted(uint256) returns bool envfree isCanceled(uint256) returns bool envfree - initialized(uint256) returns bool envfree + // initialized(uint256) returns bool envfree hasVoted(uint256, address) returns bool @@ -176,3 +176,8 @@ rule doubleVoting(uint256 pId, uint8 sup) { assert reverted, "double voting accured"; } + +/** +* +*/ +//rule votingSumAndPower(uint256 pId, uint8 sup, method f) {} From 96df9799c3275fd0abf65b7c312c81f350eb7925 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 9 Nov 2021 15:10:07 +0200 Subject: [PATCH 031/254] specificSpecForSumRule --- certora/harnesses/GovernorBasicHarness.sol | 19 ++-- .../GovernorCountingSimple-counting.sh | 8 ++ certora/specs/GovernorBase.spec | 24 +---- certora/specs/GovernorCountingSimple.spec | 87 +++++++++++++++++++ contracts/governance/Governor.sol | 4 +- .../GovernorCompatibilityBravo.sol | 4 +- .../extensions/GovernorCountingSimple.sol | 2 +- 7 files changed, 114 insertions(+), 34 deletions(-) create mode 100644 certora/scripts/GovernorCountingSimple-counting.sh create mode 100644 certora/specs/GovernorCountingSimple.spec diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol index 1b9dce2a6..57cd3c58c 100644 --- a/certora/harnesses/GovernorBasicHarness.sol +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -41,16 +41,21 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes return _votingPeriod; } -/* - function votingDelay() public pure override returns (uint256) { - return _votingDelay; - } + mapping(uint256 => uint256) public ghost_sum_vote_power_by_id; - function votingPeriod() public pure override returns (uint256) { - return _votingPeriod; + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason + ) internal override virtual returns (uint256) { + + uint deltaWeight = super._castVote(proposalId, account, support, reason); //HARNESS + ghost_sum_vote_power_by_id[proposalId] += deltaWeight; + + return deltaWeight; } -*/ // The following functions are overrides required by Solidity. diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/GovernorCountingSimple-counting.sh new file mode 100644 index 000000000..2bea57198 --- /dev/null +++ b/certora/scripts/GovernorCountingSimple-counting.sh @@ -0,0 +1,8 @@ +certoraRun certora/harnesses/GovernorBasicHarness.sol \ + --verify GovernorBasicHarness:certora/specs/GovernorCountingSimple.spec \ + --solc solc8.2 \ + --staging \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule SumOfVotesCastEqualSumOfPowerOfVoted \ + --msg "$1" \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index b0952a534..dad38fc48 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -19,20 +19,10 @@ methods { // getter for checking the sums counter_vote_power_by_id(uint256) returns uint256 envfree - ghost_vote_power_by_id(uint256) returns uint256 envfree + ghost_sum_vote_power_by_id(uint256) returns uint256 envfree counted_weight(uint256) returns uint256 envfree } -////////////////////////////////////////////////////////////////////////////// -///////////////////////////////// GHOSTS ///////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -ghost vote_power_ghost() returns uint256; - -hook Sstore ghost_vote_power_by_id[KEY uint256 pId] uint256 current_power STORAGE{ - havoc vote_power_ghost assuming vote_power_ghost@new() == vote_power_ghost@old() + current_power; -} - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// @@ -92,11 +82,6 @@ rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{ assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed"); } -/* - * sum of all votes casted is equal to the sum of voting power of those who voted - */ -invariant SumOfVotesCastEqualSumOfPowerOfVoted(uint256 pId) - counted_weight(pId) == vote_power_ghost() ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////// RULES //////////////////////////////////// @@ -174,10 +159,5 @@ rule doubleVoting(uint256 pId, uint8 sup) { castVote@withrevert(e, pId, sup); bool reverted = lastReverted; - assert reverted, "double voting accured"; + assert votedCheck => reverted, "double voting accured"; } - -/** -* -*/ -//rule votingSumAndPower(uint256 pId, uint8 sup, method f) {} diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec new file mode 100644 index 000000000..197dae1c0 --- /dev/null +++ b/certora/specs/GovernorCountingSimple.spec @@ -0,0 +1,87 @@ +////////////////////////////////////////////////////////////////////////////// +///////////////////// Governor.sol base definitions ////////////////////////// +////////////////////////////////////////////////////////////////////////////// +methods { + proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart + proposalDeadline(uint256) returns uint256 envfree + hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree + isExecuted(uint256) returns bool envfree + isCanceled(uint256) returns bool envfree + // initialized(uint256) returns bool envfree + + hasVoted(uint256, address) returns bool + + castVote(uint256, uint8) returns uint256 + + // internal functions made public in harness: + _quorumReached(uint256) returns bool envfre + _voteSucceeded(uint256) returns bool envfree + + // getter for checking the sums + ghost_sum_vote_power_by_id(uint256) returns uint256 envfree +} + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// GHOSTS ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +ghost sum_all_votes_power() returns uint256 { + init_state axiom sum_all_votes_power() == 0; +} + +hook Sstore ghost_sum_vote_power_by_id[KEY uint256 pId] uint256 current_power (uint256 old_power) STORAGE{ + havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; +} + +ghost tracked_weight(uint256) returns uint256 { + init_state axiom forall uint256 p. tracked_weight(p) == 0; +} +ghost sum_tracked_weight() returns uint256 { + init_state axiom sum_tracked_weight() == 0; +} + +/* +function update_tracked_weights(uint256 pId, uint256 votes, uint256 old_votes) { + havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +}*/ + +hook Sstore _proposalVotes[KEY uint256 pId].againstVotes uint256 votes (uint256 old_votes) STORAGE { + //update_tracked_weights(pId, votes, old_votes); + havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +} + +hook Sstore _proposalVotes[KEY uint256 pId].forVotes uint256 votes (uint256 old_votes) STORAGE { + //update_tracked_weights(pId, votes, old_votes); + havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +} + +hook Sstore _proposalVotes[KEY uint256 pId].abstainVotes uint256 votes (uint256 old_votes) STORAGE { + //update_tracked_weights(pId, votes, old_votes); + havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////// INVARIANTS //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +/* + * sum of all votes casted is equal to the sum of voting power of those who voted, per each proposal + */ +invariant SumOfVotesCastEqualSumOfPowerOfVotedPerProposal(uint256 pId) + tracked_weight(pId) == ghost_sum_vote_power_by_id(pId) + +/* + * sum of all votes casted is equal to the sum of voting power of those who voted + */ +invariant SumOfVotesCastEqualSumOfPowerOfVoted() + sum_tracked_weight() == sum_all_votes_power() + diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index df85e2bbb..3aecd3a1c 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -146,12 +146,12 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public + function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal /** * @dev Is the proposal successful or not. */ - function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public + function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal /** * @dev Register a vote with a given support and voting weight. diff --git a/contracts/governance/compatibility/GovernorCompatibilityBravo.sol b/contracts/governance/compatibility/GovernorCompatibilityBravo.sol index cbdd72791..e0261ea38 100644 --- a/contracts/governance/compatibility/GovernorCompatibilityBravo.sol +++ b/contracts/governance/compatibility/GovernorCompatibilityBravo.sol @@ -260,7 +260,7 @@ abstract contract GovernorCompatibilityBravo is /** * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. */ - function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { + function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal ProposalDetails storage details = _proposalDetails[proposalId]; return quorum(proposalSnapshot(proposalId)) < details.forVotes; } @@ -268,7 +268,7 @@ abstract contract GovernorCompatibilityBravo is /** * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be scritly over the againstVotes. */ - function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { + function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal ProposalDetails storage details = _proposalDetails[proposalId]; return details.forVotes > details.againstVotes; } diff --git a/contracts/governance/extensions/GovernorCountingSimple.sol b/contracts/governance/extensions/GovernorCountingSimple.sol index 00f4d4aa8..fae6b5c5b 100644 --- a/contracts/governance/extensions/GovernorCountingSimple.sol +++ b/contracts/governance/extensions/GovernorCountingSimple.sol @@ -97,7 +97,7 @@ abstract contract GovernorCountingSimple is Governor { } else if (support == uint8(VoteType.For)) { proposalvote.forVotes += weight; } else if (support == uint8(VoteType.Abstain)) { - proposalvote.abstainVotes += weight; + // proposalvote.abstainVotes += weight; } else { revert("GovernorVotingSimple: invalid value for enum VoteType"); } From 6ac85d8d150f34de1a6d13598939811ca4aa6158 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 9 Nov 2021 15:16:44 +0200 Subject: [PATCH 032/254] RemovedInsertedBugForSumRule --- certora/specs/GovernorBase.spec | 2 +- contracts/governance/extensions/GovernorCountingSimple.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index dad38fc48..5b24bae2c 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -147,7 +147,7 @@ rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { } /** -* Check if it's possible to vote two time. Relevant to GovernorCountingSimpleHarness.sol contract +* A user cannot vote twice */ rule doubleVoting(uint256 pId, uint8 sup) { env e; diff --git a/contracts/governance/extensions/GovernorCountingSimple.sol b/contracts/governance/extensions/GovernorCountingSimple.sol index fae6b5c5b..00f4d4aa8 100644 --- a/contracts/governance/extensions/GovernorCountingSimple.sol +++ b/contracts/governance/extensions/GovernorCountingSimple.sol @@ -97,7 +97,7 @@ abstract contract GovernorCountingSimple is Governor { } else if (support == uint8(VoteType.For)) { proposalvote.forVotes += weight; } else if (support == uint8(VoteType.Abstain)) { - // proposalvote.abstainVotes += weight; + proposalvote.abstainVotes += weight; } else { revert("GovernorVotingSimple: invalid value for enum VoteType"); } From 85855b8cc7af95fa6c532a9787bf2b4315863e67 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Tue, 9 Nov 2021 21:09:54 +0200 Subject: [PATCH 033/254] FixedTypoInEnvfreeWord --- certora/specs/GovernorCountingSimple.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 197dae1c0..d3094896f 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -14,7 +14,7 @@ methods { castVote(uint256, uint8) returns uint256 // internal functions made public in harness: - _quorumReached(uint256) returns bool envfre + _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree // getter for checking the sums From 52924aaec0792d081dd4dcc854949f82f20fb65e Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Wed, 10 Nov 2021 15:07:34 +0200 Subject: [PATCH 034/254] Changed deltaWeight type from uint to uin256 --- certora/harnesses/GovernorBasicHarness.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol index 57cd3c58c..6a36475d4 100644 --- a/certora/harnesses/GovernorBasicHarness.sol +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -51,7 +51,7 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes string memory reason ) internal override virtual returns (uint256) { - uint deltaWeight = super._castVote(proposalId, account, support, reason); //HARNESS + uint256 deltaWeight = super._castVote(proposalId, account, support, reason); //HARNESS ghost_sum_vote_power_by_id[proposalId] += deltaWeight; return deltaWeight; From eb87bb482238aa30e04a77e3747de9c094b0fc08 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Wed, 10 Nov 2021 15:10:37 +0200 Subject: [PATCH 035/254] aesthetic --- certora/specs/GovernorBase.spec | 232 +++++++++++++++----------------- 1 file changed, 108 insertions(+), 124 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 5b24bae2c..9faa14f9f 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -7,6 +7,7 @@ methods { hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree isExecuted(uint256) returns bool envfree isCanceled(uint256) returns bool envfree + execute(address[], uint256[], bytes[], bytes32) returns uint256 // initialized(uint256) returns bool envfree hasVoted(uint256, address) returns bool @@ -17,147 +18,130 @@ methods { _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree - // getter for checking the sums - counter_vote_power_by_id(uint256) returns uint256 envfree - ghost_sum_vote_power_by_id(uint256) returns uint256 envfree - counted_weight(uint256) returns uint256 envfree } - - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -/** - * A proposal cannot end unless it started. - */ -//invariant voteStartBeforeVoteEnd1(uint256 pId) proposalSnapshot(pId) < proposalDeadline(pId) -invariant voteStartBeforeVoteEnd(uint256 pId) + /* + * A proposal cannot end unless it started. + */ + invariant voteStartBeforeVoteEnd(uint256 pId) (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) - /* - proposalSnapshot(pId) < proposalDeadline(pId) || (proposalSnapshot(pId) == 0 && proposalDeadline(pId) == 0) - { preserved { - require initialized(pId) == true; - }} - */ - -/** - * A proposal cannot be both executed and canceled. - */ -invariant noBothExecutedAndCanceled(uint256 pId) - !isExecuted(pId) || !isCanceled(pId) - -/** - * A proposal cannot be neither executed nor canceled before it starts - */ -invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) - e.block.number < proposalSnapshot(pId) - => !isExecuted(pId) && !isCanceled(pId) - -/** - * A proposal could be executed only if quorum was reached and vote succeeded - */ -invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) - isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) - -/* - * No functions should be allowed to run after a job is deemed as canceled - */ -rule cannotSetIfCanceled(uint256 pId, method f) filtered { f-> !f.isView }{ - require(isCanceled(pId)); - env e; calldataarg args; - f(e, args); - assert(isCanceled(pId) => lastReverted == true, "Function did not revert when canceled"); -} - -/* - * No functions should be allowed to run after a job is deemed as executed - */ -rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{ - require(isExecuted(pId)); - env e; calldataarg args; - f(e, args); - assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed"); -} -////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////// RULES //////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// + /* + * A proposal cannot be both executed and canceled. + */ + invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) -/** - * The voting must start not before the proposal’s creation time - */ -rule noStartBeforeCreation(uint256 pId) { - uint previousStart = proposalSnapshot(pId); - require previousStart == 0; - env e; - calldataarg arg; - propose(e, arg); - - uint newStart = proposalSnapshot(pId); - // if created, start is after creation - assert newStart != 0 => newStart >= e.block.number; -} - -/** - * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) - */ - /* -rule checkHashProposal { - address[] t1; - address[] t2; - uint256[] v1; - uint256[] v2; - bytes[] c1; - bytes[] c2; - bytes32 d1; - bytes32 d2; - - uint256 h1 = hashProposal(t1,v1,c1,d1); - uint256 h2 = hashProposal(t2,v2,c2,d2); - bool equalHashes = h1 == h2; - assert equalHashes => t1.length == t2.length; - assert equalHashes => v1.length == v2.length; - assert equalHashes => c1.length == c2.length; - assert equalHashes => d1 == d2; -} -*/ + /* + * A proposal cannot be neither executed nor canceled before it starts + */ + invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) + e.block.number < proposalSnapshot(pId) + => !isExecuted(pId) && !isCanceled(pId) -/** - * Once a proposal is created, voteStart and voteEnd are immutable - */ -rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalSnapshot(pId); - uint _voteEnd = proposalDeadline(pId); - require _voteStart > 0; // proposal was created + /* + * A proposal could be executed only if quorum was reached and vote succeeded + */ + invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) + isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) - env e; - calldataarg arg; - f(e, arg); - uint voteStart_ = proposalSnapshot(pId); - uint voteEnd_ = proposalDeadline(pId); - assert _voteStart == voteStart_; - assert _voteEnd == voteEnd_; -} + ////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////// RULES //////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + /* + * The voting must start not before the proposal’s creation time + */ + rule noStartBeforeCreation(uint256 pId) { + uint previousStart = proposalSnapshot(pId); + require previousStart == 0; + env e; + calldataarg arg; + propose(e, arg); -/** -* A user cannot vote twice -*/ -rule doubleVoting(uint256 pId, uint8 sup) { - env e; - address user = e.msg.sender; + uint newStart = proposalSnapshot(pId); + // if created, start is after creation + assert(newStart != 0 => newStart >= e.block.number); + } - bool votedCheck = hasVoted(e, pId, user); - require votedCheck == true; - castVote@withrevert(e, pId, sup); - bool reverted = lastReverted; + /* + * Once a proposal is created, voteStart and voteEnd are immutable + */ + rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint _voteStart = proposalSnapshot(pId); + uint _voteEnd = proposalDeadline(pId); + require _voteStart > 0; // proposal was created - assert votedCheck => reverted, "double voting accured"; -} + env e; + calldataarg arg; + f(e, arg); + + uint voteStart_ = proposalSnapshot(pId); + uint voteEnd_ = proposalDeadline(pId); + assert _voteStart == voteStart_; + assert _voteEnd == voteEnd_; + } + + + /* + * A user cannot vote twice + */ + rule doubleVoting(uint256 pId, uint8 sup) { + env e; + address user = e.msg.sender; + + bool votedCheck = hasVoted(e, pId, user); + require votedCheck == true; + + castVote@withrevert(e, pId, sup); + bool reverted = lastReverted; + + assert votedCheck => reverted, "double voting accured"; + } + + + /* + * When a proposal is created the start and end date are created in the future. + */ + rule proposalCreatedStartAndEndDateInFuture(address[] targets, uint256[] values, bytes[] calldatas){ + env e; + uint256 pId = callPropose(e, targets, values, calldatas); + uint256 startDate = proposalSnapshot(pId); + uint256 endDate = proposalDeadline(pId); + assert(startDate >= e.block.number && endDate >= e.block.number, "Start or end date were set in the past"); + } + + + /** + * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) + */ + /* + rule checkHashProposal { + address[] t1; + address[] t2; + uint256[] v1; + uint256[] v2; + bytes[] c1; + bytes[] c2; + bytes32 d1; + bytes32 d2; + + uint256 h1 = hashProposal(t1,v1,c1,d1); + uint256 h2 = hashProposal(t2,v2,c2,d2); + bool equalHashes = h1 == h2; + assert equalHashes => t1.length == t2.length; + assert equalHashes => v1.length == v2.length; + assert equalHashes => c1.length == c2.length; + assert equalHashes => d1 == d2; + } + */ From b52832ca7f2442044da2faedf465e97ba6255a46 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Wed, 10 Nov 2021 15:26:29 +0200 Subject: [PATCH 036/254] Cleaned harness + callPropose --- certora/harnesses/GovernorHarness.sol | 74 +++++---------------------- 1 file changed, 14 insertions(+), 60 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index e6182ff8e..fce0c5c18 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -74,6 +74,16 @@ contract GovernorHarness is Governor { } + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name) Governor(name) {} // _countVots == Sum of castVote @@ -86,68 +96,12 @@ contract GovernorHarness is Governor { // mapping of count // countMap - mapping(uint256 => uint256) counted_weight; - // uint decision; // uint numberOfOptions; - function _countVote( - uint256 proposalId, - address account, - uint8 support, - uint256 weight - ) internal override virtual { - counted_weight[proposalId] += weight; - } - mapping(uint256 => uint256) public counter_vote_power_by_id; - mapping(uint256 => uint256) public ghost_vote_power_by_id; - - function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { - address voter = _msgSender(); - // 2) - ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); - - // 1) - counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; - - // return _castVote(proposalId, voter, support, ""); - return ghost_vote_power_by_id[proposalId]; - } - - function castVoteWithReason( - uint256 proposalId, - uint8 support, - string calldata reason - ) public virtual override returns (uint256) { - address voter = _msgSender(); - // 2) - ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, reason); - - // 1) - counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; - - return ghost_vote_power_by_id[proposalId]; - } - - function castVoteBySig( - uint256 proposalId, - uint8 support, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual override returns (uint256) { - address voter = ECDSA.recover( - _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))), - v, - r, - s - ); - // 2) - ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); - - // 1) - counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; - - return ghost_vote_power_by_id[proposalId]; + function callPropose(address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas) public virtual returns (uint256) { + return super.propose(targets, values, calldatas, ""); } } \ No newline at end of file From 2a0532daccc5e48172d63aa8ce733620d0dae219 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Thu, 11 Nov 2021 17:28:22 +0200 Subject: [PATCH 037/254] CountingSimpleMoreCleanAndAddedMoreRules --- certora/specs/GovernorBase.spec | 253 +++++++++++++--------- certora/specs/GovernorCountingSimple.spec | 31 +-- 2 files changed, 151 insertions(+), 133 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 9faa14f9f..ba4565ad0 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -1,6 +1,7 @@ ////////////////////////////////////////////////////////////////////////////// ///////////////////// Governor.sol base definitions ////////////////////////// ////////////////////////////////////////////////////////////////////////////// + methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart proposalDeadline(uint256) returns uint256 envfree @@ -17,131 +18,175 @@ methods { // internal functions made public in harness: _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree - } + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// - /* - * A proposal cannot end unless it started. - */ - invariant voteStartBeforeVoteEnd(uint256 pId) - (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) - && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) +/* + * A proposal cannot end unless it started. + */ +invariant voteStartBeforeVoteEnd(uint256 pId) + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) + && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) - /* - * A proposal cannot be both executed and canceled. - */ - invariant noBothExecutedAndCanceled(uint256 pId) - !isExecuted(pId) || !isCanceled(pId) +/** + * A proposal cannot be both executed and canceled. + */ +invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) - /* - * A proposal cannot be neither executed nor canceled before it starts - */ - invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) - e.block.number < proposalSnapshot(pId) - => !isExecuted(pId) && !isCanceled(pId) +/** + * A proposal could be executed only if quorum was reached and vote succeeded + */ +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) + isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) - /* - * A proposal could be executed only if quorum was reached and vote succeeded - */ - invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) - isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) - - - ////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////// RULES //////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// RULES //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// - /* - * The voting must start not before the proposal’s creation time - */ - rule noStartBeforeCreation(uint256 pId) { - uint previousStart = proposalSnapshot(pId); - require previousStart == 0; - env e; - calldataarg arg; - propose(e, arg); - uint newStart = proposalSnapshot(pId); - // if created, start is after creation - assert(newStart != 0 => newStart >= e.block.number); - } +/* + * No functions should be allowed to run after a job is deemed as canceled + */ +rule cannotSetIfCanceled(uint256 pId, method f) filtered { f-> !f.isView }{ + require(isCanceled(pId)); + env e; calldataarg args; + f(e, args); + assert(isCanceled(pId) => lastReverted == true, "Function did not revert when canceled"); +} - /* - * Once a proposal is created, voteStart and voteEnd are immutable - */ - rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalSnapshot(pId); - uint _voteEnd = proposalDeadline(pId); - require _voteStart > 0; // proposal was created - - env e; - calldataarg arg; - f(e, arg); - - uint voteStart_ = proposalSnapshot(pId); - uint voteEnd_ = proposalDeadline(pId); - assert _voteStart == voteStart_; - assert _voteEnd == voteEnd_; - } +/* + * No functions should be allowed to run after a job is deemed as executed + */ +rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{ + require(isExecuted(pId)); + env e; calldataarg args; + f(e, args); + assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed"); +} - /* - * A user cannot vote twice - */ - rule doubleVoting(uint256 pId, uint8 sup) { - env e; - address user = e.msg.sender; +/* + * The voting must start not before the proposal’s creation time + */ +rule noStartBeforeCreation(uint256 pId) { + uint previousStart = proposalSnapshot(pId); + require previousStart == 0; + env e; + calldataarg arg; + propose(e, arg); - bool votedCheck = hasVoted(e, pId, user); - require votedCheck == true; - - castVote@withrevert(e, pId, sup); - bool reverted = lastReverted; - - assert votedCheck => reverted, "double voting accured"; - } + uint newStart = proposalSnapshot(pId); + // if created, start is after creation + assert(newStart != 0 => newStart >= e.block.number); +} - /* - * When a proposal is created the start and end date are created in the future. - */ - rule proposalCreatedStartAndEndDateInFuture(address[] targets, uint256[] values, bytes[] calldatas){ - env e; - uint256 pId = callPropose(e, targets, values, calldatas); - uint256 startDate = proposalSnapshot(pId); - uint256 endDate = proposalDeadline(pId); - assert(startDate >= e.block.number && endDate >= e.block.number, "Start or end date were set in the past"); - } +/* + * Once a proposal is created, voteStart and voteEnd are immutable + */ +rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint _voteStart = proposalSnapshot(pId); + uint _voteEnd = proposalDeadline(pId); + require _voteStart > 0; // proposal was created + + env e; + calldataarg arg; + f(e, arg); + + uint voteStart_ = proposalSnapshot(pId); + uint voteEnd_ = proposalDeadline(pId); + assert _voteStart == voteStart_; + assert _voteEnd == voteEnd_; +} - /** - * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) - */ - /* - rule checkHashProposal { - address[] t1; - address[] t2; - uint256[] v1; - uint256[] v2; - bytes[] c1; - bytes[] c2; - bytes32 d1; - bytes32 d2; +/* + * A user cannot vote twice + */ +rule doubleVoting(uint256 pId, uint8 sup) { + env e; + address user = e.msg.sender; - uint256 h1 = hashProposal(t1,v1,c1,d1); - uint256 h2 = hashProposal(t2,v2,c2,d2); - bool equalHashes = h1 == h2; - assert equalHashes => t1.length == t2.length; - assert equalHashes => v1.length == v2.length; - assert equalHashes => c1.length == c2.length; - assert equalHashes => d1 == d2; - } - */ + bool votedCheck = hasVoted(e, pId, user); + + castVote@withrevert(e, pId, sup); + bool reverted = lastReverted; + + assert votedCheck => reverted, "double voting accured"; +} + + +/* + * When a proposal is created the start and end date are created in the future. + */ +rule proposalCreatedStartAndEndDateInFuture(address[] targets, uint256[] values, bytes[] calldatas){ + env e; + uint256 pId = callPropose(e, targets, values, calldatas); + uint256 startDate = proposalSnapshot(pId); + uint256 endDate = proposalDeadline(pId); + assert(startDate >= e.block.number && endDate >= e.block.number, "Start or end date were set in the past"); +} + + +/** + * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) + */ +/* +rule checkHashProposal { + address[] t1; + address[] t2; + uint256[] v1; + uint256[] v2; + bytes[] c1; + bytes[] c2; + bytes32 d1; + bytes32 d2; + + uint256 h1 = hashProposal(t1,v1,c1,d1); + uint256 h2 = hashProposal(t2,v2,c2,d2); + bool equalHashes = h1 == h2; + assert equalHashes => t1.length == t2.length; + assert equalHashes => v1.length == v2.length; + assert equalHashes => c1.length == c2.length; + assert equalHashes => d1 == d2; +} +*/ + + +/** + * A proposal cannot be neither executed nor canceled before it starts + */ +rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ + env e; + + require !isExecuted(pId) && !isCanceled(pId); + + calldataarg arg; + f(e, arg); + + assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; +} + +/** + * A proposal cannot be neither executed nor canceled before proposal's deadline + */ +rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ + env e; + + requireInvariant voteStartBeforeVoteEnd(pId); + require !isExecuted(pId) && !isCanceled(pId); + + calldataarg arg; + f(e, arg); + + assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; +} \ No newline at end of file diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index d3094896f..a0ae7b6d6 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -1,23 +1,6 @@ -////////////////////////////////////////////////////////////////////////////// -///////////////////// Governor.sol base definitions ////////////////////////// -////////////////////////////////////////////////////////////////////////////// +import "GovernorBase.spec" + methods { - proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart - proposalDeadline(uint256) returns uint256 envfree - hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree - isExecuted(uint256) returns bool envfree - isCanceled(uint256) returns bool envfree - // initialized(uint256) returns bool envfree - - hasVoted(uint256, address) returns bool - - castVote(uint256, uint8) returns uint256 - - // internal functions made public in harness: - _quorumReached(uint256) returns bool envfree - _voteSucceeded(uint256) returns bool envfree - - // getter for checking the sums ghost_sum_vote_power_by_id(uint256) returns uint256 envfree } @@ -40,29 +23,19 @@ ghost sum_tracked_weight() returns uint256 { init_state axiom sum_tracked_weight() == 0; } -/* -function update_tracked_weights(uint256 pId, uint256 votes, uint256 old_votes) { - havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; -}*/ - hook Sstore _proposalVotes[KEY uint256 pId].againstVotes uint256 votes (uint256 old_votes) STORAGE { - //update_tracked_weights(pId, votes, old_votes); havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; } hook Sstore _proposalVotes[KEY uint256 pId].forVotes uint256 votes (uint256 old_votes) STORAGE { - //update_tracked_weights(pId, votes, old_votes); havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; } hook Sstore _proposalVotes[KEY uint256 pId].abstainVotes uint256 votes (uint256 old_votes) STORAGE { - //update_tracked_weights(pId, votes, old_votes); havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; From 2ecba5326bc1f8ec3353fd2dc1284193448a66b5 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Fri, 12 Nov 2021 09:02:51 +0200 Subject: [PATCH 038/254] reorganization + violated rules --- certora/specs/GovernorBase.spec | 281 ++++++++++++++++++-------------- 1 file changed, 162 insertions(+), 119 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index ba4565ad0..4cea34323 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -18,124 +18,169 @@ methods { // internal functions made public in harness: _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree + } -////////////////////////////////////////////////////////////////////////////// -////////////////////////////// INVARIANTS //////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - - /* - * A proposal cannot end unless it started. - */ -invariant voteStartBeforeVoteEnd(uint256 pId) - (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) - && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////// State Diagram ////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // // + // castVote(s)() // + // ------------- propose() ---------------------- time pass --------------- time passes ----------- // + // | No Proposal | --------> | Before Start (Delay) | --------> | Voting Period | ----------------------> | execute() | // + // ------------- ---------------------- --------------- -> Executed/Canceled ----------- // + // ------------------------------------------------------------|---------------|-------------------------|--------------> // + // t start end timelock // + // // + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +*/ -/** - * A proposal cannot be both executed and canceled. - */ -invariant noBothExecutedAndCanceled(uint256 pId) - !isExecuted(pId) || !isCanceled(pId) + +/////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// Global Valid States ///////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + + // not complete + /* + * If any of the properties are non zero, the rest has to be non zero + */ + // start !0 !0 !0 + // end = !0 !0 !0 + // exe = 0 0 1 1 + // can = 0 1 0 1 + invariant proposalInitiated(uint256 pId) + (proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0) && + (isCanceled(pId) => proposalSnapshot(pId) != 0) && + (isExecuted(pId) => proposalSnapshot(pId) != 0) + + // pass + /* + * A proposal cannot end unless it started. + */ + invariant voteStartBeforeVoteEnd(uint256 pId) + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) + && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) -/** - * A proposal could be executed only if quorum was reached and vote succeeded - */ -invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) - isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) + // pass + /* + * A proposal cannot be both executed and canceled. + */ + invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) -////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////// RULES //////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/* - * No functions should be allowed to run after a job is deemed as canceled - */ -rule cannotSetIfCanceled(uint256 pId, method f) filtered { f-> !f.isView }{ - require(isCanceled(pId)); - env e; calldataarg args; - f(e, args); - assert(isCanceled(pId) => lastReverted == true, "Function did not revert when canceled"); -} +/////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// In-State Rules ///////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +//========================================== +//------------- Voting Period -------------- +//========================================== + + // pass + /* + * A user cannot vote twice + */ + rule doubleVoting(uint256 pId, uint8 sup) { + env e; + address user = e.msg.sender; + + bool votedCheck = hasVoted(e, pId, user); + require votedCheck == true; + + castVote@withrevert(e, pId, sup); + bool reverted = lastReverted; + + assert votedCheck => reverted, "double voting accured"; + } -/* - * No functions should be allowed to run after a job is deemed as executed - */ -rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{ - require(isExecuted(pId)); - env e; calldataarg args; - f(e, args); - assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed"); -} +/////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////// State Transitions Rules ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +//=========================================== +//-------- Propose() --> End of Time -------- +//=========================================== + + // pass + /* + * The voting must start not before the proposal’s creation time + */ + rule noStartBeforeCreation(uint256 pId) { + uint256 previousStart = proposalSnapshot(pId); + require previousStart == 0; + env e; + calldataarg arg; + propose(e, arg); + + uint newStart = proposalSnapshot(pId); + // if created, start is after current block number (creation block) + assert(newStart != previousStart => newStart >= e.block.number); + } -/* - * The voting must start not before the proposal’s creation time - */ -rule noStartBeforeCreation(uint256 pId) { - uint previousStart = proposalSnapshot(pId); - require previousStart == 0; - env e; - calldataarg arg; - propose(e, arg); + // pass + /* + * Once a proposal is created, voteStart and voteEnd are immutable + */ + rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint _voteStart = proposalSnapshot(pId); + uint _voteEnd = proposalDeadline(pId); + require _voteStart > 0; // proposal was created - relation proved in noStartBeforeCreation - uint newStart = proposalSnapshot(pId); - // if created, start is after creation - assert(newStart != 0 => newStart >= e.block.number); -} + env e; + calldataarg arg; + f(e, arg); + uint voteStart_ = proposalSnapshot(pId); + uint voteEnd_ = proposalDeadline(pId); + assert _voteStart == voteStart_; + assert _voteEnd == voteEnd_; + } -/* - * Once a proposal is created, voteStart and voteEnd are immutable - */ -rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalSnapshot(pId); - uint _voteEnd = proposalDeadline(pId); - require _voteStart > 0; // proposal was created + // pass + /* + * A proposal cannot be neither executed nor canceled before it starts + */ + rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ + env e; - env e; - calldataarg arg; - f(e, arg); + require !isExecuted(pId) && !isCanceled(pId); - uint voteStart_ = proposalSnapshot(pId); - uint voteEnd_ = proposalDeadline(pId); - assert _voteStart == voteStart_; - assert _voteEnd == voteEnd_; -} + calldataarg arg; + f(e, arg); + assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; + } -/* - * A user cannot vote twice - */ -rule doubleVoting(uint256 pId, uint8 sup) { - env e; - address user = e.msg.sender; +//============================================ +//--- End of Voting Period --> End of Time --- +//============================================ - bool votedCheck = hasVoted(e, pId, user); + // pass + /* + * A proposal cannot be neither executed nor canceled before proposal's deadline + */ + rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ + env e; - castVote@withrevert(e, pId, sup); - bool reverted = lastReverted; + requireInvariant voteStartBeforeVoteEnd(pId); + require !isExecuted(pId) && !isCanceled(pId); - assert votedCheck => reverted, "double voting accured"; -} + calldataarg arg; + f(e, arg); + assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before deadline"; + } -/* - * When a proposal is created the start and end date are created in the future. - */ -rule proposalCreatedStartAndEndDateInFuture(address[] targets, uint256[] values, bytes[] calldatas){ - env e; - uint256 pId = callPropose(e, targets, values, calldatas); - uint256 startDate = proposalSnapshot(pId); - uint256 endDate = proposalDeadline(pId); - assert(startDate >= e.block.number && endDate >= e.block.number, "Start or end date were set in the past"); -} - +//////////////////////////////////////////////////////////////////////////////// +////////////////////// Integrity Of Functions (Unit Tests) ///////////////////// +//////////////////////////////////////////////////////////////////////////////// /** * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) @@ -161,32 +206,30 @@ rule checkHashProposal { } */ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// High Level Rules ////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// -/** - * A proposal cannot be neither executed nor canceled before it starts - */ -rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ - env e; + // not passing + /* + * all non-view functions should revert if proposal is executed + */ + rule allFunctionsRevertIfExecuted(uint256 pId, method f) filtered { f -> !f.isView } { + env e; calldataarg args; + require(isExecuted(pId)); + f@withrevert(e,args); + assert(lastReverted == true, "Function was not reverted"); + } - require !isExecuted(pId) && !isCanceled(pId); - - calldataarg arg; - f(e, arg); - - assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; -} - -/** - * A proposal cannot be neither executed nor canceled before proposal's deadline - */ -rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ - env e; - - requireInvariant voteStartBeforeVoteEnd(pId); - require !isExecuted(pId) && !isCanceled(pId); - - calldataarg arg; - f(e, arg); - - assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; -} \ No newline at end of file + // not passing + /* + * all non-view functions should revert if proposal is canceled + */ + rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView } { + env e; calldataarg args; + uint256 pId = e.msg.value; + require(isCanceled(pId)); + requireInvariant noBothExecutedAndCanceled(pId); + f@withrevert(e,args); + assert(lastReverted == true, "Function was not reverted"); + } \ No newline at end of file From 4337957a6b871c37c0652c6f27e73cb607cdcf48 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Fri, 12 Nov 2021 14:12:03 +0200 Subject: [PATCH 039/254] codeCleaningNumberIDontKnow --- certora/specs/GovernorBase.spec | 229 ++++++++++++++++---------------- 1 file changed, 115 insertions(+), 114 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 4cea34323..88fe278f8 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -37,41 +37,45 @@ methods { */ - /////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// Global Valid States ///////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// - // not complete - /* - * If any of the properties are non zero, the rest has to be non zero - */ - // start !0 !0 !0 - // end = !0 !0 !0 - // exe = 0 0 1 1 - // can = 0 1 0 1 - invariant proposalInitiated(uint256 pId) - (proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0) && - (isCanceled(pId) => proposalSnapshot(pId) != 0) && - (isExecuted(pId) => proposalSnapshot(pId) != 0) - // pass - /* - * A proposal cannot end unless it started. - */ - invariant voteStartBeforeVoteEnd(uint256 pId) - (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) - && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) +/* + * If any of the properties are non zero, the rest has to be non zero + */ + // start !0 !0 !0 + // end = !0 !0 !0 + // exe = 0 0 1 1 + // can = 0 1 0 1 +invariant proposalInitiated(uint256 pId) + (proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0) && + (isCanceled(pId) => proposalSnapshot(pId) != 0) && + (isExecuted(pId) => proposalSnapshot(pId) != 0) - // pass - /* - * A proposal cannot be both executed and canceled. - */ - invariant noBothExecutedAndCanceled(uint256 pId) - !isExecuted(pId) || !isCanceled(pId) +/* + * A proposal cannot end unless it started. + */ +invariant voteStartBeforeVoteEnd(uint256 pId) + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) + && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) +/* + * A proposal cannot be both executed and canceled. + */ +invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) + + +/** + * A proposal could be executed only if quorum was reached and vote succeeded + */ +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) + isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) + /////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// In-State Rules ///////////////////////////////////// @@ -81,22 +85,20 @@ methods { //------------- Voting Period -------------- //========================================== - // pass - /* - * A user cannot vote twice - */ - rule doubleVoting(uint256 pId, uint8 sup) { - env e; - address user = e.msg.sender; - bool votedCheck = hasVoted(e, pId, user); - require votedCheck == true; +/* + * A user cannot vote twice + */ +rule doubleVoting(uint256 pId, uint8 sup) { + env e; + address user = e.msg.sender; + bool votedCheck = hasVoted(e, pId, user); - castVote@withrevert(e, pId, sup); - bool reverted = lastReverted; + castVote@withrevert(e, pId, sup); + bool reverted = lastReverted; - assert votedCheck => reverted, "double voting accured"; - } + assert votedCheck => reverted, "double voting accured"; +} /////////////////////////////////////////////////////////////////////////////////////// @@ -107,76 +109,75 @@ methods { //-------- Propose() --> End of Time -------- //=========================================== - // pass - /* - * The voting must start not before the proposal’s creation time - */ - rule noStartBeforeCreation(uint256 pId) { - uint256 previousStart = proposalSnapshot(pId); - require previousStart == 0; - env e; - calldataarg arg; - propose(e, arg); - uint newStart = proposalSnapshot(pId); - // if created, start is after current block number (creation block) - assert(newStart != previousStart => newStart >= e.block.number); - } +/* + * The voting must start not before the proposal’s creation time + */ +rule noStartBeforeCreation(uint256 pId) { + uint256 previousStart = proposalSnapshot(pId); + require previousStart == 0; + env e; + calldataarg arg; + propose(e, arg); + + uint newStart = proposalSnapshot(pId); + // if created, start is after current block number (creation block) + assert(newStart != previousStart => newStart >= e.block.number); +} - // pass - /* - * Once a proposal is created, voteStart and voteEnd are immutable - */ - rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalSnapshot(pId); - uint _voteEnd = proposalDeadline(pId); - require _voteStart > 0; // proposal was created - relation proved in noStartBeforeCreation +/* + * Once a proposal is created, voteStart and voteEnd are immutable + */ +rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint _voteStart = proposalSnapshot(pId); + uint _voteEnd = proposalDeadline(pId); + require _voteStart > 0; // proposal was created - relation proved in noStartBeforeCreation - env e; - calldataarg arg; - f(e, arg); + env e; + calldataarg arg; + f(e, arg); - uint voteStart_ = proposalSnapshot(pId); - uint voteEnd_ = proposalDeadline(pId); - assert _voteStart == voteStart_; - assert _voteEnd == voteEnd_; - } + uint voteStart_ = proposalSnapshot(pId); + uint voteEnd_ = proposalDeadline(pId); + assert _voteStart == voteStart_; + assert _voteEnd == voteEnd_; +} - // pass - /* - * A proposal cannot be neither executed nor canceled before it starts - */ - rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ - env e; - require !isExecuted(pId) && !isCanceled(pId); +/* + * A proposal cannot be neither executed nor canceled before it starts + */ +rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ + env e; - calldataarg arg; - f(e, arg); + require !isExecuted(pId) && !isCanceled(pId); - assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; - } + calldataarg arg; + f(e, arg); + + assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; +} //============================================ //--- End of Voting Period --> End of Time --- //============================================ - // pass - /* - * A proposal cannot be neither executed nor canceled before proposal's deadline - */ - rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ - env e; - requireInvariant voteStartBeforeVoteEnd(pId); - require !isExecuted(pId) && !isCanceled(pId); +/* + * A proposal cannot be neither executed nor canceled before proposal's deadline + */ +rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ + env e; - calldataarg arg; - f(e, arg); + requireInvariant voteStartBeforeVoteEnd(pId); + require !isExecuted(pId) && !isCanceled(pId); - assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before deadline"; - } + calldataarg arg; + f(e, arg); + + assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before deadline"; +} //////////////////////////////////////////////////////////////////////////////// ////////////////////// Integrity Of Functions (Unit Tests) ///////////////////// @@ -210,26 +211,26 @@ rule checkHashProposal { //////////////////////////////// High Level Rules ////////////////////////////// //////////////////////////////////////////////////////////////////////////////// - // not passing - /* - * all non-view functions should revert if proposal is executed - */ - rule allFunctionsRevertIfExecuted(uint256 pId, method f) filtered { f -> !f.isView } { - env e; calldataarg args; - require(isExecuted(pId)); - f@withrevert(e,args); - assert(lastReverted == true, "Function was not reverted"); - } - // not passing - /* - * all non-view functions should revert if proposal is canceled - */ - rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView } { - env e; calldataarg args; - uint256 pId = e.msg.value; - require(isCanceled(pId)); - requireInvariant noBothExecutedAndCanceled(pId); - f@withrevert(e,args); - assert(lastReverted == true, "Function was not reverted"); - } \ No newline at end of file +/* + * all non-view functions should revert if proposal is executed + */ +rule allFunctionsRevertIfExecuted(uint256 pId, method f) filtered { f -> !f.isView } { + env e; calldataarg args; + require(isExecuted(pId)); + f@withrevert(e,args); + assert(lastReverted == true, "Function was not reverted"); +} + + +/* + * all non-view functions should revert if proposal is canceled + */ +rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView } { + env e; calldataarg args; + uint256 pId = e.msg.value; + require(isCanceled(pId)); + requireInvariant noBothExecutedAndCanceled(pId); + f@withrevert(e,args); + assert(lastReverted == true, "Function was not reverted"); +} From 9a194f24b8f02b8ae798df1e8ccc5708d91953bd Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Sun, 26 Sep 2021 00:21:08 +0300 Subject: [PATCH 040/254] start work on governor --- .../GovernorCountingSimpleHarness.sol | 29 +++++++ certora/harnesses/GovernorHarness.sol | 58 ++++++++++++++ .../GovernorProposalThresholdHarness.sol | 58 ++++++++++++++ .../GovernorTimelockCompoundHarness.sol | 58 ++++++++++++++ certora/harnesses/GovernorVotesHarness.sol | 52 ++++++++++++ .../GovernorVotesQuorumFractionHarness.sol | 46 +++++++++++ certora/scripts/Governor.sh | 2 + certora/scripts/GovernorCountingSimple.sh | 2 + certora/scripts/GovernorProposalThreshold.sh | 2 + certora/scripts/GovernorTimelockCompound.sh | 2 + certora/scripts/GovernorVotes.sh | 2 + .../GovernorVotesQuorumFractionHarness.sh | 2 + certora/scripts/check.sh | 7 ++ certora/specs/GovernorBase.spec | 79 +++++++++++++++++++ certora/specs/Privileged.spec | 31 ++++++++ 15 files changed, 430 insertions(+) create mode 100644 certora/harnesses/GovernorCountingSimpleHarness.sol create mode 100644 certora/harnesses/GovernorHarness.sol create mode 100644 certora/harnesses/GovernorProposalThresholdHarness.sol create mode 100644 certora/harnesses/GovernorTimelockCompoundHarness.sol create mode 100644 certora/harnesses/GovernorVotesHarness.sol create mode 100644 certora/harnesses/GovernorVotesQuorumFractionHarness.sol create mode 100755 certora/scripts/Governor.sh create mode 100755 certora/scripts/GovernorCountingSimple.sh create mode 100755 certora/scripts/GovernorProposalThreshold.sh create mode 100755 certora/scripts/GovernorTimelockCompound.sh create mode 100755 certora/scripts/GovernorVotes.sh create mode 100755 certora/scripts/GovernorVotesQuorumFractionHarness.sh create mode 100755 certora/scripts/check.sh create mode 100644 certora/specs/GovernorBase.spec create mode 100644 certora/specs/Privileged.spec diff --git a/certora/harnesses/GovernorCountingSimpleHarness.sol b/certora/harnesses/GovernorCountingSimpleHarness.sol new file mode 100644 index 000000000..52e980a81 --- /dev/null +++ b/certora/harnesses/GovernorCountingSimpleHarness.sol @@ -0,0 +1,29 @@ +import "../../contracts/governance/extensions/GovernorCountingSimple.sol"; + +contract GovernorCountingSimpleHarness is GovernorCountingSimple { + + mapping(uint256 => uint256) _quorum; + + function quorum(uint256 blockNumber) public view override virtual returns (uint256) { + return _quorum[blockNumber]; + } + + mapping (address => mapping (uint256 => uint256)) _getVotes; + + function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { + return _getVotes[account][blockNumber]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + constructor(string memory name) Governor(name) {} + +} \ No newline at end of file diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol new file mode 100644 index 000000000..d2df3d450 --- /dev/null +++ b/certora/harnesses/GovernorHarness.sol @@ -0,0 +1,58 @@ +import "../../contracts/governance/Governor.sol"; + +contract GovernorHarness is Governor { + + mapping(uint256 => uint256) _quorum; + + function quorum(uint256 blockNumber) public view override virtual returns (uint256) { + return _quorum[blockNumber]; + } + + mapping (address => mapping (uint256 => uint256)) _getVotes; + + function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { + return _getVotes[account][blockNumber]; + } + + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + return __quoromReached[proposalId]; + } + + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + return __voteSucceeded[proposalId]; + } + + //string _COUNTING_MODE; + function COUNTING_MODE() public pure override virtual returns (string memory) { + return "dummy"; + } + + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { + return _hasVoted[proposalId][account]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name) Governor(name) {} + +} \ No newline at end of file diff --git a/certora/harnesses/GovernorProposalThresholdHarness.sol b/certora/harnesses/GovernorProposalThresholdHarness.sol new file mode 100644 index 000000000..8301a76d8 --- /dev/null +++ b/certora/harnesses/GovernorProposalThresholdHarness.sol @@ -0,0 +1,58 @@ +import "../../contracts/governance/extensions/GovernorProposalThreshold.sol"; + +contract GovernorProposalThresholdHarness is GovernorProposalThreshold { + + mapping(uint256 => uint256) _quorum; + + function quorum(uint256 blockNumber) public view override virtual returns (uint256) { + return _quorum[blockNumber]; + } + + mapping (address => mapping (uint256 => uint256)) _getVotes; + + function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { + return _getVotes[account][blockNumber]; + } + + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + return __quoromReached[proposalId]; + } + + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + return __voteSucceeded[proposalId]; + } + + //string _COUNTING_MODE; + function COUNTING_MODE() public pure override virtual returns (string memory) { + return "dummy"; + } + + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { + return _hasVoted[proposalId][account]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name) Governor(name) {} + +} \ No newline at end of file diff --git a/certora/harnesses/GovernorTimelockCompoundHarness.sol b/certora/harnesses/GovernorTimelockCompoundHarness.sol new file mode 100644 index 000000000..5513546ab --- /dev/null +++ b/certora/harnesses/GovernorTimelockCompoundHarness.sol @@ -0,0 +1,58 @@ +import "../../contracts/governance/extensions/GovernorTimelockCompound.sol"; + +contract GovernorTimelockCompoundHarness is GovernorTimelockCompound { + + mapping(uint256 => uint256) _quorum; + + function quorum(uint256 blockNumber) public view override virtual returns (uint256) { + return _quorum[blockNumber]; + } + + mapping (address => mapping (uint256 => uint256)) _getVotes; + + function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { + return _getVotes[account][blockNumber]; + } + + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + return __quoromReached[proposalId]; + } + + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + return __voteSucceeded[proposalId]; + } + + //string _COUNTING_MODE; + function COUNTING_MODE() public pure override virtual returns (string memory) { + return "dummy"; + } + + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { + return _hasVoted[proposalId][account]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name, ICompoundTimelock timelock) Governor(name) GovernorTimelockCompound(timelock) {} + +} \ No newline at end of file diff --git a/certora/harnesses/GovernorVotesHarness.sol b/certora/harnesses/GovernorVotesHarness.sol new file mode 100644 index 000000000..65a095156 --- /dev/null +++ b/certora/harnesses/GovernorVotesHarness.sol @@ -0,0 +1,52 @@ +import "../../contracts/governance/extensions/GovernorVotes.sol"; + +contract GovernorVotesHarness is GovernorVotes { + + mapping(uint256 => uint256) _quorum; + + function quorum(uint256 blockNumber) public view override virtual returns (uint256) { + return _quorum[blockNumber]; + } + + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + return __quoromReached[proposalId]; + } + + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + return __voteSucceeded[proposalId]; + } + + //string _COUNTING_MODE; + function COUNTING_MODE() public pure override virtual returns (string memory) { + return "dummy"; + } + + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { + return _hasVoted[proposalId][account]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name) Governor(name) {} + +} \ No newline at end of file diff --git a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol new file mode 100644 index 000000000..3c7015eb9 --- /dev/null +++ b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol @@ -0,0 +1,46 @@ +import "../../contracts/governance/extensions/GovernorVotesQuorumFractionGovernor.sol"; + +contract GovernorVotesQuorumFractionHarness is GovernorVotesQuorumFraction { + + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + return __quoromReached[proposalId]; + } + + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + return __voteSucceeded[proposalId]; + } + + //string _COUNTING_MODE; + function COUNTING_MODE() public pure override virtual returns (string memory) { + return "dummy"; + } + + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { + return _hasVoted[proposalId][account]; + } + + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name) Governor(name) {} + +} \ No newline at end of file diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh new file mode 100755 index 000000000..4caada718 --- /dev/null +++ b/certora/scripts/Governor.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorHarness.sol \ + --verify GovernorHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorCountingSimple.sh b/certora/scripts/GovernorCountingSimple.sh new file mode 100755 index 000000000..95c2c2551 --- /dev/null +++ b/certora/scripts/GovernorCountingSimple.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorCountingSimpleHarness.sol \ + --verify GovernorCountingSimpleHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorProposalThreshold.sh b/certora/scripts/GovernorProposalThreshold.sh new file mode 100755 index 000000000..cb10572fc --- /dev/null +++ b/certora/scripts/GovernorProposalThreshold.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorProposalThresholdHarness.sol \ + --verify GovernorProposalThresholdHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorTimelockCompound.sh b/certora/scripts/GovernorTimelockCompound.sh new file mode 100755 index 000000000..9cbdd1ea0 --- /dev/null +++ b/certora/scripts/GovernorTimelockCompound.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorTimelockCompoundHarness.sol \ + --verify GovernorTimelockCompoundHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorVotes.sh b/certora/scripts/GovernorVotes.sh new file mode 100755 index 000000000..1dc476445 --- /dev/null +++ b/certora/scripts/GovernorVotes.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorVotesHarness.sol \ + --verify GovernorVotesHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorVotesQuorumFractionHarness.sh b/certora/scripts/GovernorVotesQuorumFractionHarness.sh new file mode 100755 index 000000000..239339ade --- /dev/null +++ b/certora/scripts/GovernorVotesQuorumFractionHarness.sh @@ -0,0 +1,2 @@ +certoraRun certora/harnesses/GovernorVotesQuorumFractionHarness.sol \ + --verify GovernorVotesQuorumFractionHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/check.sh b/certora/scripts/check.sh new file mode 100755 index 000000000..0cb980dc3 --- /dev/null +++ b/certora/scripts/check.sh @@ -0,0 +1,7 @@ +echo "Usage: Contract Spec" +echo "e.g. GovernorVotes Privileged" +Contract=$1 +Spec=$2 +shift 2 +certoraRun certora/harnesses/${Contract}Harness.sol \ + --verify ${Contract}Harness:certora/specs/${Spec}.spec "$@" \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec new file mode 100644 index 000000000..58c07582f --- /dev/null +++ b/certora/specs/GovernorBase.spec @@ -0,0 +1,79 @@ +// Governor.sol base definitions +methods { + proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart +} +ghost proposalVoteStart(uint256) returns uint64 { + init_state axiom forall uint256 pId. proposalVoteStart(pId) == 0; +} +ghost proposalVoteEnd(uint256) returns uint64 { + init_state axiom forall uint256 pId. proposalVoteEnd(pId) == 0; +} +ghost proposalExecuted(uint256) returns bool { + init_state axiom forall uint256 pId. !proposalExecuted(pId); +} +ghost proposalCanceled(uint256) returns bool { + init_state axiom forall uint256 pId. !proposalCanceled(pId); +} + +hook Sstore _proposals[KEY uint256 pId].(offset 0) uint64 newValue STORAGE { + havoc proposalVoteStart assuming ( + proposalVoteStart@new(pId) == newValue + && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) + ); +} + +hook Sload uint64 value _proposals[KEY uint256 pId].(offset 0) STORAGE { + require proposalVoteStart(pId) == value; +} + + +hook Sstore _proposals[KEY uint256 pId].(offset 32).(offset 0) uint64 newValue STORAGE { + havoc proposalVoteEnd assuming ( + proposalVoteEnd@new(pId) == newValue + && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2)) + ); +} + +hook Sload uint64 value _proposals[KEY uint256 pId].(offset 32).(offset 0) STORAGE { + require proposalVoteEnd(pId) == value; +} + +rule sanityCheckVoteStart(method f, uint256 pId) { + uint64 preGhost = proposalVoteStart(pId); + uint256 pre = proposalSnapshot(pId); + + env e; + calldataarg arg; + f(e, arg); + + uint64 postGhost = proposalVoteStart(pId); + uint256 post = proposalSnapshot(pId); + + assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; + assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; +} + +rule sanityCheckVoteEnd(method f, uint256 pId) { + uint64 preGhost = proposalVoteEnd(pId); + uint256 pre = proposalSnapshot(pId); + + env e; + calldataarg arg; + f(e, arg); + + uint64 postGhost = proposalVoteEnd(pId); + uint256 post = proposalSnapshot(pId); + + assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; + assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; +} + +/** + * A proposal cannot end unless it started. + */ +invariant voteStartBeforeVoteEnd(uint256 pId) proposalVoteStart(pId) < proposalVoteEnd(pId) + +/** + * A proposal cannot be both executed and canceled. + */ +invariant noBothExecutedAndCanceled(uint256 pId) !proposalExecuted(pId) || !proposalCanceled(pId) \ No newline at end of file diff --git a/certora/specs/Privileged.spec b/certora/specs/Privileged.spec new file mode 100644 index 000000000..f9615a619 --- /dev/null +++ b/certora/specs/Privileged.spec @@ -0,0 +1,31 @@ +definition knownAsNonPrivileged(method f) returns bool = false +/* ( f.selector == isWhitelistedOtoken(address).selector || + f.selector == isWhitelistedProduct(address,address,address,bool).selector || + f.selector == owner().selector || + f.selector == isWhitelistedCallee(address).selector || + f.selector == whitelistOtoken(address).selector || + f.selector == addressBook().selector || + f.selector == isWhitelistedCollateral(address).selector )*/; + + + +rule privilegedOperation(method f, address privileged) +description "$f can be called by more than one user without reverting" +{ + env e1; + calldataarg arg; + require !knownAsNonPrivileged(f); + require e1.msg.sender == privileged; + + storage initialStorage = lastStorage; + invoke f(e1, arg); // privileged succeeds executing candidate privileged operation. + bool firstSucceeded = !lastReverted; + + env e2; + calldataarg arg2; + require e2.msg.sender != privileged; + invoke f(e2, arg2) at initialStorage; // unprivileged + bool secondSucceeded = !lastReverted; + + assert !(firstSucceeded && secondSucceeded), "${f.selector} can be called by both ${e1.msg.sender} and ${e2.msg.sender}, so it is not privileged"; +} From 4a0077d68564037241857ab893ffe0bb51ed5e5a Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Sun, 26 Sep 2021 00:25:59 +0300 Subject: [PATCH 041/254] Back to expected pattern? --- certora/specs/GovernorBase.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 58c07582f..85c20162b 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -15,14 +15,14 @@ ghost proposalCanceled(uint256) returns bool { init_state axiom forall uint256 pId. !proposalCanceled(pId); } -hook Sstore _proposals[KEY uint256 pId].(offset 0) uint64 newValue STORAGE { +hook Sstore _proposals[KEY uint256 pId].(offset 0).(offset 0) uint64 newValue STORAGE { havoc proposalVoteStart assuming ( proposalVoteStart@new(pId) == newValue && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) ); } -hook Sload uint64 value _proposals[KEY uint256 pId].(offset 0) STORAGE { +hook Sload uint64 value _proposals[KEY uint256 pId].(offset 0).(offset 0) STORAGE { require proposalVoteStart(pId) == value; } From 8494fe20bc4d38599954cf9570903dbfdbded7c0 Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Sun, 26 Sep 2021 01:39:27 +0300 Subject: [PATCH 042/254] fixes --- certora/specs/GovernorBase.spec | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 85c20162b..99c712872 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -2,6 +2,7 @@ methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart } + ghost proposalVoteStart(uint256) returns uint64 { init_state axiom forall uint256 pId. proposalVoteStart(pId) == 0; } @@ -15,27 +16,27 @@ ghost proposalCanceled(uint256) returns bool { init_state axiom forall uint256 pId. !proposalCanceled(pId); } -hook Sstore _proposals[KEY uint256 pId].(offset 0).(offset 0) uint64 newValue STORAGE { +hook Sstore _proposals[KEY uint256 pId] uint64 newValue STORAGE { havoc proposalVoteStart assuming ( - proposalVoteStart@new(pId) == newValue + proposalVoteStart@new(pId) == newValue & (max_uint64-1) && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) ); } -hook Sload uint64 value _proposals[KEY uint256 pId].(offset 0).(offset 0) STORAGE { - require proposalVoteStart(pId) == value; +hook Sload uint64 value _proposals[KEY uint256 pId] STORAGE { + require proposalVoteStart(pId) == value & (max_uint64-1); } hook Sstore _proposals[KEY uint256 pId].(offset 32).(offset 0) uint64 newValue STORAGE { havoc proposalVoteEnd assuming ( - proposalVoteEnd@new(pId) == newValue + proposalVoteEnd@new(pId) == newValue & (max_uint64-1) && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2)) ); } hook Sload uint64 value _proposals[KEY uint256 pId].(offset 32).(offset 0) STORAGE { - require proposalVoteEnd(pId) == value; + require proposalVoteEnd(pId) == value & (max_uint64-1); } rule sanityCheckVoteStart(method f, uint256 pId) { From a0cb8cd446d96bcbce37cfdce4f7f9b19baba585 Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Sun, 26 Sep 2021 01:43:16 +0300 Subject: [PATCH 043/254] fixes --- certora/specs/GovernorBase.spec | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 99c712872..32b0fd21b 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -16,27 +16,29 @@ ghost proposalCanceled(uint256) returns bool { init_state axiom forall uint256 pId. !proposalCanceled(pId); } +definition mask_uint64() returns uint256 = max_uint64 - 1; + hook Sstore _proposals[KEY uint256 pId] uint64 newValue STORAGE { havoc proposalVoteStart assuming ( - proposalVoteStart@new(pId) == newValue & (max_uint64-1) + proposalVoteStart@new(pId) == newValue & mask_uint64() && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) ); } hook Sload uint64 value _proposals[KEY uint256 pId] STORAGE { - require proposalVoteStart(pId) == value & (max_uint64-1); + require proposalVoteStart(pId) == value & mask_uint64(); } hook Sstore _proposals[KEY uint256 pId].(offset 32).(offset 0) uint64 newValue STORAGE { havoc proposalVoteEnd assuming ( - proposalVoteEnd@new(pId) == newValue & (max_uint64-1) + proposalVoteEnd@new(pId) == newValue & mask_uint64() && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2)) ); } hook Sload uint64 value _proposals[KEY uint256 pId].(offset 32).(offset 0) STORAGE { - require proposalVoteEnd(pId) == value & (max_uint64-1); + require proposalVoteEnd(pId) == value & mask_uint64(); } rule sanityCheckVoteStart(method f, uint256 pId) { From ea6baf22205b98904257a030ba13dd7da2f97947 Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Thu, 7 Oct 2021 11:58:47 +0300 Subject: [PATCH 044/254] rule drafts --- certora/harnesses/GovernorHarness.sol | 4 +- certora/specs/GovernorBase.spec | 72 ++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index d2df3d450..332f8b863 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -15,12 +15,12 @@ contract GovernorHarness is Governor { } mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 32b0fd21b..60949c987 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -1,6 +1,11 @@ // Governor.sol base definitions methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart + hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree + + // internal functions made public in harness: + _quorumReached(uint256) returns bool envfree + _voteSucceeded(uint256) returns bool envfree } ghost proposalVoteStart(uint256) returns uint64 { @@ -79,4 +84,69 @@ invariant voteStartBeforeVoteEnd(uint256 pId) proposalVoteStart(pId) < proposalV /** * A proposal cannot be both executed and canceled. */ -invariant noBothExecutedAndCanceled(uint256 pId) !proposalExecuted(pId) || !proposalCanceled(pId) \ No newline at end of file +invariant noBothExecutedAndCanceled(uint256 pId) !proposalExecuted(pId) || !proposalCanceled(pId) + +/** + * A proposal cannot be executed nor canceled before it starts + */ +invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.timestamp < proposalVoteStart(pId) => !proposalExecuted(pId) && !proposalCanceled(pId) + +/** + * The voting must start not before the proposal’s creation time + */ +rule noStartBeforeCreation(uint256 pId) { + uint previousStart = proposalVoteStart(pId); + require previousStart == 0; + env e; + calldataarg arg; + propose(e, arg); + + uint newStart = proposalVoteStart(pId); + // if created, start is after creation + assert newStart != 0 => newStart > e.block.timestamp; +} + +/** + * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) + */ +rule checkHashProposal { + address[] t1; + address[] t2; + uint256[] v1; + uint256[] v2; + bytes[] c1; + bytes[] c2; + bytes32 d1; + bytes32 d2; + + uint256 h1 = hashProposal(t1,v1,c1,d1); + uint256 h2 = hashProposal(t2,v2,c2,d2); + bool equalHashes = h1 == h2; + assert equalHashes => t1.length == t2.length; + assert equalHashes => v1.length == v2.length; + assert equalHashes => c1.length == c2.length; + assert equalHashes => d1 == d2; +} + +/** + * A proposal could be executed only if quorum was reached and vote succeeded + */ +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) proposalExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) + +/** + * Once a proposal is created, voteStart and voteEnd are immutable + */ +rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint _voteStart = proposalVoteStart(pId); + uint _voteEnd = proposalVoteEnd(pId); + require _voteStart > 0; // proposal was created + + env e; + calldataarg arg; + f(e, arg); + + uint voteStart_ = proposalVoteStart(pId); + uint voteEnd_ = proposalVoteEnd(pId); + assert _voteStart == voteStart_; + assert _voteEnd == voteEnd_; +} \ No newline at end of file From d6036f929100ba719d96101f30443861f94b46ff Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Tue, 2 Nov 2021 14:01:48 +0200 Subject: [PATCH 045/254] ignore certora's generated files --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 0a62cf0b3..c60c5d945 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,8 @@ allFiredEvents # hardhat cache artifacts + +# Certora +.certora* +.last_confs +certora_* From e81037926227b4778fee7710eb8daafc5781ba15 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Wed, 3 Nov 2021 17:05:06 +0200 Subject: [PATCH 046/254] sanity rule preparations --- .../harnesses/GovernorProposalThresholdHarness.sol | 9 +++++++-- .../harnesses/GovernorTimelockCompoundHarness.sol | 4 ++-- certora/harnesses/GovernorVotesHarness.sol | 7 +++---- .../GovernorVotesQuorumFractionHarness.sol | 6 +++--- certora/scripts/check.sh | 4 +++- certora/scripts/sanity.sh | 9 +++++++++ certora/specs/sanity.spec | 14 ++++++++++++++ contracts/governance/Governor.sol | 4 ++-- .../extensions/GovernorCountingSimple.sol | 4 ++-- 9 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 certora/scripts/sanity.sh create mode 100644 certora/specs/sanity.spec diff --git a/certora/harnesses/GovernorProposalThresholdHarness.sol b/certora/harnesses/GovernorProposalThresholdHarness.sol index 8301a76d8..1d0559a60 100644 --- a/certora/harnesses/GovernorProposalThresholdHarness.sol +++ b/certora/harnesses/GovernorProposalThresholdHarness.sol @@ -15,12 +15,12 @@ contract GovernorProposalThresholdHarness is GovernorProposalThreshold { } mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } @@ -53,6 +53,11 @@ contract GovernorProposalThresholdHarness is GovernorProposalThreshold { // havoc something } + uint256 _proposalThreshold; + function proposalThreshold() public view override virtual returns (uint256) { + return _proposalThreshold; + } + constructor(string memory name) Governor(name) {} } \ No newline at end of file diff --git a/certora/harnesses/GovernorTimelockCompoundHarness.sol b/certora/harnesses/GovernorTimelockCompoundHarness.sol index 5513546ab..f8a85e53f 100644 --- a/certora/harnesses/GovernorTimelockCompoundHarness.sol +++ b/certora/harnesses/GovernorTimelockCompoundHarness.sol @@ -15,12 +15,12 @@ contract GovernorTimelockCompoundHarness is GovernorTimelockCompound { } mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } diff --git a/certora/harnesses/GovernorVotesHarness.sol b/certora/harnesses/GovernorVotesHarness.sol index 65a095156..8ed638e41 100644 --- a/certora/harnesses/GovernorVotesHarness.sol +++ b/certora/harnesses/GovernorVotesHarness.sol @@ -9,12 +9,12 @@ contract GovernorVotesHarness is GovernorVotes { } mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } @@ -47,6 +47,5 @@ contract GovernorVotesHarness is GovernorVotes { // havoc something } - constructor(string memory name) Governor(name) {} - + constructor(ERC20Votes tokenAddr) GovernorVotes(tokenAddr) {} } \ No newline at end of file diff --git a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol index 3c7015eb9..65be741d4 100644 --- a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol +++ b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol @@ -1,14 +1,14 @@ -import "../../contracts/governance/extensions/GovernorVotesQuorumFractionGovernor.sol"; +import "../../contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; contract GovernorVotesQuorumFractionHarness is GovernorVotesQuorumFraction { mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) { + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) { + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } diff --git a/certora/scripts/check.sh b/certora/scripts/check.sh index 0cb980dc3..cdb356e90 100755 --- a/certora/scripts/check.sh +++ b/certora/scripts/check.sh @@ -4,4 +4,6 @@ Contract=$1 Spec=$2 shift 2 certoraRun certora/harnesses/${Contract}Harness.sol \ - --verify ${Contract}Harness:certora/specs/${Spec}.spec "$@" \ No newline at end of file + --verify ${Contract}Harness:certora/specs/${Spec}.spec "$@" \ + --solc solc8.0 + \ No newline at end of file diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh new file mode 100644 index 000000000..4db90ebff --- /dev/null +++ b/certora/scripts/sanity.sh @@ -0,0 +1,9 @@ +for f in certora/harnesses/*.sol +do + echo "Processing $f" + file=$(basename $f) + echo ${file%.*} + certoraRun certora/harnesses/$file \ + --verify ${file%.*}:certora/specs/sanity.spec "$@" \ + --solc solc8.0 +done \ No newline at end of file diff --git a/certora/specs/sanity.spec b/certora/specs/sanity.spec new file mode 100644 index 000000000..e08f68845 --- /dev/null +++ b/certora/specs/sanity.spec @@ -0,0 +1,14 @@ +/* +This rule looks for a non-reverting execution path to each method, including those overridden in the harness. +A method has such an execution path if it violates this rule. +How it works: + - If there is a non-reverting execution path, we reach the false assertion, and the sanity fails. + - If all execution paths are reverting, we never call the assertion, and the method will pass this rule vacuously. +*/ + +rule sanity(method f) { + env e; + calldataarg arg; + f(e, arg); + assert false; +} \ No newline at end of file diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index f11287ca8..9f08dc2ad 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -154,12 +154,12 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) internal view virtual returns (bool); + function _quorumReached(uint256 proposalId) public view virtual returns (bool); /** * @dev Is the proposal successful or not. */ - function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool); + function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); /** * @dev Register a vote with a given support and voting weight. diff --git a/contracts/governance/extensions/GovernorCountingSimple.sol b/contracts/governance/extensions/GovernorCountingSimple.sol index 782c8a699..b8c72ed9e 100644 --- a/contracts/governance/extensions/GovernorCountingSimple.sol +++ b/contracts/governance/extensions/GovernorCountingSimple.sol @@ -64,7 +64,7 @@ abstract contract GovernorCountingSimple is Governor { /** * @dev See {Governor-_quorumReached}. */ - function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { + function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { ProposalVote storage proposalvote = _proposalVotes[proposalId]; return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes; @@ -73,7 +73,7 @@ abstract contract GovernorCountingSimple is Governor { /** * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. */ - function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { + function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { ProposalVote storage proposalvote = _proposalVotes[proposalId]; return proposalvote.forVotes > proposalvote.againstVotes; From 2d33674870b15f5d729235a81ed1cecef5504ac9 Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Wed, 3 Nov 2021 17:09:27 +0200 Subject: [PATCH 047/254] multiple inheritance is tricky --- certora/harnesses/GovernorVotesHarness.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/harnesses/GovernorVotesHarness.sol b/certora/harnesses/GovernorVotesHarness.sol index 8ed638e41..3e06c8daa 100644 --- a/certora/harnesses/GovernorVotesHarness.sol +++ b/certora/harnesses/GovernorVotesHarness.sol @@ -47,5 +47,5 @@ contract GovernorVotesHarness is GovernorVotes { // havoc something } - constructor(ERC20Votes tokenAddr) GovernorVotes(tokenAddr) {} + constructor(ERC20Votes tokenAddr, string memory name) GovernorVotes(tokenAddr) Governor(name) {} } \ No newline at end of file From 1c35a7dad0a0cb3a868a8ebe99567cbf8ae2c890 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Wed, 3 Nov 2021 17:24:35 +0200 Subject: [PATCH 048/254] multiple inheritance is tricky x2 --- certora/harnesses/GovernorVotesQuorumFractionHarness.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol index 65be741d4..86ae202ad 100644 --- a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol +++ b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol @@ -41,6 +41,7 @@ contract GovernorVotesQuorumFractionHarness is GovernorVotesQuorumFraction { // havoc something } - constructor(string memory name) Governor(name) {} + constructor(ERC20Votes tokenAddr, string memory name, uint256 quorumNumeratorValue) + GovernorVotesQuorumFraction(quorumNumeratorValue) GovernorVotes(tokenAddr) Governor(name) {} } \ No newline at end of file From 788d4672d79000f672a871c52cddd45dd2472153 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 11:27:44 +0200 Subject: [PATCH 049/254] slight script changes and ghost fix --- certora/harnesses/GovernorHarness.sol | 13 +++++++++++++ certora/scripts/Governor.sh | 8 +++++++- certora/scripts/check.sh | 2 +- certora/scripts/sanity.sh | 4 +++- certora/specs/GovernorBase.spec | 28 +++++++++++++++++---------- contracts/governance/Governor.sol | 4 ++-- 6 files changed, 44 insertions(+), 15 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index 332f8b863..f12a3ab4e 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -8,42 +8,55 @@ contract GovernorHarness is Governor { return _quorum[blockNumber]; } + mapping (address => mapping (uint256 => uint256)) _getVotes; function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { return _getVotes[account][blockNumber]; } + mapping (uint256 => bool) __quoromReached; + function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { return __quoromReached[proposalId]; } + mapping (uint256 => bool) __voteSucceeded; + function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { return __voteSucceeded[proposalId]; } + //string _COUNTING_MODE; function COUNTING_MODE() public pure override virtual returns (string memory) { return "dummy"; } + mapping(uint256 => mapping(address => bool)) _hasVoted; + function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { return _hasVoted[proposalId][account]; } + uint256 _votingDelay; + function votingDelay() public view override virtual returns (uint256) { return _votingDelay; } + uint256 _votingPeriod; + function votingPeriod() public view override virtual returns (uint256) { return _votingPeriod; } + function _countVote( uint256 proposalId, address account, diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index 4caada718..cba7edf6b 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -1,2 +1,8 @@ certoraRun certora/harnesses/GovernorHarness.sol \ - --verify GovernorHarness:certora/specs/Privileged.spec \ No newline at end of file + --verify GovernorHarness:certora/specs/GovernorBase.spec \ + --solc solc8.0 \ + --staging \ + --msg $1 \ + --disableLocalTypeChecking \ + --rule voteStartBeforeVoteEnd + diff --git a/certora/scripts/check.sh b/certora/scripts/check.sh index cdb356e90..e3291eb1c 100755 --- a/certora/scripts/check.sh +++ b/certora/scripts/check.sh @@ -5,5 +5,5 @@ Spec=$2 shift 2 certoraRun certora/harnesses/${Contract}Harness.sol \ --verify ${Contract}Harness:certora/specs/${Spec}.spec "$@" \ - --solc solc8.0 + --solc solc8.0 --staging --rule noBothExecutedAndCanceled \ No newline at end of file diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index 4db90ebff..0da31d15e 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -5,5 +5,7 @@ do echo ${file%.*} certoraRun certora/harnesses/$file \ --verify ${file%.*}:certora/specs/sanity.spec "$@" \ - --solc solc8.0 + --solc solc8.0 \ + --staging \ + --msg "sanity ${file}" done \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 60949c987..b54f8c0e8 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -21,31 +21,34 @@ ghost proposalCanceled(uint256) returns bool { init_state axiom forall uint256 pId. !proposalCanceled(pId); } -definition mask_uint64() returns uint256 = max_uint64 - 1; - -hook Sstore _proposals[KEY uint256 pId] uint64 newValue STORAGE { +hook Sstore _proposals[KEY uint256 pId].voteStart._deadline uint64 newValue STORAGE { havoc proposalVoteStart assuming ( - proposalVoteStart@new(pId) == newValue & mask_uint64() + proposalVoteStart@new(pId) == newValue && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) ); } -hook Sload uint64 value _proposals[KEY uint256 pId] STORAGE { - require proposalVoteStart(pId) == value & mask_uint64(); +hook Sload uint64 value _proposals[KEY uint256 pId].voteStart._deadline STORAGE { + require proposalVoteStart(pId) == value; } -hook Sstore _proposals[KEY uint256 pId].(offset 32).(offset 0) uint64 newValue STORAGE { +hook Sstore _proposals[KEY uint256 pId].voteEnd._deadline uint64 newValue STORAGE { havoc proposalVoteEnd assuming ( - proposalVoteEnd@new(pId) == newValue & mask_uint64() + proposalVoteEnd@new(pId) == newValue && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2)) ); } -hook Sload uint64 value _proposals[KEY uint256 pId].(offset 32).(offset 0) STORAGE { - require proposalVoteEnd(pId) == value & mask_uint64(); +hook Sload uint64 value _proposals[KEY uint256 pId].voteEnd._deadline STORAGE { + require proposalVoteEnd(pId) == value; } +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// SANITY CHECKS /////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// + rule sanityCheckVoteStart(method f, uint256 pId) { uint64 preGhost = proposalVoteStart(pId); uint256 pre = proposalSnapshot(pId); @@ -76,6 +79,11 @@ rule sanityCheckVoteEnd(method f, uint256 pId) { assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; } +////////////////////////////////////////////////////////////////////////////// +////////////////////////////// INVARIANTS //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// + /** * A proposal cannot end unless it started. */ diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index 9f08dc2ad..16283c728 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -154,12 +154,12 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) public view virtual returns (bool); + function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public /** * @dev Is the proposal successful or not. */ - function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); + function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public /** * @dev Register a vote with a given support and voting weight. From 32ab301c9d27ebe17dfdef5c3b8b71902b7e5582 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 11:48:55 +0200 Subject: [PATCH 050/254] Hooks fixed --- certora/specs/GovernorBase.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index b54f8c0e8..ae7d0a073 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -82,7 +82,7 @@ rule sanityCheckVoteEnd(method f, uint256 pId) { ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -// +// /** * A proposal cannot end unless it started. From 6307b3bb64082587c650e82f4cb401a3cd4ec18f Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 11:50:40 +0200 Subject: [PATCH 051/254] slight changes change for convenience + disableLocalTypeChecking flag for the hooks --- certora/scripts/Governor.sh | 1 - certora/scripts/sanity.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index cba7edf6b..8450e7d03 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -5,4 +5,3 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --msg $1 \ --disableLocalTypeChecking \ --rule voteStartBeforeVoteEnd - diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index 0da31d15e..d157d6d42 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -8,4 +8,4 @@ do --solc solc8.0 \ --staging \ --msg "sanity ${file}" -done \ No newline at end of file +done From 547e7a8308f7c58a6768263d4abb47c33cfc68a0 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 11:51:14 +0200 Subject: [PATCH 052/254] Harness private to public --- contracts/governance/Governor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index 16283c728..82675bf69 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -154,7 +154,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public + function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public /** * @dev Is the proposal successful or not. From c08a73a6cada4f36fd0768faba243cb8fe193021 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:29:04 +0200 Subject: [PATCH 053/254] Harness private to public --- contracts/governance/Governor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index 82675bf69..16283c728 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -154,7 +154,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public + function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public /** * @dev Is the proposal successful or not. From 6323c9a73dc036cfed87ea7043ddc897c8152c61 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:35:11 +0200 Subject: [PATCH 054/254] slight changes in scripts + disableLocalTypeChecking --- certora/scripts/Governor.sh | 2 +- certora/scripts/sanity.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index 8450e7d03..97486c2fa 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -4,4 +4,4 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --staging \ --msg $1 \ --disableLocalTypeChecking \ - --rule voteStartBeforeVoteEnd + --rule voteStartBeforeVoteEnd \ No newline at end of file diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index d157d6d42..0da31d15e 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -8,4 +8,4 @@ do --solc solc8.0 \ --staging \ --msg "sanity ${file}" -done +done \ No newline at end of file From 91f89198768ec48c3b1bd085acf56c3338636461 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:35:37 +0200 Subject: [PATCH 055/254] hooks fixed --- certora/specs/GovernorBase.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index ae7d0a073..b54f8c0e8 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -82,7 +82,7 @@ rule sanityCheckVoteEnd(method f, uint256 pId) { ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -// +// /** * A proposal cannot end unless it started. From 9298482163d1f01e7f28f5f33e6ca0df0a253118 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:54:23 +0200 Subject: [PATCH 056/254] scripts settings added --- certora/scripts/Governor.sh | 4 +++- certora/scripts/sanity.sh | 7 ++++--- certora/specs/GovernorBase.spec | 1 - 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index 97486c2fa..8b177300d 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -4,4 +4,6 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --staging \ --msg $1 \ --disableLocalTypeChecking \ - --rule voteStartBeforeVoteEnd \ No newline at end of file + --optimistic_loop \ + --settings -copyLoopUnroll=4 + --rule sanityCheckVoteStart diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index 0da31d15e..7ffaf52f7 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -5,7 +5,8 @@ do echo ${file%.*} certoraRun certora/harnesses/$file \ --verify ${file%.*}:certora/specs/sanity.spec "$@" \ - --solc solc8.0 \ - --staging \ - --msg "sanity ${file}" + --solc solc8.0 --staging \ + --optimistic_loop \ + --msg "checking sanity on ${file%.*}" + --settings -copyLoopUnroll=4 done \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index b54f8c0e8..17a37b88e 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -32,7 +32,6 @@ hook Sload uint64 value _proposals[KEY uint256 pId].voteStart._deadline STORAGE require proposalVoteStart(pId) == value; } - hook Sstore _proposals[KEY uint256 pId].voteEnd._deadline uint64 newValue STORAGE { havoc proposalVoteEnd assuming ( proposalVoteEnd@new(pId) == newValue From 364da56ab4755d895a2595c76b9372d1996be4ec Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 4 Nov 2021 15:03:28 +0200 Subject: [PATCH 057/254] quotes on var in msg --- certora/scripts/Governor.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index 8b177300d..c31b43cd9 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -2,8 +2,8 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --verify GovernorHarness:certora/specs/GovernorBase.spec \ --solc solc8.0 \ --staging \ - --msg $1 \ - --disableLocalTypeChecking \ --optimistic_loop \ - --settings -copyLoopUnroll=4 - --rule sanityCheckVoteStart + --disableLocalTypeChecking \ + --settings -copyLoopUnroll=4 \ + --rule voteStartBeforeVoteEnd \ + --msg "$1" From 77efd53f0c2fc6d5118589b44f43abd04d0d992a Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Thu, 4 Nov 2021 17:54:26 +0200 Subject: [PATCH 058/254] checkingInvariantsWithoutGhosts --- certora/harnesses/GovernorHarness.sol | 8 ++++++++ certora/scripts/Governor.sh | 3 +-- certora/specs/GovernorBase.spec | 22 +++++++++++++++------- contracts/governance/Governor.sol | 2 +- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index f12a3ab4e..28e556556 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -2,6 +2,14 @@ import "../../contracts/governance/Governor.sol"; contract GovernorHarness is Governor { + function isExecuted(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].executed; + } + + function isCanceled(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].canceled; + } + mapping(uint256 => uint256) _quorum; function quorum(uint256 blockNumber) public view override virtual returns (uint256) { diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index c31b43cd9..ccdf90203 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -3,7 +3,6 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --solc solc8.0 \ --staging \ --optimistic_loop \ - --disableLocalTypeChecking \ --settings -copyLoopUnroll=4 \ - --rule voteStartBeforeVoteEnd \ + --rule noExecuteOrCancelBeforeStarting \ --msg "$1" diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 17a37b88e..cbe376bf0 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -2,25 +2,32 @@ methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree + isExecuted(uint256) returns bool envfree + isCanceled(uint256) returns bool envfree // internal functions made public in harness: _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree } +/* ghost proposalVoteStart(uint256) returns uint64 { init_state axiom forall uint256 pId. proposalVoteStart(pId) == 0; } ghost proposalVoteEnd(uint256) returns uint64 { init_state axiom forall uint256 pId. proposalVoteEnd(pId) == 0; } +*/ + +/* ghost proposalExecuted(uint256) returns bool { init_state axiom forall uint256 pId. !proposalExecuted(pId); } ghost proposalCanceled(uint256) returns bool { init_state axiom forall uint256 pId. !proposalCanceled(pId); } - +*/ +/* hook Sstore _proposals[KEY uint256 pId].voteStart._deadline uint64 newValue STORAGE { havoc proposalVoteStart assuming ( proposalVoteStart@new(pId) == newValue @@ -42,21 +49,22 @@ hook Sstore _proposals[KEY uint256 pId].voteEnd._deadline uint64 newValue STORAG hook Sload uint64 value _proposals[KEY uint256 pId].voteEnd._deadline STORAGE { require proposalVoteEnd(pId) == value; } +*/ ////////////////////////////////////////////////////////////////////////////// //////////////////////////// SANITY CHECKS /////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // - +/* rule sanityCheckVoteStart(method f, uint256 pId) { - uint64 preGhost = proposalVoteStart(pId); + uint64 preGhost = _proposals(pId).voteStart._deadline; uint256 pre = proposalSnapshot(pId); env e; calldataarg arg; f(e, arg); - uint64 postGhost = proposalVoteStart(pId); + uint64 postGhost = _proposals(pId).voteStart._deadline; uint256 post = proposalSnapshot(pId); assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; @@ -77,7 +85,7 @@ rule sanityCheckVoteEnd(method f, uint256 pId) { assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; } - +*/ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -91,12 +99,12 @@ invariant voteStartBeforeVoteEnd(uint256 pId) proposalVoteStart(pId) < proposalV /** * A proposal cannot be both executed and canceled. */ -invariant noBothExecutedAndCanceled(uint256 pId) !proposalExecuted(pId) || !proposalCanceled(pId) +invariant noBothExecutedAndCanceled(uint256 pId) !isExecuted(pId) || !isCanceled(pId) /** * A proposal cannot be executed nor canceled before it starts */ -invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.timestamp < proposalVoteStart(pId) => !proposalExecuted(pId) && !proposalCanceled(pId) +invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.timestamp < proposalVoteStart(pId) => !isExecuted(pId) && !isCanceled(pId) /** * The voting must start not before the proposal’s creation time diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index 16283c728..b8999716f 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -38,7 +38,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { string private _name; - mapping(uint256 => ProposalCore) private _proposals; + mapping(uint256 => ProposalCore) public _proposals; /** * @dev Restrict access to governor executing address. Some module might override the _executor function to make From 85b65befd52b947011e2bee46ef09b6fa43923aa Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Sun, 7 Nov 2021 17:55:03 +0200 Subject: [PATCH 059/254] WorkInProgress --- certora/specs/GovernorBase.spec | 50 +++++++++++++++++++++---------- contracts/governance/Governor.sol | 2 +- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index cbe376bf0..f70bb5154 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -1,6 +1,7 @@ // Governor.sol base definitions methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart + proposalDeadline(uint256) returns uint256 envfree hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree isExecuted(uint256) returns bool envfree isCanceled(uint256) returns bool envfree @@ -91,39 +92,60 @@ rule sanityCheckVoteEnd(method f, uint256 pId) { ////////////////////////////////////////////////////////////////////////////// // +invariant inizialized() + forall uint256 pId. proposalSnapshot(pId) != 0 && proposalDeadline(pId) != 0 + => pId != 0 + +invariant uninizialized(uint256 pId) + proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0 + /** * A proposal cannot end unless it started. */ -invariant voteStartBeforeVoteEnd(uint256 pId) proposalVoteStart(pId) < proposalVoteEnd(pId) +//invariant voteStartBeforeVoteEnd1(uint256 pId) proposalSnapshot(pId) < proposalDeadline(pId) +// ALARM +invariant voteStartBeforeVoteEnd(uint256 pId) + (proposalSnapshot(pId) == 0 <=> proposalDeadline(pId) == 0) && + proposalSnapshot(pId) < proposalDeadline(pId) /** * A proposal cannot be both executed and canceled. */ + // @AK - no violations invariant noBothExecutedAndCanceled(uint256 pId) !isExecuted(pId) || !isCanceled(pId) /** - * A proposal cannot be executed nor canceled before it starts + * A proposal cannot be neither executed nor canceled before it starts */ -invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.timestamp < proposalVoteStart(pId) => !isExecuted(pId) && !isCanceled(pId) + // @AK - violations convert to a rule +invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.number < proposalSnapshot(pId) + => !isExecuted(pId) && !isCanceled(pId) + +/** + * A proposal could be executed only if quorum was reached and vote succeeded + */ + // @AK - no violations +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) /** * The voting must start not before the proposal’s creation time */ rule noStartBeforeCreation(uint256 pId) { - uint previousStart = proposalVoteStart(pId); + uint previousStart = proposalSnapshot(pId); require previousStart == 0; env e; calldataarg arg; propose(e, arg); - uint newStart = proposalVoteStart(pId); + uint newStart = proposalSnapshot(pId); // if created, start is after creation - assert newStart != 0 => newStart > e.block.timestamp; + assert newStart != 0 => newStart >= e.block.number; } /** * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) */ + /* rule checkHashProposal { address[] t1; address[] t2; @@ -142,26 +164,24 @@ rule checkHashProposal { assert equalHashes => c1.length == c2.length; assert equalHashes => d1 == d2; } +*/ -/** - * A proposal could be executed only if quorum was reached and vote succeeded - */ -invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) proposalExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) /** * Once a proposal is created, voteStart and voteEnd are immutable */ + // @AK - no violations rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalVoteStart(pId); - uint _voteEnd = proposalVoteEnd(pId); + uint _voteStart = proposalSnapshot(pId); + uint _voteEnd = proposalDeadline(pId); require _voteStart > 0; // proposal was created env e; calldataarg arg; f(e, arg); - uint voteStart_ = proposalVoteStart(pId); - uint voteEnd_ = proposalVoteEnd(pId); + uint voteStart_ = proposalSnapshot(pId); + uint voteEnd_ = proposalDeadline(pId); assert _voteStart == voteStart_; assert _voteEnd == voteEnd_; -} \ No newline at end of file +} diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index b8999716f..39dcbbed9 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -39,7 +39,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { string private _name; mapping(uint256 => ProposalCore) public _proposals; - + /** * @dev Restrict access to governor executing address. Some module might override the _executor function to make * sure this modifier is consistant with the execution model. From d4b9e9ab80dc59445c06cf8814d057b017c347d6 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 8 Nov 2021 11:44:04 +0200 Subject: [PATCH 060/254] someCleaning --- certora/specs/GovernorBase.spec | 95 +++------------------------------ 1 file changed, 7 insertions(+), 88 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index f70bb5154..b37ba6e22 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -11,99 +11,15 @@ methods { _voteSucceeded(uint256) returns bool envfree } -/* -ghost proposalVoteStart(uint256) returns uint64 { - init_state axiom forall uint256 pId. proposalVoteStart(pId) == 0; -} -ghost proposalVoteEnd(uint256) returns uint64 { - init_state axiom forall uint256 pId. proposalVoteEnd(pId) == 0; -} -*/ - -/* -ghost proposalExecuted(uint256) returns bool { - init_state axiom forall uint256 pId. !proposalExecuted(pId); -} -ghost proposalCanceled(uint256) returns bool { - init_state axiom forall uint256 pId. !proposalCanceled(pId); -} -*/ -/* -hook Sstore _proposals[KEY uint256 pId].voteStart._deadline uint64 newValue STORAGE { - havoc proposalVoteStart assuming ( - proposalVoteStart@new(pId) == newValue - && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2)) - ); -} - -hook Sload uint64 value _proposals[KEY uint256 pId].voteStart._deadline STORAGE { - require proposalVoteStart(pId) == value; -} - -hook Sstore _proposals[KEY uint256 pId].voteEnd._deadline uint64 newValue STORAGE { - havoc proposalVoteEnd assuming ( - proposalVoteEnd@new(pId) == newValue - && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2)) - ); -} - -hook Sload uint64 value _proposals[KEY uint256 pId].voteEnd._deadline STORAGE { - require proposalVoteEnd(pId) == value; -} -*/ - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////// SANITY CHECKS /////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -// -/* -rule sanityCheckVoteStart(method f, uint256 pId) { - uint64 preGhost = _proposals(pId).voteStart._deadline; - uint256 pre = proposalSnapshot(pId); - - env e; - calldataarg arg; - f(e, arg); - - uint64 postGhost = _proposals(pId).voteStart._deadline; - uint256 post = proposalSnapshot(pId); - - assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; - assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; -} - -rule sanityCheckVoteEnd(method f, uint256 pId) { - uint64 preGhost = proposalVoteEnd(pId); - uint256 pre = proposalSnapshot(pId); - - env e; - calldataarg arg; - f(e, arg); - - uint64 postGhost = proposalVoteEnd(pId); - uint256 post = proposalSnapshot(pId); - - assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes"; - assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end"; -} -*/ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -// -invariant inizialized() - forall uint256 pId. proposalSnapshot(pId) != 0 && proposalDeadline(pId) != 0 - => pId != 0 - -invariant uninizialized(uint256 pId) - proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0 /** * A proposal cannot end unless it started. */ //invariant voteStartBeforeVoteEnd1(uint256 pId) proposalSnapshot(pId) < proposalDeadline(pId) -// ALARM invariant voteStartBeforeVoteEnd(uint256 pId) (proposalSnapshot(pId) == 0 <=> proposalDeadline(pId) == 0) && proposalSnapshot(pId) < proposalDeadline(pId) @@ -111,22 +27,26 @@ invariant voteStartBeforeVoteEnd(uint256 pId) /** * A proposal cannot be both executed and canceled. */ - // @AK - no violations invariant noBothExecutedAndCanceled(uint256 pId) !isExecuted(pId) || !isCanceled(pId) /** * A proposal cannot be neither executed nor canceled before it starts */ - // @AK - violations convert to a rule invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.number < proposalSnapshot(pId) => !isExecuted(pId) && !isCanceled(pId) /** * A proposal could be executed only if quorum was reached and vote succeeded */ - // @AK - no violations invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) + + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// RULES //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + /** * The voting must start not before the proposal’s creation time */ @@ -170,7 +90,6 @@ rule checkHashProposal { /** * Once a proposal is created, voteStart and voteEnd are immutable */ - // @AK - no violations rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { uint _voteStart = proposalSnapshot(pId); uint _voteEnd = proposalDeadline(pId); From c50cb000dd373ef3c5ff552ad3aa95ce713f70ba Mon Sep 17 00:00:00 2001 From: Shelly Grossman Date: Mon, 8 Nov 2021 14:57:51 +0200 Subject: [PATCH 061/254] fix simple vote end before start --- certora/specs/GovernorBase.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index b37ba6e22..69d1fd85e 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -21,8 +21,8 @@ methods { */ //invariant voteStartBeforeVoteEnd1(uint256 pId) proposalSnapshot(pId) < proposalDeadline(pId) invariant voteStartBeforeVoteEnd(uint256 pId) - (proposalSnapshot(pId) == 0 <=> proposalDeadline(pId) == 0) && - proposalSnapshot(pId) < proposalDeadline(pId) + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) + && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) /** * A proposal cannot be both executed and canceled. From 5ea1cc7a8ad1077d54572eb2aabf987f60e7c016 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 15:57:19 +0200 Subject: [PATCH 062/254] added invariants if executed or canceled always revert --- certora/specs/GovernorBase.spec | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 69d1fd85e..41ec3e735 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -40,6 +40,18 @@ invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.number < p */ invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) +/* + * No functions should be allowed to run after a job is deemed as canceled + */ +invariant cannotSetIfCanceled(uint256 pId) + isCanceled(pId) => lastReverted == true + +/* + * No functions should be allowed to run after a job is deemed as executed + */ +invariant cannotSetIfExecuted(uint256 pId) + isExecuted(pId) => lastReverted == true + ////////////////////////////////////////////////////////////////////////////// From d5c6520e4d73dd4f841cdba1be2068fb27cde593 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 15:57:53 +0200 Subject: [PATCH 063/254] idea for sum of votes --- certora/harnesses/GovernorHarness.sol | 54 ++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index 28e556556..27a5598e1 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -64,6 +64,19 @@ contract GovernorHarness is Governor { return _votingPeriod; } + constructor(string memory name) Governor(name) {} + + // _countVots == Sum of castVote + // + // RHS: + // 1) use counter_vote_power as a counter + // 2) use counter_vote_power as a temp var for a ghost + // + // LHS: + // mapping of count + // countMap + + mapping(uint256 => mapping(address => uint256)) counted_weight_by_id; function _countVote( uint256 proposalId, @@ -71,9 +84,46 @@ contract GovernorHarness is Governor { uint8 support, uint256 weight ) internal override virtual { - // havoc something + counted_weight_by_id[proposalId][account] += weight; } - constructor(string memory name) Governor(name) {} + mapping(uint256 => uint256) counter_vote_power_by_id; + + function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { + address voter = _msgSender(); + // 1) + counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, ""); + return _castVote(proposalId, voter, support, ""); + // 2) + // counter_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); + // return counter_vote_power; + } + + function castVoteWithReason( + uint256 proposalId, + uint8 support, + string calldata reason + ) public virtual override returns (uint256) { + address voter = _msgSender(); + counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, reason); + return _castVote(proposalId, voter, support, reason); + } + + function castVoteBySig( + uint256 proposalId, + uint8 support, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override returns (uint256) { + address voter = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))), + v, + r, + s + ); + counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, ""); + return _castVote(proposalId, voter, support, ""); + } } \ No newline at end of file From 2761ec0b6678f8cfe0dd17421ee860a8caf7dd5b Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 8 Nov 2021 17:18:36 +0200 Subject: [PATCH 064/254] MoreRulesToTheGodOfRules --- certora/harnesses/GovernorHarness.sol | 32 +++++++++++++++------ certora/scripts/GovernorCountingSimple.sh | 8 +++++- certora/specs/GovernorBase.spec | 34 +++++++++++++++++++++++ 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index 27a5598e1..880f706d6 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -10,6 +10,15 @@ contract GovernorHarness is Governor { return _proposals[proposalId].canceled; } + + function initialized(uint256 proposalId) public view returns (bool){ + if (_proposals[proposalId].voteStart._deadline != 0 && _proposals[proposalId].voteEnd._deadline != 0) { + return true; + } + return false; + } + + mapping(uint256 => uint256) _quorum; function quorum(uint256 blockNumber) public view override virtual returns (uint256) { @@ -64,6 +73,7 @@ contract GovernorHarness is Governor { return _votingPeriod; } + constructor(string memory name) Governor(name) {} // _countVots == Sum of castVote @@ -76,28 +86,32 @@ contract GovernorHarness is Governor { // mapping of count // countMap - mapping(uint256 => mapping(address => uint256)) counted_weight_by_id; + mapping(uint256 => uint256) counted_weight; + // uint decision; + // uint numberOfOptions; function _countVote( uint256 proposalId, address account, uint8 support, uint256 weight ) internal override virtual { - counted_weight_by_id[proposalId][account] += weight; + counted_weight[proposalId] += weight; } - - mapping(uint256 => uint256) counter_vote_power_by_id; + mapping(uint256 => uint256) public counter_vote_power_by_id; + mapping(uint256 => uint256) public ghost_vote_power_by_id; function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { address voter = _msgSender(); - // 1) - counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, ""); - return _castVote(proposalId, voter, support, ""); // 2) - // counter_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); - // return counter_vote_power; + ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); + + // 1) + counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; + + // return _castVote(proposalId, voter, support, ""); + return ghost_vote_power_by_id[proposalId]; } function castVoteWithReason( diff --git a/certora/scripts/GovernorCountingSimple.sh b/certora/scripts/GovernorCountingSimple.sh index 95c2c2551..da013a4ef 100755 --- a/certora/scripts/GovernorCountingSimple.sh +++ b/certora/scripts/GovernorCountingSimple.sh @@ -1,2 +1,8 @@ certoraRun certora/harnesses/GovernorCountingSimpleHarness.sol \ - --verify GovernorCountingSimpleHarness:certora/specs/Privileged.spec \ No newline at end of file + --verify GovernorCountingSimpleHarness:certora/specs/GovernorBase.spec \ + --solc solc8.0 \ + --staging \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule doubleVoting \ + --msg "$1" diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 41ec3e735..ba8b9c93e 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -5,6 +5,11 @@ methods { hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree isExecuted(uint256) returns bool envfree isCanceled(uint256) returns bool envfree + initialized(uint256) returns bool envfree + + hasVoted(uint256, address) returns bool + + castVote(uint256, uint8) returns uint256 // internal functions made public in harness: _quorumReached(uint256) returns bool envfree @@ -23,6 +28,12 @@ methods { invariant voteStartBeforeVoteEnd(uint256 pId) (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) + /* + proposalSnapshot(pId) < proposalDeadline(pId) || (proposalSnapshot(pId) == 0 && proposalDeadline(pId) == 0) + { preserved { + require initialized(pId) == true; + }} + */ /** * A proposal cannot be both executed and canceled. @@ -116,3 +127,26 @@ rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { assert _voteStart == voteStart_; assert _voteEnd == voteEnd_; } + +/** +* Check if it's possible to vote two time. Relevant to GovernorCountingSimpleHarness.sol contract +*/ +rule doubleVoting(uint256 pId, uint8 sup) { + env e; + address user = e.msg.sender; + + bool votedCheck = hasVoted(e, pId, user); + require votedCheck == true; + + castVote@withrevert(e, pId, sup); + bool reverted = lastReverted; + + assert reverted, "double voting accured"; +} + +/** +* +*/ +rule votingSumAndPower(uint256 pId, uint8 sup, method f) { + +} From 53d400680610e7dd240a5aebd3a68f603799265d Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 17:51:28 +0200 Subject: [PATCH 065/254] fixed function revert if executed --- certora/specs/GovernorBase.spec | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index ba8b9c93e..ac1dfb6b3 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -61,7 +61,13 @@ invariant cannotSetIfCanceled(uint256 pId) * No functions should be allowed to run after a job is deemed as executed */ invariant cannotSetIfExecuted(uint256 pId) - isExecuted(pId) => lastReverted == true + isExecuted(pId) => lastReverted == true + { + preserved execute(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) with (env e) + { + require(isExecuted(pId)); + } + } From 8ed7f965bb54b8462de3be6629876d12b991588c Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 17:57:53 +0200 Subject: [PATCH 066/254] added ghost and counter implementation for castWithReason and castBySig --- certora/harnesses/GovernorHarness.sol | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index 880f706d6..e6182ff8e 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -120,8 +120,13 @@ contract GovernorHarness is Governor { string calldata reason ) public virtual override returns (uint256) { address voter = _msgSender(); - counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, reason); - return _castVote(proposalId, voter, support, reason); + // 2) + ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, reason); + + // 1) + counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; + + return ghost_vote_power_by_id[proposalId]; } function castVoteBySig( @@ -137,7 +142,12 @@ contract GovernorHarness is Governor { r, s ); - counter_vote_power_by_id[proposalId] += _castVote(proposalId, voter, support, ""); - return _castVote(proposalId, voter, support, ""); + // 2) + ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); + + // 1) + counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; + + return ghost_vote_power_by_id[proposalId]; } } \ No newline at end of file From 861fab858930e7643f3c15e099fadf15a1b7d3d0 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 19:00:22 +0200 Subject: [PATCH 067/254] ghosts and invariant unfinished --- certora/specs/GovernorBase.spec | 46 +++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index ac1dfb6b3..dd0df01cb 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -1,4 +1,6 @@ -// Governor.sol base definitions +////////////////////////////////////////////////////////////////////////////// +///////////////////// Governor.sol base definitions ////////////////////////// +////////////////////////////////////////////////////////////////////////////// methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart proposalDeadline(uint256) returns uint256 envfree @@ -14,8 +16,24 @@ methods { // internal functions made public in harness: _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree + + // getter for checking the sums + counter_vote_power_by_id(uint256) returns uint256 envfree + ghost_vote_power_by_id(uint256) returns uint256 envfree + counted_weight(uint256) returns uint256 envfree } +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// GHOSTS ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +ghost vote_power_ghost() returns uint256; + +hook Sstore ghost_vote_power_by_id[KEY uint256 pId] uint256 current_power STORAGE{ + havoc vote_power_ghost assuming vote_power_ghost@new() == vote_power_ghost@old() + current_power; +} + + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -38,18 +56,21 @@ invariant voteStartBeforeVoteEnd(uint256 pId) /** * A proposal cannot be both executed and canceled. */ -invariant noBothExecutedAndCanceled(uint256 pId) !isExecuted(pId) || !isCanceled(pId) +invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) /** * A proposal cannot be neither executed nor canceled before it starts */ -invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.number < proposalSnapshot(pId) - => !isExecuted(pId) && !isCanceled(pId) +invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) + e.block.number < proposalSnapshot(pId) + => !isExecuted(pId) && !isCanceled(pId) /** * A proposal could be executed only if quorum was reached and vote succeeded */ -invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) + isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) /* * No functions should be allowed to run after a job is deemed as canceled @@ -69,7 +90,13 @@ invariant cannotSetIfExecuted(uint256 pId) } } - +/* + * sum of all votes casted is equal to the sum of voting power of those who voted + */ +invariant SumOfVotesCastEqualSumOfPowerOfVoted(uint256 pId) + counted_weight(pId) == counter_vote_power_by_id(pId) && + counted_weight(pId) == vote_power_ghost && + counter_vote_power_by_id(pId) == vote_power_ghost ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////// RULES //////////////////////////////////// @@ -149,10 +176,3 @@ rule doubleVoting(uint256 pId, uint8 sup) { assert reverted, "double voting accured"; } - -/** -* -*/ -rule votingSumAndPower(uint256 pId, uint8 sup, method f) { - -} From 16e101bba9c7f1170be258a1987f365789db7949 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 8 Nov 2021 20:16:53 +0200 Subject: [PATCH 068/254] cannot set if executed and canceled as rules (not working) --- certora/specs/GovernorBase.spec | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index dd0df01cb..aa0bcfc5b 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -75,28 +75,28 @@ invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) /* * No functions should be allowed to run after a job is deemed as canceled */ -invariant cannotSetIfCanceled(uint256 pId) - isCanceled(pId) => lastReverted == true +rule cannotSetIfCanceled(uint256 pId, method f) filtered { f-> !f.isView }{ + require(isCanceled(pId)); + env e; calldataarg args; + f(e, args); + assert(isCanceled(pId) => lastReverted == true, "Function did not revert when canceled"); +} /* * No functions should be allowed to run after a job is deemed as executed */ -invariant cannotSetIfExecuted(uint256 pId) - isExecuted(pId) => lastReverted == true - { - preserved execute(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) with (env e) - { - require(isExecuted(pId)); - } - } +rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{ + require(isExecuted(pId)); + env e; calldataarg args; + f(e, args); + assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed"); +} /* * sum of all votes casted is equal to the sum of voting power of those who voted */ invariant SumOfVotesCastEqualSumOfPowerOfVoted(uint256 pId) - counted_weight(pId) == counter_vote_power_by_id(pId) && - counted_weight(pId) == vote_power_ghost && - counter_vote_power_by_id(pId) == vote_power_ghost + counted_weight(pId) == vote_power_ghost() ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////// RULES //////////////////////////////////// From bc9bbc2431d2bfbbd617d2c1e43618da2f7240d0 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 9 Nov 2021 11:18:23 +0200 Subject: [PATCH 069/254] FirstWizardHarness --- certora/harnesses/GovernorBasicHarness.sol | 124 +++++++++++++++++++++ certora/scripts/Governor.sh | 2 +- certora/scripts/GovernorBasic.sh | 8 ++ certora/specs/GovernorBase.spec | 7 +- 4 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 certora/harnesses/GovernorBasicHarness.sol create mode 100644 certora/scripts/GovernorBasic.sh diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol new file mode 100644 index 000000000..1b9dce2a6 --- /dev/null +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../contracts/governance/Governor.sol"; +import "../../contracts/governance/extensions/GovernorCountingSimple.sol"; +import "../../contracts/governance/extensions/GovernorVotes.sol"; +import "../../contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../contracts/governance/extensions/GovernorTimelockCompound.sol"; + +/* +Wizard options: +ERC20Votes +TimelockCompound +*/ + +contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockCompound { + constructor(ERC20Votes _token, ICompoundTimelock _timelock, string memory name, uint256 quorumFraction) + Governor(name) + GovernorVotes(_token) + GovernorVotesQuorumFraction(quorumFraction) + GovernorTimelockCompound(_timelock) + {} + + function isExecuted(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].executed; + } + + function isCanceled(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].canceled; + } + + uint256 _votingDelay; + + function votingDelay() public view override virtual returns (uint256) { + return _votingDelay; + } + + uint256 _votingPeriod; + + function votingPeriod() public view override virtual returns (uint256) { + return _votingPeriod; + } + +/* + function votingDelay() public pure override returns (uint256) { + return _votingDelay; + } + + + function votingPeriod() public pure override returns (uint256) { + return _votingPeriod; + } +*/ + + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function getVotes(address account, uint256 blockNumber) + public + view + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function state(uint256 proposalId) + public + view + override(Governor, GovernorTimelockCompound) + returns (ProposalState) + { + return super.state(proposalId); + } + + function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) + public + override(Governor, IGovernor) + returns (uint256) + { + return super.propose(targets, values, calldatas, description); + } + + function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockCompound) + { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockCompound) + returns (uint256) + { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() + internal + view + override(Governor, GovernorTimelockCompound) + returns (address) + { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, GovernorTimelockCompound) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index ccdf90203..d3b7f9b3f 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -4,5 +4,5 @@ certoraRun certora/harnesses/GovernorHarness.sol \ --staging \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule noExecuteOrCancelBeforeStarting \ + --rule voteStartBeforeVoteEnd \ --msg "$1" diff --git a/certora/scripts/GovernorBasic.sh b/certora/scripts/GovernorBasic.sh new file mode 100644 index 000000000..b720c28ec --- /dev/null +++ b/certora/scripts/GovernorBasic.sh @@ -0,0 +1,8 @@ +certoraRun certora/harnesses/GovernorBasicHarness.sol \ + --verify GovernorBasicHarness:certora/specs/GovernorBase.spec \ + --solc solc8.2 \ + --staging \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule doubleVoting \ + --msg "$1" \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index aa0bcfc5b..b0952a534 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -7,7 +7,7 @@ methods { hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree isExecuted(uint256) returns bool envfree isCanceled(uint256) returns bool envfree - initialized(uint256) returns bool envfree + // initialized(uint256) returns bool envfree hasVoted(uint256, address) returns bool @@ -176,3 +176,8 @@ rule doubleVoting(uint256 pId, uint8 sup) { assert reverted, "double voting accured"; } + +/** +* +*/ +//rule votingSumAndPower(uint256 pId, uint8 sup, method f) {} From 92744a195a518fdccc68a0ae88af0c1e7a2bb425 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 9 Nov 2021 15:10:07 +0200 Subject: [PATCH 070/254] specificSpecForSumRule --- certora/harnesses/GovernorBasicHarness.sol | 19 ++-- .../GovernorCountingSimple-counting.sh | 8 ++ certora/specs/GovernorBase.spec | 24 +---- certora/specs/GovernorCountingSimple.spec | 87 +++++++++++++++++++ contracts/governance/Governor.sol | 4 +- .../GovernorCompatibilityBravo.sol | 4 +- .../extensions/GovernorCountingSimple.sol | 2 +- 7 files changed, 114 insertions(+), 34 deletions(-) create mode 100644 certora/scripts/GovernorCountingSimple-counting.sh create mode 100644 certora/specs/GovernorCountingSimple.spec diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol index 1b9dce2a6..57cd3c58c 100644 --- a/certora/harnesses/GovernorBasicHarness.sol +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -41,16 +41,21 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes return _votingPeriod; } -/* - function votingDelay() public pure override returns (uint256) { - return _votingDelay; - } + mapping(uint256 => uint256) public ghost_sum_vote_power_by_id; - function votingPeriod() public pure override returns (uint256) { - return _votingPeriod; + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason + ) internal override virtual returns (uint256) { + + uint deltaWeight = super._castVote(proposalId, account, support, reason); //HARNESS + ghost_sum_vote_power_by_id[proposalId] += deltaWeight; + + return deltaWeight; } -*/ // The following functions are overrides required by Solidity. diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/GovernorCountingSimple-counting.sh new file mode 100644 index 000000000..2bea57198 --- /dev/null +++ b/certora/scripts/GovernorCountingSimple-counting.sh @@ -0,0 +1,8 @@ +certoraRun certora/harnesses/GovernorBasicHarness.sol \ + --verify GovernorBasicHarness:certora/specs/GovernorCountingSimple.spec \ + --solc solc8.2 \ + --staging \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule SumOfVotesCastEqualSumOfPowerOfVoted \ + --msg "$1" \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index b0952a534..dad38fc48 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -19,20 +19,10 @@ methods { // getter for checking the sums counter_vote_power_by_id(uint256) returns uint256 envfree - ghost_vote_power_by_id(uint256) returns uint256 envfree + ghost_sum_vote_power_by_id(uint256) returns uint256 envfree counted_weight(uint256) returns uint256 envfree } -////////////////////////////////////////////////////////////////////////////// -///////////////////////////////// GHOSTS ///////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -ghost vote_power_ghost() returns uint256; - -hook Sstore ghost_vote_power_by_id[KEY uint256 pId] uint256 current_power STORAGE{ - havoc vote_power_ghost assuming vote_power_ghost@new() == vote_power_ghost@old() + current_power; -} - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// @@ -92,11 +82,6 @@ rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{ assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed"); } -/* - * sum of all votes casted is equal to the sum of voting power of those who voted - */ -invariant SumOfVotesCastEqualSumOfPowerOfVoted(uint256 pId) - counted_weight(pId) == vote_power_ghost() ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////// RULES //////////////////////////////////// @@ -174,10 +159,5 @@ rule doubleVoting(uint256 pId, uint8 sup) { castVote@withrevert(e, pId, sup); bool reverted = lastReverted; - assert reverted, "double voting accured"; + assert votedCheck => reverted, "double voting accured"; } - -/** -* -*/ -//rule votingSumAndPower(uint256 pId, uint8 sup, method f) {} diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec new file mode 100644 index 000000000..197dae1c0 --- /dev/null +++ b/certora/specs/GovernorCountingSimple.spec @@ -0,0 +1,87 @@ +////////////////////////////////////////////////////////////////////////////// +///////////////////// Governor.sol base definitions ////////////////////////// +////////////////////////////////////////////////////////////////////////////// +methods { + proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart + proposalDeadline(uint256) returns uint256 envfree + hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree + isExecuted(uint256) returns bool envfree + isCanceled(uint256) returns bool envfree + // initialized(uint256) returns bool envfree + + hasVoted(uint256, address) returns bool + + castVote(uint256, uint8) returns uint256 + + // internal functions made public in harness: + _quorumReached(uint256) returns bool envfre + _voteSucceeded(uint256) returns bool envfree + + // getter for checking the sums + ghost_sum_vote_power_by_id(uint256) returns uint256 envfree +} + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// GHOSTS ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +ghost sum_all_votes_power() returns uint256 { + init_state axiom sum_all_votes_power() == 0; +} + +hook Sstore ghost_sum_vote_power_by_id[KEY uint256 pId] uint256 current_power (uint256 old_power) STORAGE{ + havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; +} + +ghost tracked_weight(uint256) returns uint256 { + init_state axiom forall uint256 p. tracked_weight(p) == 0; +} +ghost sum_tracked_weight() returns uint256 { + init_state axiom sum_tracked_weight() == 0; +} + +/* +function update_tracked_weights(uint256 pId, uint256 votes, uint256 old_votes) { + havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +}*/ + +hook Sstore _proposalVotes[KEY uint256 pId].againstVotes uint256 votes (uint256 old_votes) STORAGE { + //update_tracked_weights(pId, votes, old_votes); + havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +} + +hook Sstore _proposalVotes[KEY uint256 pId].forVotes uint256 votes (uint256 old_votes) STORAGE { + //update_tracked_weights(pId, votes, old_votes); + havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +} + +hook Sstore _proposalVotes[KEY uint256 pId].abstainVotes uint256 votes (uint256 old_votes) STORAGE { + //update_tracked_weights(pId, votes, old_votes); + havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////// INVARIANTS //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +/* + * sum of all votes casted is equal to the sum of voting power of those who voted, per each proposal + */ +invariant SumOfVotesCastEqualSumOfPowerOfVotedPerProposal(uint256 pId) + tracked_weight(pId) == ghost_sum_vote_power_by_id(pId) + +/* + * sum of all votes casted is equal to the sum of voting power of those who voted + */ +invariant SumOfVotesCastEqualSumOfPowerOfVoted() + sum_tracked_weight() == sum_all_votes_power() + diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index 39dcbbed9..f8bbb42c4 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -154,12 +154,12 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public + function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal /** * @dev Is the proposal successful or not. */ - function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public + function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal /** * @dev Register a vote with a given support and voting weight. diff --git a/contracts/governance/compatibility/GovernorCompatibilityBravo.sol b/contracts/governance/compatibility/GovernorCompatibilityBravo.sol index b05130186..20e507b61 100644 --- a/contracts/governance/compatibility/GovernorCompatibilityBravo.sol +++ b/contracts/governance/compatibility/GovernorCompatibilityBravo.sol @@ -245,7 +245,7 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp /** * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. */ - function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { + function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal ProposalDetails storage details = _proposalDetails[proposalId]; return quorum(proposalSnapshot(proposalId)) < details.forVotes; } @@ -253,7 +253,7 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp /** * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be scritly over the againstVotes. */ - function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { + function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal ProposalDetails storage details = _proposalDetails[proposalId]; return details.forVotes > details.againstVotes; } diff --git a/contracts/governance/extensions/GovernorCountingSimple.sol b/contracts/governance/extensions/GovernorCountingSimple.sol index b8c72ed9e..18647fc05 100644 --- a/contracts/governance/extensions/GovernorCountingSimple.sol +++ b/contracts/governance/extensions/GovernorCountingSimple.sol @@ -98,7 +98,7 @@ abstract contract GovernorCountingSimple is Governor { } else if (support == uint8(VoteType.For)) { proposalvote.forVotes += weight; } else if (support == uint8(VoteType.Abstain)) { - proposalvote.abstainVotes += weight; + // proposalvote.abstainVotes += weight; } else { revert("GovernorVotingSimple: invalid value for enum VoteType"); } From f8a54d2ae2632dc5d7a53bcae03232eefbd1c369 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 9 Nov 2021 15:16:44 +0200 Subject: [PATCH 071/254] RemovedInsertedBugForSumRule --- certora/specs/GovernorBase.spec | 2 +- contracts/governance/extensions/GovernorCountingSimple.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index dad38fc48..5b24bae2c 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -147,7 +147,7 @@ rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { } /** -* Check if it's possible to vote two time. Relevant to GovernorCountingSimpleHarness.sol contract +* A user cannot vote twice */ rule doubleVoting(uint256 pId, uint8 sup) { env e; diff --git a/contracts/governance/extensions/GovernorCountingSimple.sol b/contracts/governance/extensions/GovernorCountingSimple.sol index 18647fc05..b8c72ed9e 100644 --- a/contracts/governance/extensions/GovernorCountingSimple.sol +++ b/contracts/governance/extensions/GovernorCountingSimple.sol @@ -98,7 +98,7 @@ abstract contract GovernorCountingSimple is Governor { } else if (support == uint8(VoteType.For)) { proposalvote.forVotes += weight; } else if (support == uint8(VoteType.Abstain)) { - // proposalvote.abstainVotes += weight; + proposalvote.abstainVotes += weight; } else { revert("GovernorVotingSimple: invalid value for enum VoteType"); } From 9b4634bebe994e019a8e6b235c522df687d5c4ec Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Tue, 9 Nov 2021 21:09:54 +0200 Subject: [PATCH 072/254] FixedTypoInEnvfreeWord --- certora/specs/GovernorCountingSimple.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 197dae1c0..d3094896f 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -14,7 +14,7 @@ methods { castVote(uint256, uint8) returns uint256 // internal functions made public in harness: - _quorumReached(uint256) returns bool envfre + _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree // getter for checking the sums From 5267eaac81b7b2e31633b871ee507243d7270587 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Wed, 10 Nov 2021 15:07:34 +0200 Subject: [PATCH 073/254] Changed deltaWeight type from uint to uin256 --- certora/harnesses/GovernorBasicHarness.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol index 57cd3c58c..6a36475d4 100644 --- a/certora/harnesses/GovernorBasicHarness.sol +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -51,7 +51,7 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes string memory reason ) internal override virtual returns (uint256) { - uint deltaWeight = super._castVote(proposalId, account, support, reason); //HARNESS + uint256 deltaWeight = super._castVote(proposalId, account, support, reason); //HARNESS ghost_sum_vote_power_by_id[proposalId] += deltaWeight; return deltaWeight; From b948e702589e6c3ea4b021e98373b20ad0e32b2e Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Wed, 10 Nov 2021 15:10:37 +0200 Subject: [PATCH 074/254] aesthetic --- certora/specs/GovernorBase.spec | 232 +++++++++++++++----------------- 1 file changed, 108 insertions(+), 124 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 5b24bae2c..9faa14f9f 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -7,6 +7,7 @@ methods { hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree isExecuted(uint256) returns bool envfree isCanceled(uint256) returns bool envfree + execute(address[], uint256[], bytes[], bytes32) returns uint256 // initialized(uint256) returns bool envfree hasVoted(uint256, address) returns bool @@ -17,147 +18,130 @@ methods { _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree - // getter for checking the sums - counter_vote_power_by_id(uint256) returns uint256 envfree - ghost_sum_vote_power_by_id(uint256) returns uint256 envfree - counted_weight(uint256) returns uint256 envfree } - - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -/** - * A proposal cannot end unless it started. - */ -//invariant voteStartBeforeVoteEnd1(uint256 pId) proposalSnapshot(pId) < proposalDeadline(pId) -invariant voteStartBeforeVoteEnd(uint256 pId) + /* + * A proposal cannot end unless it started. + */ + invariant voteStartBeforeVoteEnd(uint256 pId) (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) - /* - proposalSnapshot(pId) < proposalDeadline(pId) || (proposalSnapshot(pId) == 0 && proposalDeadline(pId) == 0) - { preserved { - require initialized(pId) == true; - }} - */ - -/** - * A proposal cannot be both executed and canceled. - */ -invariant noBothExecutedAndCanceled(uint256 pId) - !isExecuted(pId) || !isCanceled(pId) - -/** - * A proposal cannot be neither executed nor canceled before it starts - */ -invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) - e.block.number < proposalSnapshot(pId) - => !isExecuted(pId) && !isCanceled(pId) - -/** - * A proposal could be executed only if quorum was reached and vote succeeded - */ -invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) - isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) - -/* - * No functions should be allowed to run after a job is deemed as canceled - */ -rule cannotSetIfCanceled(uint256 pId, method f) filtered { f-> !f.isView }{ - require(isCanceled(pId)); - env e; calldataarg args; - f(e, args); - assert(isCanceled(pId) => lastReverted == true, "Function did not revert when canceled"); -} - -/* - * No functions should be allowed to run after a job is deemed as executed - */ -rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{ - require(isExecuted(pId)); - env e; calldataarg args; - f(e, args); - assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed"); -} -////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////// RULES //////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// + /* + * A proposal cannot be both executed and canceled. + */ + invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) -/** - * The voting must start not before the proposal’s creation time - */ -rule noStartBeforeCreation(uint256 pId) { - uint previousStart = proposalSnapshot(pId); - require previousStart == 0; - env e; - calldataarg arg; - propose(e, arg); - - uint newStart = proposalSnapshot(pId); - // if created, start is after creation - assert newStart != 0 => newStart >= e.block.number; -} - -/** - * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) - */ - /* -rule checkHashProposal { - address[] t1; - address[] t2; - uint256[] v1; - uint256[] v2; - bytes[] c1; - bytes[] c2; - bytes32 d1; - bytes32 d2; - - uint256 h1 = hashProposal(t1,v1,c1,d1); - uint256 h2 = hashProposal(t2,v2,c2,d2); - bool equalHashes = h1 == h2; - assert equalHashes => t1.length == t2.length; - assert equalHashes => v1.length == v2.length; - assert equalHashes => c1.length == c2.length; - assert equalHashes => d1 == d2; -} -*/ + /* + * A proposal cannot be neither executed nor canceled before it starts + */ + invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) + e.block.number < proposalSnapshot(pId) + => !isExecuted(pId) && !isCanceled(pId) -/** - * Once a proposal is created, voteStart and voteEnd are immutable - */ -rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalSnapshot(pId); - uint _voteEnd = proposalDeadline(pId); - require _voteStart > 0; // proposal was created + /* + * A proposal could be executed only if quorum was reached and vote succeeded + */ + invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) + isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) - env e; - calldataarg arg; - f(e, arg); - uint voteStart_ = proposalSnapshot(pId); - uint voteEnd_ = proposalDeadline(pId); - assert _voteStart == voteStart_; - assert _voteEnd == voteEnd_; -} + ////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////// RULES //////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + /* + * The voting must start not before the proposal’s creation time + */ + rule noStartBeforeCreation(uint256 pId) { + uint previousStart = proposalSnapshot(pId); + require previousStart == 0; + env e; + calldataarg arg; + propose(e, arg); -/** -* A user cannot vote twice -*/ -rule doubleVoting(uint256 pId, uint8 sup) { - env e; - address user = e.msg.sender; + uint newStart = proposalSnapshot(pId); + // if created, start is after creation + assert(newStart != 0 => newStart >= e.block.number); + } - bool votedCheck = hasVoted(e, pId, user); - require votedCheck == true; - castVote@withrevert(e, pId, sup); - bool reverted = lastReverted; + /* + * Once a proposal is created, voteStart and voteEnd are immutable + */ + rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint _voteStart = proposalSnapshot(pId); + uint _voteEnd = proposalDeadline(pId); + require _voteStart > 0; // proposal was created - assert votedCheck => reverted, "double voting accured"; -} + env e; + calldataarg arg; + f(e, arg); + + uint voteStart_ = proposalSnapshot(pId); + uint voteEnd_ = proposalDeadline(pId); + assert _voteStart == voteStart_; + assert _voteEnd == voteEnd_; + } + + + /* + * A user cannot vote twice + */ + rule doubleVoting(uint256 pId, uint8 sup) { + env e; + address user = e.msg.sender; + + bool votedCheck = hasVoted(e, pId, user); + require votedCheck == true; + + castVote@withrevert(e, pId, sup); + bool reverted = lastReverted; + + assert votedCheck => reverted, "double voting accured"; + } + + + /* + * When a proposal is created the start and end date are created in the future. + */ + rule proposalCreatedStartAndEndDateInFuture(address[] targets, uint256[] values, bytes[] calldatas){ + env e; + uint256 pId = callPropose(e, targets, values, calldatas); + uint256 startDate = proposalSnapshot(pId); + uint256 endDate = proposalDeadline(pId); + assert(startDate >= e.block.number && endDate >= e.block.number, "Start or end date were set in the past"); + } + + + /** + * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) + */ + /* + rule checkHashProposal { + address[] t1; + address[] t2; + uint256[] v1; + uint256[] v2; + bytes[] c1; + bytes[] c2; + bytes32 d1; + bytes32 d2; + + uint256 h1 = hashProposal(t1,v1,c1,d1); + uint256 h2 = hashProposal(t2,v2,c2,d2); + bool equalHashes = h1 == h2; + assert equalHashes => t1.length == t2.length; + assert equalHashes => v1.length == v2.length; + assert equalHashes => c1.length == c2.length; + assert equalHashes => d1 == d2; + } + */ From 0d724ca892e3bdd0887067cffefe29a2d8144e49 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Wed, 10 Nov 2021 15:26:29 +0200 Subject: [PATCH 075/254] Cleaned harness + callPropose --- certora/harnesses/GovernorHarness.sol | 74 +++++---------------------- 1 file changed, 14 insertions(+), 60 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index e6182ff8e..fce0c5c18 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -74,6 +74,16 @@ contract GovernorHarness is Governor { } + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal override virtual { + // havoc something + } + + constructor(string memory name) Governor(name) {} // _countVots == Sum of castVote @@ -86,68 +96,12 @@ contract GovernorHarness is Governor { // mapping of count // countMap - mapping(uint256 => uint256) counted_weight; - // uint decision; // uint numberOfOptions; - function _countVote( - uint256 proposalId, - address account, - uint8 support, - uint256 weight - ) internal override virtual { - counted_weight[proposalId] += weight; - } - mapping(uint256 => uint256) public counter_vote_power_by_id; - mapping(uint256 => uint256) public ghost_vote_power_by_id; - - function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { - address voter = _msgSender(); - // 2) - ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); - - // 1) - counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; - - // return _castVote(proposalId, voter, support, ""); - return ghost_vote_power_by_id[proposalId]; - } - - function castVoteWithReason( - uint256 proposalId, - uint8 support, - string calldata reason - ) public virtual override returns (uint256) { - address voter = _msgSender(); - // 2) - ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, reason); - - // 1) - counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; - - return ghost_vote_power_by_id[proposalId]; - } - - function castVoteBySig( - uint256 proposalId, - uint8 support, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual override returns (uint256) { - address voter = ECDSA.recover( - _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))), - v, - r, - s - ); - // 2) - ghost_vote_power_by_id[proposalId] = _castVote(proposalId, voter, support, ""); - - // 1) - counter_vote_power_by_id[proposalId] += ghost_vote_power_by_id[proposalId]; - - return ghost_vote_power_by_id[proposalId]; + function callPropose(address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas) public virtual returns (uint256) { + return super.propose(targets, values, calldatas, ""); } } \ No newline at end of file From 0598a3ac432781288e9a640ee15899d99f4a2627 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Thu, 11 Nov 2021 17:28:22 +0200 Subject: [PATCH 076/254] CountingSimpleMoreCleanAndAddedMoreRules --- certora/specs/GovernorBase.spec | 253 +++++++++++++--------- certora/specs/GovernorCountingSimple.spec | 31 +-- 2 files changed, 151 insertions(+), 133 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 9faa14f9f..ba4565ad0 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -1,6 +1,7 @@ ////////////////////////////////////////////////////////////////////////////// ///////////////////// Governor.sol base definitions ////////////////////////// ////////////////////////////////////////////////////////////////////////////// + methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart proposalDeadline(uint256) returns uint256 envfree @@ -17,131 +18,175 @@ methods { // internal functions made public in harness: _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree - } + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// - /* - * A proposal cannot end unless it started. - */ - invariant voteStartBeforeVoteEnd(uint256 pId) - (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) - && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) +/* + * A proposal cannot end unless it started. + */ +invariant voteStartBeforeVoteEnd(uint256 pId) + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) + && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) - /* - * A proposal cannot be both executed and canceled. - */ - invariant noBothExecutedAndCanceled(uint256 pId) - !isExecuted(pId) || !isCanceled(pId) +/** + * A proposal cannot be both executed and canceled. + */ +invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) - /* - * A proposal cannot be neither executed nor canceled before it starts - */ - invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) - e.block.number < proposalSnapshot(pId) - => !isExecuted(pId) && !isCanceled(pId) +/** + * A proposal could be executed only if quorum was reached and vote succeeded + */ +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) + isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) - /* - * A proposal could be executed only if quorum was reached and vote succeeded - */ - invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) - isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) - - - ////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////// RULES //////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// RULES //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// - /* - * The voting must start not before the proposal’s creation time - */ - rule noStartBeforeCreation(uint256 pId) { - uint previousStart = proposalSnapshot(pId); - require previousStart == 0; - env e; - calldataarg arg; - propose(e, arg); - uint newStart = proposalSnapshot(pId); - // if created, start is after creation - assert(newStart != 0 => newStart >= e.block.number); - } +/* + * No functions should be allowed to run after a job is deemed as canceled + */ +rule cannotSetIfCanceled(uint256 pId, method f) filtered { f-> !f.isView }{ + require(isCanceled(pId)); + env e; calldataarg args; + f(e, args); + assert(isCanceled(pId) => lastReverted == true, "Function did not revert when canceled"); +} - /* - * Once a proposal is created, voteStart and voteEnd are immutable - */ - rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalSnapshot(pId); - uint _voteEnd = proposalDeadline(pId); - require _voteStart > 0; // proposal was created - - env e; - calldataarg arg; - f(e, arg); - - uint voteStart_ = proposalSnapshot(pId); - uint voteEnd_ = proposalDeadline(pId); - assert _voteStart == voteStart_; - assert _voteEnd == voteEnd_; - } +/* + * No functions should be allowed to run after a job is deemed as executed + */ +rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{ + require(isExecuted(pId)); + env e; calldataarg args; + f(e, args); + assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed"); +} - /* - * A user cannot vote twice - */ - rule doubleVoting(uint256 pId, uint8 sup) { - env e; - address user = e.msg.sender; +/* + * The voting must start not before the proposal’s creation time + */ +rule noStartBeforeCreation(uint256 pId) { + uint previousStart = proposalSnapshot(pId); + require previousStart == 0; + env e; + calldataarg arg; + propose(e, arg); - bool votedCheck = hasVoted(e, pId, user); - require votedCheck == true; - - castVote@withrevert(e, pId, sup); - bool reverted = lastReverted; - - assert votedCheck => reverted, "double voting accured"; - } + uint newStart = proposalSnapshot(pId); + // if created, start is after creation + assert(newStart != 0 => newStart >= e.block.number); +} - /* - * When a proposal is created the start and end date are created in the future. - */ - rule proposalCreatedStartAndEndDateInFuture(address[] targets, uint256[] values, bytes[] calldatas){ - env e; - uint256 pId = callPropose(e, targets, values, calldatas); - uint256 startDate = proposalSnapshot(pId); - uint256 endDate = proposalDeadline(pId); - assert(startDate >= e.block.number && endDate >= e.block.number, "Start or end date were set in the past"); - } +/* + * Once a proposal is created, voteStart and voteEnd are immutable + */ +rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint _voteStart = proposalSnapshot(pId); + uint _voteEnd = proposalDeadline(pId); + require _voteStart > 0; // proposal was created + + env e; + calldataarg arg; + f(e, arg); + + uint voteStart_ = proposalSnapshot(pId); + uint voteEnd_ = proposalDeadline(pId); + assert _voteStart == voteStart_; + assert _voteEnd == voteEnd_; +} - /** - * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) - */ - /* - rule checkHashProposal { - address[] t1; - address[] t2; - uint256[] v1; - uint256[] v2; - bytes[] c1; - bytes[] c2; - bytes32 d1; - bytes32 d2; +/* + * A user cannot vote twice + */ +rule doubleVoting(uint256 pId, uint8 sup) { + env e; + address user = e.msg.sender; - uint256 h1 = hashProposal(t1,v1,c1,d1); - uint256 h2 = hashProposal(t2,v2,c2,d2); - bool equalHashes = h1 == h2; - assert equalHashes => t1.length == t2.length; - assert equalHashes => v1.length == v2.length; - assert equalHashes => c1.length == c2.length; - assert equalHashes => d1 == d2; - } - */ + bool votedCheck = hasVoted(e, pId, user); + + castVote@withrevert(e, pId, sup); + bool reverted = lastReverted; + + assert votedCheck => reverted, "double voting accured"; +} + + +/* + * When a proposal is created the start and end date are created in the future. + */ +rule proposalCreatedStartAndEndDateInFuture(address[] targets, uint256[] values, bytes[] calldatas){ + env e; + uint256 pId = callPropose(e, targets, values, calldatas); + uint256 startDate = proposalSnapshot(pId); + uint256 endDate = proposalDeadline(pId); + assert(startDate >= e.block.number && endDate >= e.block.number, "Start or end date were set in the past"); +} + + +/** + * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) + */ +/* +rule checkHashProposal { + address[] t1; + address[] t2; + uint256[] v1; + uint256[] v2; + bytes[] c1; + bytes[] c2; + bytes32 d1; + bytes32 d2; + + uint256 h1 = hashProposal(t1,v1,c1,d1); + uint256 h2 = hashProposal(t2,v2,c2,d2); + bool equalHashes = h1 == h2; + assert equalHashes => t1.length == t2.length; + assert equalHashes => v1.length == v2.length; + assert equalHashes => c1.length == c2.length; + assert equalHashes => d1 == d2; +} +*/ + + +/** + * A proposal cannot be neither executed nor canceled before it starts + */ +rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ + env e; + + require !isExecuted(pId) && !isCanceled(pId); + + calldataarg arg; + f(e, arg); + + assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; +} + +/** + * A proposal cannot be neither executed nor canceled before proposal's deadline + */ +rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ + env e; + + requireInvariant voteStartBeforeVoteEnd(pId); + require !isExecuted(pId) && !isCanceled(pId); + + calldataarg arg; + f(e, arg); + + assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; +} \ No newline at end of file diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index d3094896f..a0ae7b6d6 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -1,23 +1,6 @@ -////////////////////////////////////////////////////////////////////////////// -///////////////////// Governor.sol base definitions ////////////////////////// -////////////////////////////////////////////////////////////////////////////// +import "GovernorBase.spec" + methods { - proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart - proposalDeadline(uint256) returns uint256 envfree - hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree - isExecuted(uint256) returns bool envfree - isCanceled(uint256) returns bool envfree - // initialized(uint256) returns bool envfree - - hasVoted(uint256, address) returns bool - - castVote(uint256, uint8) returns uint256 - - // internal functions made public in harness: - _quorumReached(uint256) returns bool envfree - _voteSucceeded(uint256) returns bool envfree - - // getter for checking the sums ghost_sum_vote_power_by_id(uint256) returns uint256 envfree } @@ -40,29 +23,19 @@ ghost sum_tracked_weight() returns uint256 { init_state axiom sum_tracked_weight() == 0; } -/* -function update_tracked_weights(uint256 pId, uint256 votes, uint256 old_votes) { - havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; -}*/ - hook Sstore _proposalVotes[KEY uint256 pId].againstVotes uint256 votes (uint256 old_votes) STORAGE { - //update_tracked_weights(pId, votes, old_votes); havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; } hook Sstore _proposalVotes[KEY uint256 pId].forVotes uint256 votes (uint256 old_votes) STORAGE { - //update_tracked_weights(pId, votes, old_votes); havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; } hook Sstore _proposalVotes[KEY uint256 pId].abstainVotes uint256 votes (uint256 old_votes) STORAGE { - //update_tracked_weights(pId, votes, old_votes); havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; From 921c668a59f3d8f6b27e121cc1b28cd162249a7b Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Fri, 12 Nov 2021 09:02:51 +0200 Subject: [PATCH 077/254] reorganization + violated rules --- certora/specs/GovernorBase.spec | 281 ++++++++++++++++++-------------- 1 file changed, 162 insertions(+), 119 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index ba4565ad0..4cea34323 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -18,124 +18,169 @@ methods { // internal functions made public in harness: _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree + } -////////////////////////////////////////////////////////////////////////////// -////////////////////////////// INVARIANTS //////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - - /* - * A proposal cannot end unless it started. - */ -invariant voteStartBeforeVoteEnd(uint256 pId) - (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) - && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////// State Diagram ////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // // + // castVote(s)() // + // ------------- propose() ---------------------- time pass --------------- time passes ----------- // + // | No Proposal | --------> | Before Start (Delay) | --------> | Voting Period | ----------------------> | execute() | // + // ------------- ---------------------- --------------- -> Executed/Canceled ----------- // + // ------------------------------------------------------------|---------------|-------------------------|--------------> // + // t start end timelock // + // // + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +*/ -/** - * A proposal cannot be both executed and canceled. - */ -invariant noBothExecutedAndCanceled(uint256 pId) - !isExecuted(pId) || !isCanceled(pId) + +/////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// Global Valid States ///////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + + // not complete + /* + * If any of the properties are non zero, the rest has to be non zero + */ + // start !0 !0 !0 + // end = !0 !0 !0 + // exe = 0 0 1 1 + // can = 0 1 0 1 + invariant proposalInitiated(uint256 pId) + (proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0) && + (isCanceled(pId) => proposalSnapshot(pId) != 0) && + (isExecuted(pId) => proposalSnapshot(pId) != 0) + + // pass + /* + * A proposal cannot end unless it started. + */ + invariant voteStartBeforeVoteEnd(uint256 pId) + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) + && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) -/** - * A proposal could be executed only if quorum was reached and vote succeeded - */ -invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) - isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) + // pass + /* + * A proposal cannot be both executed and canceled. + */ + invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) -////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////// RULES //////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/* - * No functions should be allowed to run after a job is deemed as canceled - */ -rule cannotSetIfCanceled(uint256 pId, method f) filtered { f-> !f.isView }{ - require(isCanceled(pId)); - env e; calldataarg args; - f(e, args); - assert(isCanceled(pId) => lastReverted == true, "Function did not revert when canceled"); -} +/////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// In-State Rules ///////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +//========================================== +//------------- Voting Period -------------- +//========================================== + + // pass + /* + * A user cannot vote twice + */ + rule doubleVoting(uint256 pId, uint8 sup) { + env e; + address user = e.msg.sender; + + bool votedCheck = hasVoted(e, pId, user); + require votedCheck == true; + + castVote@withrevert(e, pId, sup); + bool reverted = lastReverted; + + assert votedCheck => reverted, "double voting accured"; + } -/* - * No functions should be allowed to run after a job is deemed as executed - */ -rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{ - require(isExecuted(pId)); - env e; calldataarg args; - f(e, args); - assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed"); -} +/////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////// State Transitions Rules ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +//=========================================== +//-------- Propose() --> End of Time -------- +//=========================================== + + // pass + /* + * The voting must start not before the proposal’s creation time + */ + rule noStartBeforeCreation(uint256 pId) { + uint256 previousStart = proposalSnapshot(pId); + require previousStart == 0; + env e; + calldataarg arg; + propose(e, arg); + + uint newStart = proposalSnapshot(pId); + // if created, start is after current block number (creation block) + assert(newStart != previousStart => newStart >= e.block.number); + } -/* - * The voting must start not before the proposal’s creation time - */ -rule noStartBeforeCreation(uint256 pId) { - uint previousStart = proposalSnapshot(pId); - require previousStart == 0; - env e; - calldataarg arg; - propose(e, arg); + // pass + /* + * Once a proposal is created, voteStart and voteEnd are immutable + */ + rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint _voteStart = proposalSnapshot(pId); + uint _voteEnd = proposalDeadline(pId); + require _voteStart > 0; // proposal was created - relation proved in noStartBeforeCreation - uint newStart = proposalSnapshot(pId); - // if created, start is after creation - assert(newStart != 0 => newStart >= e.block.number); -} + env e; + calldataarg arg; + f(e, arg); + uint voteStart_ = proposalSnapshot(pId); + uint voteEnd_ = proposalDeadline(pId); + assert _voteStart == voteStart_; + assert _voteEnd == voteEnd_; + } -/* - * Once a proposal is created, voteStart and voteEnd are immutable - */ -rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalSnapshot(pId); - uint _voteEnd = proposalDeadline(pId); - require _voteStart > 0; // proposal was created + // pass + /* + * A proposal cannot be neither executed nor canceled before it starts + */ + rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ + env e; - env e; - calldataarg arg; - f(e, arg); + require !isExecuted(pId) && !isCanceled(pId); - uint voteStart_ = proposalSnapshot(pId); - uint voteEnd_ = proposalDeadline(pId); - assert _voteStart == voteStart_; - assert _voteEnd == voteEnd_; -} + calldataarg arg; + f(e, arg); + assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; + } -/* - * A user cannot vote twice - */ -rule doubleVoting(uint256 pId, uint8 sup) { - env e; - address user = e.msg.sender; +//============================================ +//--- End of Voting Period --> End of Time --- +//============================================ - bool votedCheck = hasVoted(e, pId, user); + // pass + /* + * A proposal cannot be neither executed nor canceled before proposal's deadline + */ + rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ + env e; - castVote@withrevert(e, pId, sup); - bool reverted = lastReverted; + requireInvariant voteStartBeforeVoteEnd(pId); + require !isExecuted(pId) && !isCanceled(pId); - assert votedCheck => reverted, "double voting accured"; -} + calldataarg arg; + f(e, arg); + assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before deadline"; + } -/* - * When a proposal is created the start and end date are created in the future. - */ -rule proposalCreatedStartAndEndDateInFuture(address[] targets, uint256[] values, bytes[] calldatas){ - env e; - uint256 pId = callPropose(e, targets, values, calldatas); - uint256 startDate = proposalSnapshot(pId); - uint256 endDate = proposalDeadline(pId); - assert(startDate >= e.block.number && endDate >= e.block.number, "Start or end date were set in the past"); -} - +//////////////////////////////////////////////////////////////////////////////// +////////////////////// Integrity Of Functions (Unit Tests) ///////////////////// +//////////////////////////////////////////////////////////////////////////////// /** * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) @@ -161,32 +206,30 @@ rule checkHashProposal { } */ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// High Level Rules ////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// -/** - * A proposal cannot be neither executed nor canceled before it starts - */ -rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ - env e; + // not passing + /* + * all non-view functions should revert if proposal is executed + */ + rule allFunctionsRevertIfExecuted(uint256 pId, method f) filtered { f -> !f.isView } { + env e; calldataarg args; + require(isExecuted(pId)); + f@withrevert(e,args); + assert(lastReverted == true, "Function was not reverted"); + } - require !isExecuted(pId) && !isCanceled(pId); - - calldataarg arg; - f(e, arg); - - assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; -} - -/** - * A proposal cannot be neither executed nor canceled before proposal's deadline - */ -rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ - env e; - - requireInvariant voteStartBeforeVoteEnd(pId); - require !isExecuted(pId) && !isCanceled(pId); - - calldataarg arg; - f(e, arg); - - assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; -} \ No newline at end of file + // not passing + /* + * all non-view functions should revert if proposal is canceled + */ + rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView } { + env e; calldataarg args; + uint256 pId = e.msg.value; + require(isCanceled(pId)); + requireInvariant noBothExecutedAndCanceled(pId); + f@withrevert(e,args); + assert(lastReverted == true, "Function was not reverted"); + } \ No newline at end of file From a858ed7a2af2932340894ed16790038ed2c6115a Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov <58052996+RedLikeRosesss@users.noreply.github.com> Date: Fri, 12 Nov 2021 14:12:03 +0200 Subject: [PATCH 078/254] codeCleaningNumberIDontKnow --- certora/specs/GovernorBase.spec | 229 ++++++++++++++++---------------- 1 file changed, 115 insertions(+), 114 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 4cea34323..88fe278f8 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -37,41 +37,45 @@ methods { */ - /////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// Global Valid States ///////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// - // not complete - /* - * If any of the properties are non zero, the rest has to be non zero - */ - // start !0 !0 !0 - // end = !0 !0 !0 - // exe = 0 0 1 1 - // can = 0 1 0 1 - invariant proposalInitiated(uint256 pId) - (proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0) && - (isCanceled(pId) => proposalSnapshot(pId) != 0) && - (isExecuted(pId) => proposalSnapshot(pId) != 0) - // pass - /* - * A proposal cannot end unless it started. - */ - invariant voteStartBeforeVoteEnd(uint256 pId) - (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) - && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) +/* + * If any of the properties are non zero, the rest has to be non zero + */ + // start !0 !0 !0 + // end = !0 !0 !0 + // exe = 0 0 1 1 + // can = 0 1 0 1 +invariant proposalInitiated(uint256 pId) + (proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0) && + (isCanceled(pId) => proposalSnapshot(pId) != 0) && + (isExecuted(pId) => proposalSnapshot(pId) != 0) - // pass - /* - * A proposal cannot be both executed and canceled. - */ - invariant noBothExecutedAndCanceled(uint256 pId) - !isExecuted(pId) || !isCanceled(pId) +/* + * A proposal cannot end unless it started. + */ +invariant voteStartBeforeVoteEnd(uint256 pId) + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) + && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) +/* + * A proposal cannot be both executed and canceled. + */ +invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) + + +/** + * A proposal could be executed only if quorum was reached and vote succeeded + */ +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) + isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) + /////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// In-State Rules ///////////////////////////////////// @@ -81,22 +85,20 @@ methods { //------------- Voting Period -------------- //========================================== - // pass - /* - * A user cannot vote twice - */ - rule doubleVoting(uint256 pId, uint8 sup) { - env e; - address user = e.msg.sender; - bool votedCheck = hasVoted(e, pId, user); - require votedCheck == true; +/* + * A user cannot vote twice + */ +rule doubleVoting(uint256 pId, uint8 sup) { + env e; + address user = e.msg.sender; + bool votedCheck = hasVoted(e, pId, user); - castVote@withrevert(e, pId, sup); - bool reverted = lastReverted; + castVote@withrevert(e, pId, sup); + bool reverted = lastReverted; - assert votedCheck => reverted, "double voting accured"; - } + assert votedCheck => reverted, "double voting accured"; +} /////////////////////////////////////////////////////////////////////////////////////// @@ -107,76 +109,75 @@ methods { //-------- Propose() --> End of Time -------- //=========================================== - // pass - /* - * The voting must start not before the proposal’s creation time - */ - rule noStartBeforeCreation(uint256 pId) { - uint256 previousStart = proposalSnapshot(pId); - require previousStart == 0; - env e; - calldataarg arg; - propose(e, arg); - uint newStart = proposalSnapshot(pId); - // if created, start is after current block number (creation block) - assert(newStart != previousStart => newStart >= e.block.number); - } +/* + * The voting must start not before the proposal’s creation time + */ +rule noStartBeforeCreation(uint256 pId) { + uint256 previousStart = proposalSnapshot(pId); + require previousStart == 0; + env e; + calldataarg arg; + propose(e, arg); + + uint newStart = proposalSnapshot(pId); + // if created, start is after current block number (creation block) + assert(newStart != previousStart => newStart >= e.block.number); +} - // pass - /* - * Once a proposal is created, voteStart and voteEnd are immutable - */ - rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalSnapshot(pId); - uint _voteEnd = proposalDeadline(pId); - require _voteStart > 0; // proposal was created - relation proved in noStartBeforeCreation +/* + * Once a proposal is created, voteStart and voteEnd are immutable + */ +rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint _voteStart = proposalSnapshot(pId); + uint _voteEnd = proposalDeadline(pId); + require _voteStart > 0; // proposal was created - relation proved in noStartBeforeCreation - env e; - calldataarg arg; - f(e, arg); + env e; + calldataarg arg; + f(e, arg); - uint voteStart_ = proposalSnapshot(pId); - uint voteEnd_ = proposalDeadline(pId); - assert _voteStart == voteStart_; - assert _voteEnd == voteEnd_; - } + uint voteStart_ = proposalSnapshot(pId); + uint voteEnd_ = proposalDeadline(pId); + assert _voteStart == voteStart_; + assert _voteEnd == voteEnd_; +} - // pass - /* - * A proposal cannot be neither executed nor canceled before it starts - */ - rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ - env e; - require !isExecuted(pId) && !isCanceled(pId); +/* + * A proposal cannot be neither executed nor canceled before it starts + */ +rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ + env e; - calldataarg arg; - f(e, arg); + require !isExecuted(pId) && !isCanceled(pId); - assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; - } + calldataarg arg; + f(e, arg); + + assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; +} //============================================ //--- End of Voting Period --> End of Time --- //============================================ - // pass - /* - * A proposal cannot be neither executed nor canceled before proposal's deadline - */ - rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ - env e; - requireInvariant voteStartBeforeVoteEnd(pId); - require !isExecuted(pId) && !isCanceled(pId); +/* + * A proposal cannot be neither executed nor canceled before proposal's deadline + */ +rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ + env e; - calldataarg arg; - f(e, arg); + requireInvariant voteStartBeforeVoteEnd(pId); + require !isExecuted(pId) && !isCanceled(pId); - assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before deadline"; - } + calldataarg arg; + f(e, arg); + + assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before deadline"; +} //////////////////////////////////////////////////////////////////////////////// ////////////////////// Integrity Of Functions (Unit Tests) ///////////////////// @@ -210,26 +211,26 @@ rule checkHashProposal { //////////////////////////////// High Level Rules ////////////////////////////// //////////////////////////////////////////////////////////////////////////////// - // not passing - /* - * all non-view functions should revert if proposal is executed - */ - rule allFunctionsRevertIfExecuted(uint256 pId, method f) filtered { f -> !f.isView } { - env e; calldataarg args; - require(isExecuted(pId)); - f@withrevert(e,args); - assert(lastReverted == true, "Function was not reverted"); - } - // not passing - /* - * all non-view functions should revert if proposal is canceled - */ - rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView } { - env e; calldataarg args; - uint256 pId = e.msg.value; - require(isCanceled(pId)); - requireInvariant noBothExecutedAndCanceled(pId); - f@withrevert(e,args); - assert(lastReverted == true, "Function was not reverted"); - } \ No newline at end of file +/* + * all non-view functions should revert if proposal is executed + */ +rule allFunctionsRevertIfExecuted(uint256 pId, method f) filtered { f -> !f.isView } { + env e; calldataarg args; + require(isExecuted(pId)); + f@withrevert(e,args); + assert(lastReverted == true, "Function was not reverted"); +} + + +/* + * all non-view functions should revert if proposal is canceled + */ +rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView } { + env e; calldataarg args; + uint256 pId = e.msg.value; + require(isCanceled(pId)); + requireInvariant noBothExecutedAndCanceled(pId); + f@withrevert(e,args); + assert(lastReverted == true, "Function was not reverted"); +} From 54fa59f87957222e5d9cc26a15f70695e02fcf95 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Sun, 14 Nov 2021 15:33:30 +0200 Subject: [PATCH 079/254] proposeInitialized done --- certora/scripts/GovernorBasic.sh | 5 +++-- certora/specs/GovernorBase.spec | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/certora/scripts/GovernorBasic.sh b/certora/scripts/GovernorBasic.sh index b720c28ec..8a6b48373 100644 --- a/certora/scripts/GovernorBasic.sh +++ b/certora/scripts/GovernorBasic.sh @@ -1,8 +1,9 @@ certoraRun certora/harnesses/GovernorBasicHarness.sol \ --verify GovernorBasicHarness:certora/specs/GovernorBase.spec \ --solc solc8.2 \ - --staging \ + --staging uri/add_with_env_to_preserved_all \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule doubleVoting \ + --disableLocalTypeChecking \ + --rule proposalInitiated \ --msg "$1" \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 88fe278f8..1a6580d40 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -47,12 +47,16 @@ methods { */ // start !0 !0 !0 // end = !0 !0 !0 - // exe = 0 0 1 1 - // can = 0 1 0 1 + // exe = 0 0 1 1 + // can = 0 1 0 1 invariant proposalInitiated(uint256 pId) (proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0) && (isCanceled(pId) => proposalSnapshot(pId) != 0) && (isExecuted(pId) => proposalSnapshot(pId) != 0) + { preserved with (env e){ + require e.block.number > 0; + }} + /* From c6365ef8683f320f233af4ddcaa9b928ebdfd399 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Sun, 14 Nov 2021 15:44:29 +0200 Subject: [PATCH 080/254] creating new ghost for 26 b --- .../GovernorCountingSimple-counting.sh | 2 +- certora/specs/GovernorCountingSimple.spec | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/GovernorCountingSimple-counting.sh index 2bea57198..efb5bb59a 100644 --- a/certora/scripts/GovernorCountingSimple-counting.sh +++ b/certora/scripts/GovernorCountingSimple-counting.sh @@ -4,5 +4,5 @@ certoraRun certora/harnesses/GovernorBasicHarness.sol \ --staging \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule SumOfVotesCastEqualSumOfPowerOfVoted \ + --rule OneIsNoMoreThanAll \ --msg "$1" \ No newline at end of file diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index a0ae7b6d6..f1b824512 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -2,6 +2,7 @@ import "GovernorBase.spec" methods { ghost_sum_vote_power_by_id(uint256) returns uint256 envfree + //_getVotes(address, uint256) returns uint256 } ////////////////////////////////////////////////////////////////////////////// @@ -41,6 +42,13 @@ hook Sstore _proposalVotes[KEY uint256 pId].abstainVotes uint256 votes (uint256 havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; } +/* +ghost totalVotesPossible() returns uint256{ + init_state axiom totalVotesPossible() == 0; +} + +hook Sstore _getVotes[KEY address pId][KEY uint256 blockNumber] uint256 voteWeight (uint old_voteWeight) STORAGE +*/ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -55,6 +63,16 @@ invariant SumOfVotesCastEqualSumOfPowerOfVotedPerProposal(uint256 pId) /* * sum of all votes casted is equal to the sum of voting power of those who voted */ -invariant SumOfVotesCastEqualSumOfPowerOfVoted() - sum_tracked_weight() == sum_all_votes_power() +// invariant SumOfVotesCastEqualSumOfPowerOfVoted() +// sum_tracked_weight() == sum_all_votes_power() +/* +* totalVoted >= vote(id) +*/ +invariant OneIsNotMoreThanAll(uint256 pId) + sum_all_votes_power() >= tracked_weight(pId) + +/* +* totalVotesPossible (supply/weight) >= votePower(id) +*/ +invariant possibleTotalVotes(uint pId) From c0a257fa0c78020f7163059da71d2f8c10277464 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 15 Nov 2021 17:56:30 +0200 Subject: [PATCH 081/254] overriding castVoteWithReason --- certora/harnesses/GovernorHarness.sol | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index fce0c5c18..3491871f9 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -104,4 +104,14 @@ contract GovernorHarness is Governor { bytes[] memory calldatas) public virtual returns (uint256) { return super.propose(targets, values, calldatas, ""); } + + uint256 public proposalid_global; + uint8 public support_global; + + function castVoteWithReason(uint256 proposalId, + uint8 support, string calldata reason) public virtual override returns (uint256){ + require(proposalId == proposalid_global); + require(support == support_global); + return super.castVoteWithReason(proposalId, support, reason); + } } \ No newline at end of file From eee306acda9540bafd4c3a258c59228e412157dc Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 15 Nov 2021 17:58:36 +0200 Subject: [PATCH 082/254] commenting helper function, executed only after exec, func revert if canceled or executed --- certora/specs/GovernorBase.spec | 74 ++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 1a6580d40..9ce360e17 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -14,13 +14,44 @@ methods { hasVoted(uint256, address) returns bool castVote(uint256, uint8) returns uint256 + // castVoteBySig(uint256,uint8,uint8,bytes32,bytes32) returns uint256 // internal functions made public in harness: _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree + // hashProposal(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) => uniqueHashGhost(descriptionHash) } +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// Ghosts //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +// ghost uniqueHashGhost(bytes32) returns uint256; + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Helper Functions /////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +/* +function callFunctionWithParams(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, uint256 proposalId, uint8 support, string reason, uint8 v, bytes32 r, bytes32 s, method f) { + env e; + if (f.selector == callPropose(address[], uint256[], bytes[]).selector) { + callPropose(e, targets, values, calldatas); + } else if (f.selector == execute(address[], uint256[], bytes[], bytes32).selector) { + execute(e, targets, values, calldatas, descriptionHash); + } else if (f.selector == castVote(uint256, uint8).selector) { + castVote(e, proposalId, support); + } else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) { + castVoteWithReason(e, proposalId, support, reason); + } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) { + castVoteBySig(e, proposalId, support, v, r, s); + } else { + calldataarg args; + f(e,args); + } +} +*/ + /* ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////// State Diagram ////////////////////////////////////////////////////////// @@ -45,17 +76,13 @@ methods { /* * If any of the properties are non zero, the rest has to be non zero */ - // start !0 !0 !0 - // end = !0 !0 !0 - // exe = 0 0 1 1 - // can = 0 1 0 1 invariant proposalInitiated(uint256 pId) (proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0) && (isCanceled(pId) => proposalSnapshot(pId) != 0) && (isExecuted(pId) => proposalSnapshot(pId) != 0) - { preserved with (env e){ + /*{ preserved with (env e){ require e.block.number > 0; - }} + }}*/ @@ -212,17 +239,21 @@ rule checkHashProposal { */ //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////// High Level Rules ////////////////////////////// +////////////////////////////// High Level Rules //////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /* * all non-view functions should revert if proposal is executed */ -rule allFunctionsRevertIfExecuted(uint256 pId, method f) filtered { f -> !f.isView } { +rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView } { env e; calldataarg args; + uint256 pId; uint8 sup; uint8 v; bytes32 r; bytes32 s ; require(isExecuted(pId)); - f@withrevert(e,args); + requireInvariant noBothExecutedAndCanceled(pId); + // f@withrevert(e,args); + // castVote@withrevert(e, pId, sup); + castVoteBySig@withrevert(e, pId, sup, v, r, s); assert(lastReverted == true, "Function was not reverted"); } @@ -232,9 +263,30 @@ rule allFunctionsRevertIfExecuted(uint256 pId, method f) filtered { f -> !f.isVi */ rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView } { env e; calldataarg args; - uint256 pId = e.msg.value; + address[] targets; uint256[] values; bytes[] calldatas; bytes32 descriptionHash; + uint256 pId = uniqueHashGhost(descriptionHash); + uint8 sup; uint8 v; bytes32 r; bytes32 s; + require(isCanceled(pId)); requireInvariant noBothExecutedAndCanceled(pId); - f@withrevert(e,args); + // castVote@withrevert(e, pId, sup); + // castVoteWithReason(e, pId, sup, ""); + // castVoteBySig@withrevert(e, pId, sup, v, r, s); + require(isCanceled(pId) => proposalSnapshot(pId) != 0); + execute@withrevert(e, targets, values, calldatas, descriptionHash); assert(lastReverted == true, "Function was not reverted"); } + +/* + * Shows that executed can only change due to execute() + */ +rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) filtered {f -> f.selector != (execute(address[], uint256[], bytes[], bytes32).selector)} { + env e; calldataarg args; + uint256 pId = hashProposal(targets, values, calldatas, descriptionHash); + bool executedBefore = isExecuted(pId); + require(!executedBefore); + f(e, args); + // execute(e, targets, values, calldatas, descriptionHash); + bool executedAfter = isExecuted(pId); + assert(executedAfter != executedBefore, "executed property did not change"); +} From d297280617b7a7adca059edd68a9a4cbea0afb95 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 15 Nov 2021 18:00:04 +0200 Subject: [PATCH 083/254] oneUserVoteInCast, noVotesForSomeoneElse --- certora/specs/GovernorCountingSimple.spec | 40 ++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index f1b824512..72a57c7f2 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -2,6 +2,8 @@ import "GovernorBase.spec" methods { ghost_sum_vote_power_by_id(uint256) returns uint256 envfree + // castVote(uint256, uint8) returns uint256 + // castVoteBySig(uint256,uint8,uint8,bytes32,bytes32) returns uint256 //_getVotes(address, uint256) returns uint256 } @@ -9,6 +11,15 @@ methods { ///////////////////////////////// GHOSTS ///////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// +ghost hasVoteGhost(uint256) returns uint256 { + init_state axiom forall uint256 pId. hasVoteGhost(pId) == 0; +} + +hook Sstore _proposalVotes[KEY uint256 pId].hasVoted[KEY address user] bool current_voting_State (bool old_voting_state) STORAGE{ + havoc hasVoteGhost assuming forall uint256 p. ((p == pId && current_voting_State && !old_voting_state) ? (hasVoteGhost@new(p) == hasVoteGhost@old(p) + 1) : + (hasVoteGhost@new(p) == hasVoteGhost@old(p))); +} + ghost sum_all_votes_power() returns uint256 { init_state axiom sum_all_votes_power() == 0; } @@ -75,4 +86,31 @@ invariant OneIsNotMoreThanAll(uint256 pId) /* * totalVotesPossible (supply/weight) >= votePower(id) */ -invariant possibleTotalVotes(uint pId) +// invariant possibleTotalVotes(uint pId) + +/* +rule someOtherRuleToRemoveLater(uint256 num){ + env e; calldataarg args; method f; + uint256 x = hasVoteGhost(num); + f(e, args); + assert(false); +} +*/ + + +rule oneUserVotesInCast(uint256 pId, method f) filtered { f -> ((f.selector == castVote(uint256, uint8).selector) || + f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) }{ + env e; calldataarg args; + uint256 ghost_Before = hasVoteGhost(pId); + f(e, args); + uint256 ghost_After = hasVoteGhost(pId); + assert(ghost_After == ghost_Before + 1, "Raised by more than 1"); +} + + +rule noVoteForSomeoneElse(uint256 pId, uint8 support){ + env e; + uint256 voter = e.msg.sender; + castVote(e, pId, support); + +} \ No newline at end of file From a16eaebb2525bac3e2d3d3f228f6a9823c8297c3 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 15 Nov 2021 18:22:36 +0200 Subject: [PATCH 084/254] ManyNonWorkingRules --- certora/harnesses/GovernorBasicHarness.sol | 10 ++ certora/harnesses/GovernorHarness.sol | 4 + .../GovernorCountingSimple-counting.sh | 2 +- certora/specs/GovernorCountingSimple.spec | 170 ++++++++++++++---- 4 files changed, 154 insertions(+), 32 deletions(-) diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol index 6a36475d4..db3f0818b 100644 --- a/certora/harnesses/GovernorBasicHarness.sol +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -21,6 +21,8 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes GovernorTimelockCompound(_timelock) {} + + function isExecuted(uint256 proposalId) public view returns (bool) { return _proposals[proposalId].executed; } @@ -57,6 +59,14 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes return deltaWeight; } + /* + mapping (address => mapping (uint256 => uint256)) _getVotes; + + function getVotesHarnness(address account, uint256 blockNumber) public { + _getVotes[account][blockNumber] = getVotes(account, blockNumber); + } + */ + // The following functions are overrides required by Solidity. function quorum(uint256 blockNumber) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index 3491871f9..225f488d5 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -10,6 +10,10 @@ contract GovernorHarness is Governor { return _proposals[proposalId].canceled; } + function snapshot(uint256 proposalId) public view returns (uint64) { + return _proposals[proposalId].voteStart._deadline; + } + function initialized(uint256 proposalId) public view returns (bool){ if (_proposals[proposalId].voteStart._deadline != 0 && _proposals[proposalId].voteEnd._deadline != 0) { diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/GovernorCountingSimple-counting.sh index efb5bb59a..26df57c7b 100644 --- a/certora/scripts/GovernorCountingSimple-counting.sh +++ b/certora/scripts/GovernorCountingSimple-counting.sh @@ -4,5 +4,5 @@ certoraRun certora/harnesses/GovernorBasicHarness.sol \ --staging \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule OneIsNoMoreThanAll \ + --rule hasVotedCorrelation \ --msg "$1" \ No newline at end of file diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 72a57c7f2..84d77f168 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -4,7 +4,13 @@ methods { ghost_sum_vote_power_by_id(uint256) returns uint256 envfree // castVote(uint256, uint8) returns uint256 // castVoteBySig(uint256,uint8,uint8,bytes32,bytes32) returns uint256 - //_getVotes(address, uint256) returns uint256 + + snapshot(uint256) returns uint64 envfree + quorum(uint256) returns uint256 envfree + proposalVotes(uint256) returns (uint256, uint256, uint256) envfree + + getVotes(address, uint256) returns uint256 envfree + //getVotes(address, uint256) => CONSTANT } ////////////////////////////////////////////////////////////////////////////// @@ -21,45 +27,78 @@ hook Sstore _proposalVotes[KEY uint256 pId].hasVoted[KEY address user] bool curr } ghost sum_all_votes_power() returns uint256 { - init_state axiom sum_all_votes_power() == 0; + init_state axiom sum_all_votes_power() == 0; } -hook Sstore ghost_sum_vote_power_by_id[KEY uint256 pId] uint256 current_power (uint256 old_power) STORAGE{ - havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; +hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE { + havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; } ghost tracked_weight(uint256) returns uint256 { - init_state axiom forall uint256 p. tracked_weight(p) == 0; + init_state axiom forall uint256 p. tracked_weight(p) == 0; } ghost sum_tracked_weight() returns uint256 { - init_state axiom sum_tracked_weight() == 0; + init_state axiom sum_tracked_weight() == 0; } -hook Sstore _proposalVotes[KEY uint256 pId].againstVotes uint256 votes (uint256 old_votes) STORAGE { - havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +ghost votesAgainst() returns uint256 { + init_state axiom votesAgainst() == 0; } -hook Sstore _proposalVotes[KEY uint256 pId].forVotes uint256 votes (uint256 old_votes) STORAGE { - havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +ghost votesFor() returns uint256 { + init_state axiom votesFor() == 0; } -hook Sstore _proposalVotes[KEY uint256 pId].abstainVotes uint256 votes (uint256 old_votes) STORAGE { - havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; +ghost votesAbstain() returns uint256 { + init_state axiom votesAbstain() == 0; +} + +hook Sstore _proposalVotes [KEY uint256 pId].againstVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesAgainst assuming votesAgainst@new() == votesAgainst@old() - old_votes + votes; +} + +hook Sstore _proposalVotes [KEY uint256 pId].forVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesFor assuming votesFor@new() == votesFor@old() - old_votes + votes; +} + +hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesAbstain assuming votesAbstain@new() == votesAbstain@old() - old_votes + votes; } /* -ghost totalVotesPossible() returns uint256{ - init_state axiom totalVotesPossible() == 0; +ghost totalVotesPossible(uint256) returns uint256 { + init_state axiom forall uint256 bn. totalVotesPossible(bn) == 0; } -hook Sstore _getVotes[KEY address pId][KEY uint256 blockNumber] uint256 voteWeight (uint old_voteWeight) STORAGE +ghost possibleMaxOfVoters(uint256) returns uint256{ + init_state axiom forall uint256 pId. possibleMaxOfVoters(pId) == 0; +} + +hook Sstore _getVotes[KEY address uId][KEY uint256 blockNumber] uint256 voteWeight(uint256 old_voteWeight) STORAGE { + havoc totalVotesPossible assuming forall uint256 bn. + (bn == blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn) - old_voteWeight + voteWeight) + && (bn != blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn)); + //somehow havoc possibleMaxOfVoters based on scheme. Probably can be implemented if previous havoc is possible +} */ +/* +_proposalVotes[pId].hasVoted[account] -> bool + ^ + | + go through all accounts, + if true => +possibleMaxOfVoters(pId) += getVotes(pId, acount); +*/ + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -68,25 +107,36 @@ hook Sstore _getVotes[KEY address pId][KEY uint256 blockNumber] uint256 voteWeig /* * sum of all votes casted is equal to the sum of voting power of those who voted, per each proposal */ -invariant SumOfVotesCastEqualSumOfPowerOfVotedPerProposal(uint256 pId) - tracked_weight(pId) == ghost_sum_vote_power_by_id(pId) - +invariant SumOfVotesCastEqualSumOfPowerOfVotedPerProposal(uint256 pId) + tracked_weight(pId) == ghost_sum_vote_power_by_id(pId) + /* * sum of all votes casted is equal to the sum of voting power of those who voted */ -// invariant SumOfVotesCastEqualSumOfPowerOfVoted() -// sum_tracked_weight() == sum_all_votes_power() - +invariant SumOfVotesCastEqualSumOfPowerOfVoted() + sum_tracked_weight() == sum_all_votes_power() + /* * totalVoted >= vote(id) */ -invariant OneIsNotMoreThanAll(uint256 pId) - sum_all_votes_power() >= tracked_weight(pId) +invariant OneIsNotMoreThanAll(uint256 pId) + sum_all_votes_power() >= tracked_weight(pId) + + +//NEED GHOST FIX +/* +* totalVotesPossible >= votePower(id) +*/ +//invariant possibleTotalVotes(uint256 pId) +// tracked_weight(pId) <= totalVotesPossible(snapshot(pId)) /* -* totalVotesPossible (supply/weight) >= votePower(id) +* totalVotesPossible >= votePower(id) */ // invariant possibleTotalVotes(uint pId) +//invariant trackedVsTotal(uint256 pId) +// tracked_weight(pId) <= possibleMaxOfVoters(pId) + /* rule someOtherRuleToRemoveLater(uint256 num){ @@ -113,4 +163,62 @@ rule noVoteForSomeoneElse(uint256 pId, uint8 support){ uint256 voter = e.msg.sender; castVote(e, pId, support); -} \ No newline at end of file +} + + +//ok +rule votingWeightMonotonicity(method f){ + uint256 votingWeightBefore = sum_tracked_weight(); + + env e; + calldataarg args; + f(e, args); + + uint256 votingWeightAfter = sum_tracked_weight(); + + assert votingWeightBefore <= votingWeightAfter, "Voting weight was decreased somehow"; +} + +// timeouts and strange violations +// https://vaas-stg.certora.com/output/3106/23f63c84c58b06285f4f/?anonymousKey=e5a7dc2e0ce7829cf5af8eb29a4ba231d915704c +rule quorumMonotonicity(method f, uint256 blockNumber){ + uint256 quorumBefore = quorum(blockNumber); + + env e; + calldataarg args; + f(e, args); + + uint256 quorumAfter = quorum(blockNumber); + + assert quorumBefore <= quorumAfter, "Quorum was decreased somehow"; +} + +// getVotes() returns different results. +// are we assuming that a person with 0 votes can vote? are we assuming that a person may have 0 votes +// it seems like a weight can be 0. At least there is no check for it +// If it can be 0 then < should be changed to <= but it gives less coverage +rule hasVotedCorrelation(uint256 pId, method f, address acc, env e){ + uint256 againstBefore = votesAgainst(); + uint256 forBefore = votesFor(); + uint256 abstainBefore = votesAbstain(); + //againstBefore, forBefore, abstainBefore = proposalVotes(pId); + + bool hasVotedBefore = hasVoted(e, pId, acc); + uint256 votesBefore = getVotes(acc, pId); + require votesBefore > 0; + + calldataarg args; + f(e, args); + + uint256 againstAfter = votesAgainst(); + uint256 forAfter = votesFor(); + uint256 abstainAfter = votesAbstain(); + //againstAfter, forAfter, abstainAfter = proposalVotes(pId); + + uint256 votesAfter = getVotes(acc, pId); + bool hasVotedAfter = hasVoted(e, pId, acc); + + assert hasVotedBefore != hasVotedAfter => againstBefore < againstAfter || forBefore < forAfter || abstainBefore < abstainAfter, "no correlation"; +} + + From 5833f52879e438422daad46741b2f821b42a6613 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Tue, 16 Nov 2021 18:32:38 +0200 Subject: [PATCH 085/254] Harness of castVoteWithReason to be able to impose requirement on the proposal ID --- certora/harnesses/GovernorHarness.sol | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol index 225f488d5..56cee4afa 100644 --- a/certora/harnesses/GovernorHarness.sol +++ b/certora/harnesses/GovernorHarness.sol @@ -109,13 +109,12 @@ contract GovernorHarness is Governor { return super.propose(targets, values, calldatas, ""); } - uint256 public proposalid_global; - uint8 public support_global; - function castVoteWithReason(uint256 proposalId, - uint8 support, string calldata reason) public virtual override returns (uint256){ - require(proposalId == proposalid_global); - require(support == support_global); + // Harness of castVoteWithReason to be able to impose requirement on the proposal ID. + uint256 public _pId_Harness; + function castVoteWithReason(uint256 proposalId, uint8 support, string calldata reason) + public override returns (uint256) { + require(proposalId == _pId_Harness); return super.castVoteWithReason(proposalId, support, reason); } } \ No newline at end of file From 1da0a4ae7df78c96b2e8647993be4de665ee7578 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Tue, 16 Nov 2021 18:33:49 +0200 Subject: [PATCH 086/254] allFunctionsRevertIfExecuted, allFunctionsRevertIfCanceled, executedOnlyAfterExecuteFunc passing as intended --- certora/specs/GovernorBase.spec | 84 +++++++++++++++++---------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 9ce360e17..f9814cfa7 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -4,23 +4,23 @@ methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart - proposalDeadline(uint256) returns uint256 envfree + proposalDeadline(uint256) returns uint256 envfree // matches proposalVoteEnd hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree isExecuted(uint256) returns bool envfree isCanceled(uint256) returns bool envfree execute(address[], uint256[], bytes[], bytes32) returns uint256 - // initialized(uint256) returns bool envfree - hasVoted(uint256, address) returns bool - castVote(uint256, uint8) returns uint256 - // castVoteBySig(uint256,uint8,uint8,bytes32,bytes32) returns uint256 // internal functions made public in harness: _quorumReached(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree - // hashProposal(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) => uniqueHashGhost(descriptionHash) + + _pId_Harness() returns uint256 envfree; + + // function summarization + hashProposal(address[], uint256[], bytes[], bytes32) => CONSTANT } ////////////////////////////////////////////////////////////////////////////// @@ -32,25 +32,30 @@ methods { ////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Helper Functions /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -/* -function callFunctionWithParams(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, uint256 proposalId, uint8 support, string reason, uint8 v, bytes32 r, bytes32 s, method f) { + +function callFunctionWithProposal(uint256 proposalId, method f) { + address[] targets; uint256[] values; bytes[] calldatas; bytes32 descriptionHash; + uint8 support; uint8 v; bytes32 r; bytes32 s; env e; if (f.selector == callPropose(address[], uint256[], bytes[]).selector) { - callPropose(e, targets, values, calldatas); + uint256 result = callPropose@withrevert(e, targets, values, calldatas); + require(proposalId == result); } else if (f.selector == execute(address[], uint256[], bytes[], bytes32).selector) { - execute(e, targets, values, calldatas, descriptionHash); + uint256 result = execute@withrevert(e, targets, values, calldatas, descriptionHash); + require(result == proposalId); } else if (f.selector == castVote(uint256, uint8).selector) { - castVote(e, proposalId, support); - } else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) { - castVoteWithReason(e, proposalId, support, reason); + castVote@withrevert(e, proposalId, support); + } else if (f.selector == 0x7b3c71d3) { + calldataarg args; + require(_pId_Harness() == proposalId); + f@withrevert(e, args); } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) { - castVoteBySig(e, proposalId, support, v, r, s); + castVoteBySig@withrevert(e, proposalId, support, v, r, s); } else { - calldataarg args; - f(e,args); - } + calldataarg args; + f@withrevert(e, args); + } } -*/ /* ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -246,46 +251,43 @@ rule checkHashProposal { /* * all non-view functions should revert if proposal is executed */ -rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView } { - env e; calldataarg args; - uint256 pId; uint8 sup; uint8 v; bytes32 r; bytes32 s ; +// summarization - hashProposal => Const - for any set of arguments passed to the function the same value will be returned. +// that means that for different arguments passed, the same value will be returned, for example: func(a,b,c,d) == func(o,p,g,r) +// the summarization is not an under estimation in this case, because we want to check that for a specific proposal ID (pId), any +// (non view) function call is reverting. We dont care what happen with other pIds, and dont care how the hash function generates the ID. +rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selector != 0x7d5e81e2 && !f.isFallback} { + env e; calldataarg args; // ^ + uint256 pId; // propose require(isExecuted(pId)); + requireInvariant proposalInitiated(pId); requireInvariant noBothExecutedAndCanceled(pId); - // f@withrevert(e,args); - // castVote@withrevert(e, pId, sup); - castVoteBySig@withrevert(e, pId, sup, v, r, s); - assert(lastReverted == true, "Function was not reverted"); + callFunctionWithProposal(pId, f); + assert(lastReverted, "Function was not reverted"); } - /* * all non-view functions should revert if proposal is canceled */ -rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView } { - env e; calldataarg args; - address[] targets; uint256[] values; bytes[] calldatas; bytes32 descriptionHash; - uint256 pId = uniqueHashGhost(descriptionHash); - uint8 sup; uint8 v; bytes32 r; bytes32 s; - +rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selector != 0x7d5e81e2 && !f.isFallback} { + env e; calldataarg args; // ^ + uint256 pId; // propose require(isCanceled(pId)); requireInvariant noBothExecutedAndCanceled(pId); - // castVote@withrevert(e, pId, sup); - // castVoteWithReason(e, pId, sup, ""); - // castVoteBySig@withrevert(e, pId, sup, v, r, s); - require(isCanceled(pId) => proposalSnapshot(pId) != 0); - execute@withrevert(e, targets, values, calldatas, descriptionHash); - assert(lastReverted == true, "Function was not reverted"); + requireInvariant proposalInitiated(pId); + callFunctionWithProposal(pId, f); + assert(lastReverted, "Function was not reverted"); } /* * Shows that executed can only change due to execute() */ -rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) filtered {f -> f.selector != (execute(address[], uint256[], bytes[], bytes32).selector)} { +rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) { env e; calldataarg args; - uint256 pId = hashProposal(targets, values, calldatas, descriptionHash); + uint256 pId; bool executedBefore = isExecuted(pId); require(!executedBefore); - f(e, args); + callFunctionWithProposal(pId, f); + require(!lastReverted); // execute(e, targets, values, calldatas, descriptionHash); bool executedAfter = isExecuted(pId); assert(executedAfter != executedBefore, "executed property did not change"); From daad23b3a732efbe97cbe0aa08b510e72e3f2cdb Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Tue, 16 Nov 2021 18:35:09 +0200 Subject: [PATCH 087/254] comment noVoteForSomeoneElse --- certora/specs/GovernorCountingSimple.spec | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 84d77f168..a73edaa67 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -147,7 +147,9 @@ rule someOtherRuleToRemoveLater(uint256 num){ } */ - +/* + * Checks that only one user is updated in the system when calling cast vote functions (assuming hasVoted is changing correctly, false->true, with every vote cast) + */ rule oneUserVotesInCast(uint256 pId, method f) filtered { f -> ((f.selector == castVote(uint256, uint8).selector) || f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) }{ env e; calldataarg args; @@ -157,14 +159,20 @@ rule oneUserVotesInCast(uint256 pId, method f) filtered { f -> ((f.selector == c assert(ghost_After == ghost_Before + 1, "Raised by more than 1"); } - +/* + * Checks that in any call to cast vote functions only the sender's value is updated + */ + /* rule noVoteForSomeoneElse(uint256 pId, uint8 support){ env e; - uint256 voter = e.msg.sender; + address voter = e.msg.sender; + bool hasVotedBefore = hasVoted(pId, voter); + require(!hasVotedBefore); castVote(e, pId, support); - + bool hasVotedAfter = hasVoted(pId, voter); + assert(hasVotedBefore != hasVotedAfter => forall address user. user != voter); } - +*/ //ok rule votingWeightMonotonicity(method f){ @@ -219,6 +227,4 @@ rule hasVotedCorrelation(uint256 pId, method f, address acc, env e){ bool hasVotedAfter = hasVoted(e, pId, acc); assert hasVotedBefore != hasVotedAfter => againstBefore < againstAfter || forBefore < forAfter || abstainBefore < abstainAfter, "no correlation"; -} - - +} \ No newline at end of file From eb27bdd282c1843325b836ca57d0fa011153c653 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 16 Nov 2021 19:53:07 +0200 Subject: [PATCH 088/254] MoreRulesAndFixesOfExistedRules --- certora/harnesses/GovernorBasicHarness.sol | 6 + certora/scripts/GovernorBasic.sh | 7 +- .../GovernorCountingSimple-counting.sh | 2 +- certora/specs/GovernorBase.spec | 17 +++ certora/specs/GovernorCountingSimple.spec | 132 +++++++++++++----- 5 files changed, 123 insertions(+), 41 deletions(-) diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol index db3f0818b..02846a137 100644 --- a/certora/harnesses/GovernorBasicHarness.sol +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -59,6 +59,12 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes return deltaWeight; } + function callPropose(address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas) public virtual returns (uint256) { + return super.propose(targets, values, calldatas, ""); + } + /* mapping (address => mapping (uint256 => uint256)) _getVotes; diff --git a/certora/scripts/GovernorBasic.sh b/certora/scripts/GovernorBasic.sh index 8a6b48373..7b3db7077 100644 --- a/certora/scripts/GovernorBasic.sh +++ b/certora/scripts/GovernorBasic.sh @@ -1,9 +1,8 @@ certoraRun certora/harnesses/GovernorBasicHarness.sol \ --verify GovernorBasicHarness:certora/specs/GovernorBase.spec \ - --solc solc8.2 \ - --staging uri/add_with_env_to_preserved_all \ + --solc solc8.0 \ + ---staging shelly/stringCVL \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --disableLocalTypeChecking \ - --rule proposalInitiated \ + --rule unaffectedThreshhold \ --msg "$1" \ No newline at end of file diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/GovernorCountingSimple-counting.sh index 26df57c7b..6413e5694 100644 --- a/certora/scripts/GovernorCountingSimple-counting.sh +++ b/certora/scripts/GovernorCountingSimple-counting.sh @@ -1,7 +1,7 @@ certoraRun certora/harnesses/GovernorBasicHarness.sol \ --verify GovernorBasicHarness:certora/specs/GovernorCountingSimple.spec \ --solc solc8.2 \ - --staging \ + --staging alex/external-timeout-for-solvers \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ --rule hasVotedCorrelation \ diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index f9814cfa7..35ecac5a4 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -21,6 +21,7 @@ methods { // function summarization hashProposal(address[], uint256[], bytes[], bytes32) => CONSTANT + proposalThreshold() returns uint256 envfree } ////////////////////////////////////////////////////////////////////////////// @@ -292,3 +293,19 @@ rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] c bool executedAfter = isExecuted(pId); assert(executedAfter != executedBefore, "executed property did not change"); } + + +/* +* User should not be able to affect proposal threshold +*/ +rule unaffectedThreshhold(method f){ + uint256 thresholdBefore = proposalThreshold(); + + env e; + calldataarg args; + f(e, args); + + uint256 thresholdAfter = proposalThreshold(); + + assert thresholdBefore == thresholdAfter, "threshold was changed"; +} diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index a73edaa67..e2fc1fdf9 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -9,8 +9,17 @@ methods { quorum(uint256) returns uint256 envfree proposalVotes(uint256) returns (uint256, uint256, uint256) envfree - getVotes(address, uint256) returns uint256 envfree - //getVotes(address, uint256) => CONSTANT + quorumNumerator() returns uint256 + updateQuorumNumerator(uint256) + _executor() returns address + + getVotes(address, uint256) returns uint256 envfree => DISPATCHER(true) + //getVotes(address, uint256) => DISPATCHER(true) + + getPastTotalSupply(uint256) returns uint256 envfree => DISPATCHER(true) + //getPastTotalSupply(uint256) => DISPATCHER(true) + + getPastVotes(address, uint256) returns uint256 envfree => DISPATCHER(true) } ////////////////////////////////////////////////////////////////////////////// @@ -21,18 +30,18 @@ ghost hasVoteGhost(uint256) returns uint256 { init_state axiom forall uint256 pId. hasVoteGhost(pId) == 0; } -hook Sstore _proposalVotes[KEY uint256 pId].hasVoted[KEY address user] bool current_voting_State (bool old_voting_state) STORAGE{ - havoc hasVoteGhost assuming forall uint256 p. ((p == pId && current_voting_State && !old_voting_state) ? (hasVoteGhost@new(p) == hasVoteGhost@old(p) + 1) : - (hasVoteGhost@new(p) == hasVoteGhost@old(p))); -} +//hook Sstore _proposalVotes[KEY uint256 pId].hasVoted[KEY address user] bool current_voting_State (bool old_voting_state) STORAGE{ +// havoc hasVoteGhost assuming forall uint256 p. ((p == pId && current_voting_State && !old_voting_state) ? (hasVoteGhost@new(p) == hasVoteGhost@old(p) + 1) : +// (hasVoteGhost@new(p) == hasVoteGhost@old(p))); +//} ghost sum_all_votes_power() returns uint256 { init_state axiom sum_all_votes_power() == 0; } -hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE { - havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; -} +//hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE { +// havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; +//} ghost tracked_weight(uint256) returns uint256 { init_state axiom forall uint256 p. tracked_weight(p) == 0; @@ -74,30 +83,20 @@ hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 havoc votesAbstain assuming votesAbstain@new() == votesAbstain@old() - old_votes + votes; } -/* + ghost totalVotesPossible(uint256) returns uint256 { init_state axiom forall uint256 bn. totalVotesPossible(bn) == 0; } -ghost possibleMaxOfVoters(uint256) returns uint256{ - init_state axiom forall uint256 pId. possibleMaxOfVoters(pId) == 0; -} +// Was done with _getVotes by mistake. Should check GovernorBasicHarness but _getVotes is from GovernorHarness +//hook Sstore _getVotes[KEY address uId][KEY uint256 blockNumber] uint256 voteWeight(uint256 old_voteWeight) STORAGE { +// havoc totalVotesPossible assuming forall uint256 bn. +// (bn == blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn) - old_voteWeight + voteWeight) +// && (bn != blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn)); +// +//} + -hook Sstore _getVotes[KEY address uId][KEY uint256 blockNumber] uint256 voteWeight(uint256 old_voteWeight) STORAGE { - havoc totalVotesPossible assuming forall uint256 bn. - (bn == blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn) - old_voteWeight + voteWeight) - && (bn != blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn)); - //somehow havoc possibleMaxOfVoters based on scheme. Probably can be implemented if previous havoc is possible -} -*/ -/* -_proposalVotes[pId].hasVoted[account] -> bool - ^ - | - go through all accounts, - if true => -possibleMaxOfVoters(pId) += getVotes(pId, acount); -*/ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// @@ -174,7 +173,7 @@ rule noVoteForSomeoneElse(uint256 pId, uint8 support){ } */ -//ok +// ok rule votingWeightMonotonicity(method f){ uint256 votingWeightBefore = sum_tracked_weight(); @@ -187,8 +186,9 @@ rule votingWeightMonotonicity(method f){ assert votingWeightBefore <= votingWeightAfter, "Voting weight was decreased somehow"; } -// timeouts and strange violations -// https://vaas-stg.certora.com/output/3106/23f63c84c58b06285f4f/?anonymousKey=e5a7dc2e0ce7829cf5af8eb29a4ba231d915704c +// ok with this branch: thomas/bool-hook-values +// can't use link because contracts are abstract, they don't have bytecode/constructor +// add implementation harness rule quorumMonotonicity(method f, uint256 blockNumber){ uint256 quorumBefore = quorum(blockNumber); @@ -201,30 +201,90 @@ rule quorumMonotonicity(method f, uint256 blockNumber){ assert quorumBefore <= quorumAfter, "Quorum was decreased somehow"; } + +function callFunctionWithParams(method f, uint256 proposalId, env e) { + address[] targets; + uint256[] values; + bytes[] calldatas; + bytes32 descriptionHash; + uint8 support; + uint8 v; bytes32 r; bytes32 s; + if (f.selector == callPropose(address[], uint256[], bytes[]).selector) { + uint256 result = callPropose(e, targets, values, calldatas); + require result == proposalId; + } else if (f.selector == execute(address[], uint256[], bytes[], bytes32).selector) { + uint256 result = execute(e, targets, values, calldatas, descriptionHash); + require result == proposalId; + } else if (f.selector == castVote(uint256, uint8).selector) { + castVote(e, proposalId, support); + //} else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) { + // castVoteWithReason(e, proposalId, support, reason); + } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) { + castVoteBySig(e, proposalId, support, v, r, s); + } else { + calldataarg args; + f(e,args); + } +} + + // getVotes() returns different results. +// how to ensure that the same acc is used in getVotes() in uint256 votesBefore = getVotes(acc, bn);/uint256 votesAfter = getVotes(acc, bn); and in callFunctionWithParams +// votesBefore and votesAfter give different results but shoudn't + // are we assuming that a person with 0 votes can vote? are we assuming that a person may have 0 votes // it seems like a weight can be 0. At least there is no check for it // If it can be 0 then < should be changed to <= but it gives less coverage -rule hasVotedCorrelation(uint256 pId, method f, address acc, env e){ + +// run on ALex's branch to avoid tiomeouts: --staging alex/external-timeout-for-solvers +// --staging shelly/forSasha +// implement ERC20Votes as a harness +rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f -> f.selector != 0x7d5e81e2}{ uint256 againstBefore = votesAgainst(); uint256 forBefore = votesFor(); uint256 abstainBefore = votesAbstain(); //againstBefore, forBefore, abstainBefore = proposalVotes(pId); + address acc = e.msg.sender; + bool hasVotedBefore = hasVoted(e, pId, acc); - uint256 votesBefore = getVotes(acc, pId); + uint256 votesBefore = getVotes(acc, bn); require votesBefore > 0; - calldataarg args; - f(e, args); + //calldataarg args; + //f(e, args); + callFunctionWithParams(f, pId, e); uint256 againstAfter = votesAgainst(); uint256 forAfter = votesFor(); uint256 abstainAfter = votesAbstain(); //againstAfter, forAfter, abstainAfter = proposalVotes(pId); - uint256 votesAfter = getVotes(acc, pId); + uint256 votesAfter = getVotes(acc, bn); bool hasVotedAfter = hasVoted(e, pId, acc); assert hasVotedBefore != hasVotedAfter => againstBefore < againstAfter || forBefore < forAfter || abstainBefore < abstainAfter, "no correlation"; +} + + +/* +* Check privileged operations +*/ +// NO NEED FOR SPECIFIC CHANGES +// how to check executor()? +// to make it public instead of internal is not best idea, I think. +// currentContract gives a violation in +rule privilegedOnly(method f){ + env e; + calldataarg arg; + uint256 quorumNumBefore = quorumNumerator(e); + + f(e, arg); + + uint256 quorumNumAfter = quorumNumerator(e); + + address executorCheck = currentContract; + // address executorCheck = _executor(e); + + assert quorumNumBefore != quorumNumAfter => e.msg.sender == executorCheck, "non priveleged user changed quorum numerator"; } \ No newline at end of file From 44113d58f5e1be5f3a4dcb9bbc6f51c6ef7a8f5e Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 17 Nov 2021 11:56:24 +0200 Subject: [PATCH 089/254] NewWizardHarness --- certora/harnesses/ERC20VotesHarness.sol | 5 + certora/harnesses/GovernorBasicHarness.sol | 21 ++- certora/harnesses/WizardHarness1.sol | 152 ++++++++++++++++++ certora/scripts/GovernorBasic.sh | 6 +- .../GovernorCountingSimple-counting.sh | 2 +- certora/scripts/WizardHarness1.sh | 8 + certora/specs/GovernorCountingSimple.spec | 1 + 7 files changed, 183 insertions(+), 12 deletions(-) create mode 100644 certora/harnesses/ERC20VotesHarness.sol create mode 100644 certora/harnesses/WizardHarness1.sol create mode 100644 certora/scripts/WizardHarness1.sh diff --git a/certora/harnesses/ERC20VotesHarness.sol b/certora/harnesses/ERC20VotesHarness.sol new file mode 100644 index 000000000..c9f40b024 --- /dev/null +++ b/certora/harnesses/ERC20VotesHarness.sol @@ -0,0 +1,5 @@ +import "../../contracts/token/ERC20/extensions/ERC20Votes.sol"; + +contract ERC20VotesHarness is ERC20Votes { + constructor(string memory name) ERC20Permit(name) {} +} \ No newline at end of file diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol index 02846a137..8f5a9dfd6 100644 --- a/certora/harnesses/GovernorBasicHarness.sol +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -33,16 +33,19 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes uint256 _votingDelay; - function votingDelay() public view override virtual returns (uint256) { + function votingDelay() public view override virtual returns (uint256) { // HARNESS: pure -> view return _votingDelay; } uint256 _votingPeriod; - function votingPeriod() public view override virtual returns (uint256) { + function votingPeriod() public view override virtual returns (uint256) { // HARNESS: pure -> view return _votingPeriod; } + function snapshot(uint256 proposalId) public view returns (uint64) { + return _proposals[proposalId].voteStart._deadline; + } mapping(uint256 => uint256) public ghost_sum_vote_power_by_id; @@ -65,13 +68,15 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes return super.propose(targets, values, calldatas, ""); } - /* - mapping (address => mapping (uint256 => uint256)) _getVotes; - - function getVotesHarnness(address account, uint256 blockNumber) public { - _getVotes[account][blockNumber] = getVotes(account, blockNumber); + // Harness of castVoteWithReason to be able to impose requirement on the proposal ID. + uint256 public _pId_Harness; + function castVoteWithReason(uint256 proposalId, uint8 support, string calldata reason) + public + override(IGovernor, Governor) + returns (uint256) { + require(proposalId == _pId_Harness); + return super.castVoteWithReason(proposalId, support, reason); } - */ // The following functions are overrides required by Solidity. diff --git a/certora/harnesses/WizardHarness1.sol b/certora/harnesses/WizardHarness1.sol new file mode 100644 index 000000000..1c5ed9bcb --- /dev/null +++ b/certora/harnesses/WizardHarness1.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../contracts/governance/Governor.sol"; +import "../../contracts/governance/extensions/GovernorCountingSimple.sol"; +import "../../contracts/governance/extensions/GovernorVotes.sol"; +import "../../contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../contracts/governance/extensions/GovernorTimelockControl.sol"; +import "../../contracts/governance/extensions/GovernorProposalThreshold.sol"; + +/* +Wizard options: +ProposalThreshhold = 10 +ERC20Votes +TimelockCOntroller +*/ + +contract WizardHarness1 is Governor, GovernorProposalThreshold, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { + constructor(ERC20Votes _token, TimelockController _timelock, string memory name, uint256 quorumFraction) + Governor(name) + GovernorVotes(_token) + GovernorVotesQuorumFraction(quorumFraction) + GovernorTimelockControl(_timelock) + {} + + //HARNESS + + function isExecuted(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].executed; + } + + function isCanceled(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].canceled; + } + + uint256 _votingDelay; + + uint256 _votingPeriod; + + uint256 _proposalThreshold; + + mapping(uint256 => uint256) public ghost_sum_vote_power_by_id; + + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason + ) internal override virtual returns (uint256) { + + uint256 deltaWeight = super._castVote(proposalId, account, support, reason); //HARNESS + ghost_sum_vote_power_by_id[proposalId] += deltaWeight; + + return deltaWeight; + } + + function callPropose(address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas) public virtual returns (uint256) { + return super.propose(targets, values, calldatas, ""); + } + + function snapshot(uint256 proposalId) public view returns (uint64) { + return _proposals[proposalId].voteStart._deadline; + } + + + + // original code + + function votingDelay() public view override returns (uint256) { // HARNESS: pure -> view + return _votingDelay; // HARNESS: parametric + } + + function votingPeriod() public view override returns (uint256) { // HARNESS: pure -> view + return _votingPeriod; // HARNESS: parametric + } + + function proposalThreshold() public view override returns (uint256) { // HARNESS: pure -> view + return _proposalThreshold; // HARNESS: parametric + } + + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function getVotes(address account, uint256 blockNumber) + public + view + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function state(uint256 proposalId) + public + view + override(Governor, GovernorTimelockControl) + returns (ProposalState) + { + return super.state(proposalId); + } + + function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) + public + override(Governor, GovernorProposalThreshold, IGovernor) + returns (uint256) + { + return super.propose(targets, values, calldatas, description); + } + + function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockControl) + { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockControl) + returns (uint256) + { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() + internal + view + override(Governor, GovernorTimelockControl) + returns (address) + { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/scripts/GovernorBasic.sh b/certora/scripts/GovernorBasic.sh index 7b3db7077..f7d3cbf18 100644 --- a/certora/scripts/GovernorBasic.sh +++ b/certora/scripts/GovernorBasic.sh @@ -1,8 +1,8 @@ certoraRun certora/harnesses/GovernorBasicHarness.sol \ --verify GovernorBasicHarness:certora/specs/GovernorBase.spec \ - --solc solc8.0 \ - ---staging shelly/stringCVL \ + --solc solc8.2 \ + --staging shelly/forSasha \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule unaffectedThreshhold \ + --rule allFunctionsRevertIfCanceled \ --msg "$1" \ No newline at end of file diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/GovernorCountingSimple-counting.sh index 6413e5694..ddd59baa2 100644 --- a/certora/scripts/GovernorCountingSimple-counting.sh +++ b/certora/scripts/GovernorCountingSimple-counting.sh @@ -1,7 +1,7 @@ certoraRun certora/harnesses/GovernorBasicHarness.sol \ --verify GovernorBasicHarness:certora/specs/GovernorCountingSimple.spec \ --solc solc8.2 \ - --staging alex/external-timeout-for-solvers \ + --staging shelly/forSasha \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ --rule hasVotedCorrelation \ diff --git a/certora/scripts/WizardHarness1.sh b/certora/scripts/WizardHarness1.sh new file mode 100644 index 000000000..de0a038bb --- /dev/null +++ b/certora/scripts/WizardHarness1.sh @@ -0,0 +1,8 @@ +certoraRun certora/harnesses/WizardHarness1.sol \ + --verify WizardHarness1:certora/specs/GovernorBase.spec \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule allFunctionsRevertIfCanceled \ + --msg "$1" \ No newline at end of file diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index e2fc1fdf9..aa98ce493 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -274,6 +274,7 @@ rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f - // how to check executor()? // to make it public instead of internal is not best idea, I think. // currentContract gives a violation in +// maybe need harness implementation for one of the contracts rule privilegedOnly(method f){ env e; calldataarg arg; From a33b9b2bb0a34ec2f3792688d16f30bbceca58af Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 17 Nov 2021 13:33:43 +0200 Subject: [PATCH 090/254] FixedERC20VotesIssue --- certora/harnesses/ERC20VotesHarness.sol | 2 +- certora/harnesses/GovernorBasicHarness.sol | 7 ++++--- certora/harnesses/WizardHarness1.sol | 12 +++++++++++- certora/scripts/Governor.sh | 4 ++-- certora/scripts/GovernorBasic.sh | 2 +- certora/scripts/GovernorCountingSimple-counting.sh | 2 +- certora/scripts/WizardHarness1.sh | 6 +++--- certora/specs/GovernorBase.spec | 9 ++++++++- certora/specs/GovernorCountingSimple.spec | 14 +++----------- 9 files changed, 34 insertions(+), 24 deletions(-) diff --git a/certora/harnesses/ERC20VotesHarness.sol b/certora/harnesses/ERC20VotesHarness.sol index c9f40b024..6ef5934b7 100644 --- a/certora/harnesses/ERC20VotesHarness.sol +++ b/certora/harnesses/ERC20VotesHarness.sol @@ -1,5 +1,5 @@ import "../../contracts/token/ERC20/extensions/ERC20Votes.sol"; contract ERC20VotesHarness is ERC20Votes { - constructor(string memory name) ERC20Permit(name) {} + constructor(string memory name, string memory symbol) ERC20Permit(name) ERC20(name, symbol) {} } \ No newline at end of file diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol index 8f5a9dfd6..f93855929 100644 --- a/certora/harnesses/GovernorBasicHarness.sol +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -71,9 +71,10 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes // Harness of castVoteWithReason to be able to impose requirement on the proposal ID. uint256 public _pId_Harness; function castVoteWithReason(uint256 proposalId, uint8 support, string calldata reason) - public - override(IGovernor, Governor) - returns (uint256) { + public + override(IGovernor, Governor) + returns (uint256) + { require(proposalId == _pId_Harness); return super.castVoteWithReason(proposalId, support, reason); } diff --git a/certora/harnesses/WizardHarness1.sol b/certora/harnesses/WizardHarness1.sol index 1c5ed9bcb..0407efc79 100644 --- a/certora/harnesses/WizardHarness1.sol +++ b/certora/harnesses/WizardHarness1.sol @@ -64,7 +64,17 @@ contract WizardHarness1 is Governor, GovernorProposalThreshold, GovernorCounting return _proposals[proposalId].voteStart._deadline; } - + + // Harness of castVoteWithReason to be able to impose requirement on the proposal ID. + uint256 public _pId_Harness; + function castVoteWithReason(uint256 proposalId, uint8 support, string calldata reason) + public + override(IGovernor, Governor) + returns (uint256) + { + require(proposalId == _pId_Harness); + return super.castVoteWithReason(proposalId, support, reason); + } // original code diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index d3b7f9b3f..7b44e364a 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -1,7 +1,7 @@ -certoraRun certora/harnesses/GovernorHarness.sol \ +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorHarness.sol \ --verify GovernorHarness:certora/specs/GovernorBase.spec \ --solc solc8.0 \ - --staging \ + --staging shelly/forSasha \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ --rule voteStartBeforeVoteEnd \ diff --git a/certora/scripts/GovernorBasic.sh b/certora/scripts/GovernorBasic.sh index f7d3cbf18..6f9c84059 100644 --- a/certora/scripts/GovernorBasic.sh +++ b/certora/scripts/GovernorBasic.sh @@ -1,4 +1,4 @@ -certoraRun certora/harnesses/GovernorBasicHarness.sol \ +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorBasicHarness.sol \ --verify GovernorBasicHarness:certora/specs/GovernorBase.spec \ --solc solc8.2 \ --staging shelly/forSasha \ diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/GovernorCountingSimple-counting.sh index ddd59baa2..c812f52e9 100644 --- a/certora/scripts/GovernorCountingSimple-counting.sh +++ b/certora/scripts/GovernorCountingSimple-counting.sh @@ -1,4 +1,4 @@ -certoraRun certora/harnesses/GovernorBasicHarness.sol \ +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorBasicHarness.sol \ --verify GovernorBasicHarness:certora/specs/GovernorCountingSimple.spec \ --solc solc8.2 \ --staging shelly/forSasha \ diff --git a/certora/scripts/WizardHarness1.sh b/certora/scripts/WizardHarness1.sh index de0a038bb..dd8816f35 100644 --- a/certora/scripts/WizardHarness1.sh +++ b/certora/scripts/WizardHarness1.sh @@ -1,8 +1,8 @@ -certoraRun certora/harnesses/WizardHarness1.sol \ - --verify WizardHarness1:certora/specs/GovernorBase.spec \ +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardHarness1.sol \ + --verify WizardHarness1:certora/specs/GovernorCountingSimple.spec \ --solc solc8.2 \ --staging shelly/forSasha \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule allFunctionsRevertIfCanceled \ + --rule SumOfVotesCastEqualSumOfPowerOfVoted \ --msg "$1" \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 35ecac5a4..9eca5bc7f 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -22,6 +22,14 @@ methods { // function summarization hashProposal(address[], uint256[], bytes[], bytes32) => CONSTANT proposalThreshold() returns uint256 envfree + + getVotes(address, uint256) returns uint256 envfree => DISPATCHER(true) + //getVotes(address, uint256) => DISPATCHER(true) + + getPastTotalSupply(uint256) returns uint256 envfree => DISPATCHER(true) + //getPastTotalSupply(uint256) => DISPATCHER(true) + + getPastVotes(address, uint256) returns uint256 envfree => DISPATCHER(true) } ////////////////////////////////////////////////////////////////////////////// @@ -91,7 +99,6 @@ invariant proposalInitiated(uint256 pId) }}*/ - /* * A proposal cannot end unless it started. */ diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index aa98ce493..e8ee21db5 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -12,14 +12,6 @@ methods { quorumNumerator() returns uint256 updateQuorumNumerator(uint256) _executor() returns address - - getVotes(address, uint256) returns uint256 envfree => DISPATCHER(true) - //getVotes(address, uint256) => DISPATCHER(true) - - getPastTotalSupply(uint256) returns uint256 envfree => DISPATCHER(true) - //getPastTotalSupply(uint256) => DISPATCHER(true) - - getPastVotes(address, uint256) returns uint256 envfree => DISPATCHER(true) } ////////////////////////////////////////////////////////////////////////////// @@ -39,9 +31,9 @@ ghost sum_all_votes_power() returns uint256 { init_state axiom sum_all_votes_power() == 0; } -//hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE { -// havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; -//} +hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE { + havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; +} ghost tracked_weight(uint256) returns uint256 { init_state axiom forall uint256 p. tracked_weight(p) == 0; From 61b011869c4924abe835b87e4f659ff9152dbf1a Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 17 Nov 2021 16:00:16 +0200 Subject: [PATCH 091/254] AddedLinkAndFixingGhost --- certora/harnesses/ERC20VotesHarness.sol | 30 +++++++++++++++++++ .../GovernorCountingSimple-counting.sh | 2 +- certora/scripts/WizardHarness1.sh | 3 +- certora/specs/GovernorCountingSimple.spec | 19 ++++++------ .../token/ERC20/extensions/ERC20Votes.sol | 2 +- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/certora/harnesses/ERC20VotesHarness.sol b/certora/harnesses/ERC20VotesHarness.sol index 6ef5934b7..366aa9304 100644 --- a/certora/harnesses/ERC20VotesHarness.sol +++ b/certora/harnesses/ERC20VotesHarness.sol @@ -2,4 +2,34 @@ import "../../contracts/token/ERC20/extensions/ERC20Votes.sol"; contract ERC20VotesHarness is ERC20Votes { constructor(string memory name, string memory symbol) ERC20Permit(name) ERC20(name, symbol) {} + + mapping(address => mapping(uint256 => uint256)) _getPastVotes; + + function _afterTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override { + super._afterTokenTransfer(from, to, amount); + _getPastVotes[from][block.number] -= amount; + _getPastVotes[to][block.number] += amount; + } + + /** + * @dev Change delegation for `delegator` to `delegatee`. + * + * Emits events {DelegateChanged} and {DelegateVotesChanged}. + */ + function _delegate(address delegator, address delegatee) internal virtual override{ + super._delegate(delegator, delegatee); + _getPastVotes[delegator][block.number] -= balanceOf(delegator); + _getPastVotes[delegatee][block.number] += balanceOf(delegator); + } + + function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + uint256 gpv = super.getPastVotes(account, blockNumber); + require (_getPastVotes[account][blockNumber] == gpv); + return gpv; + } + } \ No newline at end of file diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/GovernorCountingSimple-counting.sh index c812f52e9..82b2c263e 100644 --- a/certora/scripts/GovernorCountingSimple-counting.sh +++ b/certora/scripts/GovernorCountingSimple-counting.sh @@ -1,4 +1,4 @@ -certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorBasicHarness.sol \ +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorBasicHarness.sol \ --verify GovernorBasicHarness:certora/specs/GovernorCountingSimple.spec \ --solc solc8.2 \ --staging shelly/forSasha \ diff --git a/certora/scripts/WizardHarness1.sh b/certora/scripts/WizardHarness1.sh index dd8816f35..64fd52d33 100644 --- a/certora/scripts/WizardHarness1.sh +++ b/certora/scripts/WizardHarness1.sh @@ -1,8 +1,9 @@ certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardHarness1.sol \ + --link WizardHarness1:token=ERC20VotesHarness \ --verify WizardHarness1:certora/specs/GovernorCountingSimple.spec \ --solc solc8.2 \ --staging shelly/forSasha \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule SumOfVotesCastEqualSumOfPowerOfVoted \ + --rule hasVotedCorrelation \ --msg "$1" \ No newline at end of file diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index e8ee21db5..8c17bc738 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -1,5 +1,7 @@ import "GovernorBase.spec" +using ERC20VotesHarness as erc20votes + methods { ghost_sum_vote_power_by_id(uint256) returns uint256 envfree // castVote(uint256, uint8) returns uint256 @@ -81,12 +83,11 @@ ghost totalVotesPossible(uint256) returns uint256 { } // Was done with _getVotes by mistake. Should check GovernorBasicHarness but _getVotes is from GovernorHarness -//hook Sstore _getVotes[KEY address uId][KEY uint256 blockNumber] uint256 voteWeight(uint256 old_voteWeight) STORAGE { -// havoc totalVotesPossible assuming forall uint256 bn. -// (bn == blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn) - old_voteWeight + voteWeight) -// && (bn != blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn)); -// -//} +hook Sstore erc20votes._getPastVotes[KEY address uId][KEY uint256 blockNumber] uint256 voteWeight(uint256 old_voteWeight) STORAGE { + havoc totalVotesPossible assuming forall uint256 bn. + (bn == blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn) - old_voteWeight + voteWeight) + && (bn != blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn)); +} @@ -118,8 +119,8 @@ invariant OneIsNotMoreThanAll(uint256 pId) /* * totalVotesPossible >= votePower(id) */ -//invariant possibleTotalVotes(uint256 pId) -// tracked_weight(pId) <= totalVotesPossible(snapshot(pId)) +invariant possibleTotalVotes(uint256 pId) + tracked_weight(pId) <= totalVotesPossible(snapshot(pId)) /* * totalVotesPossible >= votePower(id) @@ -255,7 +256,7 @@ rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f - uint256 votesAfter = getVotes(acc, bn); bool hasVotedAfter = hasVoted(e, pId, acc); - assert hasVotedBefore != hasVotedAfter => againstBefore < againstAfter || forBefore < forAfter || abstainBefore < abstainAfter, "no correlation"; + assert hasVotedBefore != hasVotedAfter => againstBefore <= againstAfter || forBefore <= forAfter || abstainBefore <= abstainAfter, "no correlation"; } diff --git a/contracts/token/ERC20/extensions/ERC20Votes.sol b/contracts/token/ERC20/extensions/ERC20Votes.sol index 5e176973e..06fd68831 100644 --- a/contracts/token/ERC20/extensions/ERC20Votes.sol +++ b/contracts/token/ERC20/extensions/ERC20Votes.sol @@ -84,7 +84,7 @@ abstract contract ERC20Votes is ERC20Permit { * * - `blockNumber` must have been already mined */ - function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) { + function getPastVotes(address account, uint256 blockNumber) public view virtual returns (uint256) { require(blockNumber < block.number, "ERC20Votes: block not yet mined"); return _checkpointsLookup(_checkpoints[account], blockNumber); } From f7049de56730e72e73fb878b1655c4837462247e Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Thu, 18 Nov 2021 10:12:13 +0200 Subject: [PATCH 092/254] envfreeViolationFix --- certora/harnesses/ERC20VotesHarness.sol | 4 +++- certora/specs/GovernorBase.spec | 8 ++++---- certora/specs/GovernorCountingSimple.spec | 17 +++++++++++------ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/certora/harnesses/ERC20VotesHarness.sol b/certora/harnesses/ERC20VotesHarness.sol index 366aa9304..b83fc077f 100644 --- a/certora/harnesses/ERC20VotesHarness.sol +++ b/certora/harnesses/ERC20VotesHarness.sol @@ -3,7 +3,7 @@ import "../../contracts/token/ERC20/extensions/ERC20Votes.sol"; contract ERC20VotesHarness is ERC20Votes { constructor(string memory name, string memory symbol) ERC20Permit(name) ERC20(name, symbol) {} - mapping(address => mapping(uint256 => uint256)) _getPastVotes; + mapping(address => mapping(uint256 => uint256)) public _getPastVotes; function _afterTokenTransfer( address from, @@ -26,10 +26,12 @@ contract ERC20VotesHarness is ERC20Votes { _getPastVotes[delegatee][block.number] += balanceOf(delegator); } + /* function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { uint256 gpv = super.getPastVotes(account, blockNumber); require (_getPastVotes[account][blockNumber] == gpv); return gpv; } + */ } \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 9eca5bc7f..f0e11fd9c 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -13,7 +13,7 @@ methods { castVote(uint256, uint8) returns uint256 // internal functions made public in harness: - _quorumReached(uint256) returns bool envfree + _quorumReached(uint256) returns bool _voteSucceeded(uint256) returns bool envfree @@ -23,7 +23,7 @@ methods { hashProposal(address[], uint256[], bytes[], bytes32) => CONSTANT proposalThreshold() returns uint256 envfree - getVotes(address, uint256) returns uint256 envfree => DISPATCHER(true) + getVotes(address, uint256) returns uint256 => DISPATCHER(true) //getVotes(address, uint256) => DISPATCHER(true) getPastTotalSupply(uint256) returns uint256 envfree => DISPATCHER(true) @@ -117,8 +117,8 @@ invariant noBothExecutedAndCanceled(uint256 pId) /** * A proposal could be executed only if quorum was reached and vote succeeded */ -invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) - isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) +invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e) + isExecuted(pId) => _quorumReached(e, pId) && _voteSucceeded(pId) /////////////////////////////////////////////////////////////////////////////////////// diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 8c17bc738..b2f752da1 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -8,12 +8,14 @@ methods { // castVoteBySig(uint256,uint8,uint8,bytes32,bytes32) returns uint256 snapshot(uint256) returns uint64 envfree - quorum(uint256) returns uint256 envfree + quorum(uint256) returns uint256 proposalVotes(uint256) returns (uint256, uint256, uint256) envfree quorumNumerator() returns uint256 updateQuorumNumerator(uint256) _executor() returns address + + erc20votes._getPastVotes(address, uint256) returns uint256 envfree } ////////////////////////////////////////////////////////////////////////////// @@ -89,6 +91,8 @@ hook Sstore erc20votes._getPastVotes[KEY address uId][KEY uint256 blockNumber] u && (bn != blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn)); } +invariant checkGetVotesGhost(address uId, uint256 blockNumber) + erc20votes._getPastVotes(uId, blockNumber) == getPastVotes(uId, blockNumber) ////////////////////////////////////////////////////////////////////////////// @@ -183,13 +187,14 @@ rule votingWeightMonotonicity(method f){ // can't use link because contracts are abstract, they don't have bytecode/constructor // add implementation harness rule quorumMonotonicity(method f, uint256 blockNumber){ - uint256 quorumBefore = quorum(blockNumber); - env e; + + uint256 quorumBefore = quorum(e, blockNumber); + calldataarg args; f(e, args); - uint256 quorumAfter = quorum(blockNumber); + uint256 quorumAfter = quorum(e, blockNumber); assert quorumBefore <= quorumAfter, "Quorum was decreased somehow"; } @@ -241,7 +246,7 @@ rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f - address acc = e.msg.sender; bool hasVotedBefore = hasVoted(e, pId, acc); - uint256 votesBefore = getVotes(acc, bn); + uint256 votesBefore = getVotes(e, acc, bn); require votesBefore > 0; //calldataarg args; @@ -253,7 +258,7 @@ rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f - uint256 abstainAfter = votesAbstain(); //againstAfter, forAfter, abstainAfter = proposalVotes(pId); - uint256 votesAfter = getVotes(acc, bn); + uint256 votesAfter = getVotes(e, acc, bn); bool hasVotedAfter = hasVoted(e, pId, acc); assert hasVotedBefore != hasVotedAfter => againstBefore <= againstAfter || forBefore <= forAfter || abstainBefore <= abstainAfter, "no correlation"; From 65af47d90d59e10e18eef17f74a393bac845b572 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 18 Nov 2021 15:39:18 +0200 Subject: [PATCH 093/254] added filters to revert if exec and revert if canceled --- certora/specs/GovernorBase.spec | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index f0e11fd9c..ad0626b5c 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -11,6 +11,8 @@ methods { execute(address[], uint256[], bytes[], bytes32) returns uint256 hasVoted(uint256, address) returns bool castVote(uint256, uint8) returns uint256 + updateQuorumNumerator(uint256) + // internal functions made public in harness: _quorumReached(uint256) returns bool @@ -256,6 +258,11 @@ rule checkHashProposal { //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Not Categorized Yet ////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + /* * all non-view functions should revert if proposal is executed */ @@ -263,9 +270,9 @@ rule checkHashProposal { // that means that for different arguments passed, the same value will be returned, for example: func(a,b,c,d) == func(o,p,g,r) // the summarization is not an under estimation in this case, because we want to check that for a specific proposal ID (pId), any // (non view) function call is reverting. We dont care what happen with other pIds, and dont care how the hash function generates the ID. -rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selector != 0x7d5e81e2 && !f.isFallback} { - env e; calldataarg args; // ^ - uint256 pId; // propose +rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selector != 0x7d5e81e2 && !f.isFallback && f.selector != updateQuorumNumerator(uint256).selector && f.selector != 0xa890c910} { + env e; calldataarg args; // ^ ^ + uint256 pId; // propose updateTimelock require(isExecuted(pId)); requireInvariant proposalInitiated(pId); requireInvariant noBothExecutedAndCanceled(pId); @@ -276,9 +283,9 @@ rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selec /* * all non-view functions should revert if proposal is canceled */ -rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selector != 0x7d5e81e2 && !f.isFallback} { - env e; calldataarg args; // ^ - uint256 pId; // propose +rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selector != 0x7d5e81e2 && !f.isFallback && f.selector != updateQuorumNumerator(uint256).selector && f.selector != 0xa890c910} { + env e; calldataarg args; // ^ ^ + uint256 pId; // propose updateTimelock require(isCanceled(pId)); requireInvariant noBothExecutedAndCanceled(pId); requireInvariant proposalInitiated(pId); From 0ecb5fce78064a9cd8c1cc4c4cb322e69cf8edb1 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 18 Nov 2021 15:43:21 +0200 Subject: [PATCH 094/254] fix for oneUserVotesInCast --- certora/specs/GovernorCountingSimple.spec | 29 +++++++++++++++-------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index b2f752da1..c6f47d1da 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -146,11 +146,21 @@ rule someOtherRuleToRemoveLater(uint256 num){ /* * Checks that only one user is updated in the system when calling cast vote functions (assuming hasVoted is changing correctly, false->true, with every vote cast) */ -rule oneUserVotesInCast(uint256 pId, method f) filtered { f -> ((f.selector == castVote(uint256, uint8).selector) || - f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) }{ - env e; calldataarg args; - uint256 ghost_Before = hasVoteGhost(pId); - f(e, args); +rule oneUserVotesInCast(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector || f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} { + env e; calldataarg args; // ^ ^ + uint256 ghost_Before = hasVoteGhost(pId); // propose castVoteWithReason + if (f.selector == castVote(uint256, uint8).selector) + { + castVote(e, pId, sup); + } /*else if (f.selector == 0x7b3c71d3) { + require(_pId_Harness() == proposalId); + f(e,args); + }*/ else if (f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) { + uint8 v; bytes32 r; bytes32 s; + castVoteBySig(e, pId, sup, v, r, s); + } else{ + f(e, args); + } uint256 ghost_After = hasVoteGhost(pId); assert(ghost_After == ghost_Before + 1, "Raised by more than 1"); } @@ -158,17 +168,16 @@ rule oneUserVotesInCast(uint256 pId, method f) filtered { f -> ((f.selector == c /* * Checks that in any call to cast vote functions only the sender's value is updated */ - /* + rule noVoteForSomeoneElse(uint256 pId, uint8 support){ env e; address voter = e.msg.sender; - bool hasVotedBefore = hasVoted(pId, voter); + bool hasVotedBefore = hasVoted(e, pId, voter); require(!hasVotedBefore); castVote(e, pId, support); - bool hasVotedAfter = hasVoted(pId, voter); - assert(hasVotedBefore != hasVotedAfter => forall address user. user != voter); + bool hasVotedAfter = hasVoted(e, pId, voter); + assert(hasVotedBefore != hasVotedAfter => (forall address user. user != voter => !hasVoted(e, pId, user))); } -*/ // ok rule votingWeightMonotonicity(method f){ From 9f2a672240e41774dafc8ea4641e00f81aa21a3c Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 18 Nov 2021 15:46:56 +0200 Subject: [PATCH 095/254] moving updateQuorumNumerator to GovernorBase --- certora/specs/GovernorCountingSimple.spec | 1 - 1 file changed, 1 deletion(-) diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index c6f47d1da..59475a529 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -12,7 +12,6 @@ methods { proposalVotes(uint256) returns (uint256, uint256, uint256) envfree quorumNumerator() returns uint256 - updateQuorumNumerator(uint256) _executor() returns address erc20votes._getPastVotes(address, uint256) returns uint256 envfree From 0cbb98b92c6be44b21cbad735868302137cfaf70 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Thu, 18 Nov 2021 16:42:32 +0200 Subject: [PATCH 096/254] uncommenting hook for oneUserVotesInCast --- certora/specs/GovernorCountingSimple.spec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 59475a529..949cdc876 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -25,10 +25,10 @@ ghost hasVoteGhost(uint256) returns uint256 { init_state axiom forall uint256 pId. hasVoteGhost(pId) == 0; } -//hook Sstore _proposalVotes[KEY uint256 pId].hasVoted[KEY address user] bool current_voting_State (bool old_voting_state) STORAGE{ -// havoc hasVoteGhost assuming forall uint256 p. ((p == pId && current_voting_State && !old_voting_state) ? (hasVoteGhost@new(p) == hasVoteGhost@old(p) + 1) : -// (hasVoteGhost@new(p) == hasVoteGhost@old(p))); -//} +hook Sstore _proposalVotes[KEY uint256 pId].hasVoted[KEY address user] bool current_voting_State (bool old_voting_state) STORAGE{ + havoc hasVoteGhost assuming forall uint256 p. ((p == pId && current_voting_State && !old_voting_state) ? (hasVoteGhost@new(p) == hasVoteGhost@old(p) + 1) : + (hasVoteGhost@new(p) == hasVoteGhost@old(p))); +} ghost sum_all_votes_power() returns uint256 { init_state axiom sum_all_votes_power() == 0; From 92f5f0dfbb42363c5b156183ca39ea818f3ac022 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Fri, 19 Nov 2021 20:13:06 +0200 Subject: [PATCH 097/254] TryingToFixRules --- certora/harnesses/GovernorBasicHarness.sol | 4 -- certora/harnesses/WizardHarness1.sol | 4 ++ certora/specs/GovernorBase.spec | 23 ++++++-- certora/specs/GovernorCountingSimple.spec | 68 +++++++++------------- 4 files changed, 49 insertions(+), 50 deletions(-) diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/GovernorBasicHarness.sol index f93855929..f63aaa161 100644 --- a/certora/harnesses/GovernorBasicHarness.sol +++ b/certora/harnesses/GovernorBasicHarness.sol @@ -43,10 +43,6 @@ contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes return _votingPeriod; } - function snapshot(uint256 proposalId) public view returns (uint64) { - return _proposals[proposalId].voteStart._deadline; - } - mapping(uint256 => uint256) public ghost_sum_vote_power_by_id; function _castVote( diff --git a/certora/harnesses/WizardHarness1.sol b/certora/harnesses/WizardHarness1.sol index 0407efc79..8cbdf92c6 100644 --- a/certora/harnesses/WizardHarness1.sol +++ b/certora/harnesses/WizardHarness1.sol @@ -76,6 +76,10 @@ contract WizardHarness1 is Governor, GovernorProposalThreshold, GovernorCounting return super.castVoteWithReason(proposalId, support, reason); } + function getExecutor() public returns (address){ + return _executor(); + } + // original code function votingDelay() public view override returns (uint256) { // HARNESS: pure -> view diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index ad0626b5c..18863c4dc 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -2,6 +2,8 @@ ///////////////////// Governor.sol base definitions ////////////////////////// ////////////////////////////////////////////////////////////////////////////// +using ERC20VotesHarness as erc20votes + methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart proposalDeadline(uint256) returns uint256 envfree // matches proposalVoteEnd @@ -28,10 +30,10 @@ methods { getVotes(address, uint256) returns uint256 => DISPATCHER(true) //getVotes(address, uint256) => DISPATCHER(true) - getPastTotalSupply(uint256) returns uint256 envfree => DISPATCHER(true) + erc20votes.getPastTotalSupply(uint256) returns uint256 //getPastTotalSupply(uint256) => DISPATCHER(true) - getPastVotes(address, uint256) returns uint256 envfree => DISPATCHER(true) + erc20votes.getPastVotes(address, uint256) returns uint256 } ////////////////////////////////////////////////////////////////////////////// @@ -105,7 +107,7 @@ invariant proposalInitiated(uint256 pId) * A proposal cannot end unless it started. */ invariant voteStartBeforeVoteEnd(uint256 pId) - (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) // from < to <= because snapshot and deadline can be the same block number if delays are set to 0 && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) @@ -119,9 +121,20 @@ invariant noBothExecutedAndCanceled(uint256 pId) /** * A proposal could be executed only if quorum was reached and vote succeeded */ -invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e) - isExecuted(pId) => _quorumReached(e, pId) && _voteSucceeded(pId) +//invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e) +// isExecuted(pId) => _quorumReached(e, pId) && _voteSucceeded(pId) +rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f){ + + bool isExecutedBefore = isExecuted(pId); + + calldataarg args; + f(e, args); + + bool isExecutedAfter = isExecuted(pId); + + assert isExecutedBefore != isExecutedAfter => _quorumReached(e, pId) && _voteSucceeded(pId), "quorum was changed"; +} /////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// In-State Rules ///////////////////////////////////// diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 949cdc876..89fb1e719 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -6,15 +6,18 @@ methods { ghost_sum_vote_power_by_id(uint256) returns uint256 envfree // castVote(uint256, uint8) returns uint256 // castVoteBySig(uint256,uint8,uint8,bytes32,bytes32) returns uint256 - - snapshot(uint256) returns uint64 envfree + quorum(uint256) returns uint256 proposalVotes(uint256) returns (uint256, uint256, uint256) envfree quorumNumerator() returns uint256 _executor() returns address - erc20votes._getPastVotes(address, uint256) returns uint256 envfree + erc20votes._getPastVotes(address, uint256) returns uint256 + + getExecutor() returns address + + //0xe38335e5 => DISPATCHER(true) } ////////////////////////////////////////////////////////////////////////////// @@ -90,9 +93,17 @@ hook Sstore erc20votes._getPastVotes[KEY address uId][KEY uint256 blockNumber] u && (bn != blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn)); } -invariant checkGetVotesGhost(address uId, uint256 blockNumber) - erc20votes._getPastVotes(uId, blockNumber) == getPastVotes(uId, blockNumber) +invariant checkGetVotesGhost(address uId, uint256 blockNumber, env e) + erc20votes._getPastVotes(e, uId, blockNumber) == erc20votes.getPastVotes(e, uId, blockNumber) +rule whatCahnges(uint256 blockNumber, method f) { + env e; + calldataarg args; + uint256 ghostBefore = totalVotesPossible(blockNumber); + f(e,args); + uint256 ghostAfter = totalVotesPossible(blockNumber); + assert ghostAfter == ghostBefore, "ghost was changed"; +} ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// @@ -122,8 +133,11 @@ invariant OneIsNotMoreThanAll(uint256 pId) /* * totalVotesPossible >= votePower(id) */ -invariant possibleTotalVotes(uint256 pId) - tracked_weight(pId) <= totalVotesPossible(snapshot(pId)) +invariant possibleTotalVotes(uint256 pId, env e) + tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)) + +invariant voteGettersCheck(uint256 pId, address acc, env e) + erc20votes.getPastVotes(e, acc, proposalSnapshot(pId)) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)) /* * totalVotesPossible >= votePower(id) @@ -178,7 +192,7 @@ rule noVoteForSomeoneElse(uint256 pId, uint8 support){ assert(hasVotedBefore != hasVotedAfter => (forall address user. user != voter => !hasVoted(e, pId, user))); } -// ok + rule votingWeightMonotonicity(method f){ uint256 votingWeightBefore = sum_tracked_weight(); @@ -191,22 +205,6 @@ rule votingWeightMonotonicity(method f){ assert votingWeightBefore <= votingWeightAfter, "Voting weight was decreased somehow"; } -// ok with this branch: thomas/bool-hook-values -// can't use link because contracts are abstract, they don't have bytecode/constructor -// add implementation harness -rule quorumMonotonicity(method f, uint256 blockNumber){ - env e; - - uint256 quorumBefore = quorum(e, blockNumber); - - calldataarg args; - f(e, args); - - uint256 quorumAfter = quorum(e, blockNumber); - - assert quorumBefore <= quorumAfter, "Quorum was decreased somehow"; -} - function callFunctionWithParams(method f, uint256 proposalId, env e) { address[] targets; @@ -233,18 +231,8 @@ function callFunctionWithParams(method f, uint256 proposalId, env e) { } } - -// getVotes() returns different results. -// how to ensure that the same acc is used in getVotes() in uint256 votesBefore = getVotes(acc, bn);/uint256 votesAfter = getVotes(acc, bn); and in callFunctionWithParams -// votesBefore and votesAfter give different results but shoudn't - -// are we assuming that a person with 0 votes can vote? are we assuming that a person may have 0 votes -// it seems like a weight can be 0. At least there is no check for it -// If it can be 0 then < should be changed to <= but it gives less coverage - // run on ALex's branch to avoid tiomeouts: --staging alex/external-timeout-for-solvers // --staging shelly/forSasha -// implement ERC20Votes as a harness rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f -> f.selector != 0x7d5e81e2}{ uint256 againstBefore = votesAgainst(); uint256 forBefore = votesFor(); @@ -276,21 +264,19 @@ rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f - /* * Check privileged operations */ -// NO NEED FOR SPECIFIC CHANGES -// how to check executor()? -// to make it public instead of internal is not best idea, I think. -// currentContract gives a violation in -// maybe need harness implementation for one of the contracts -rule privilegedOnly(method f){ +rule privilegedOnly(method f, uint256 newQuorumNumerator){ env e; calldataarg arg; uint256 quorumNumBefore = quorumNumerator(e); + //require e.msg.sender == currentContract; f(e, arg); + //updateQuorumNumerator(e, newQuorumNumerator); + uint256 quorumNumAfter = quorumNumerator(e); - address executorCheck = currentContract; + address executorCheck = getExecutor(e); // address executorCheck = _executor(e); assert quorumNumBefore != quorumNumAfter => e.msg.sender == executorCheck, "non priveleged user changed quorum numerator"; From 0fbf745efe02d5f12fc4ff298d563a837a0660b6 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Sat, 20 Nov 2021 01:52:06 +0200 Subject: [PATCH 098/254] noVoteForSomeoneElse fix --- certora/specs/GovernorCountingSimple.spec | 49 +++++++++++++++-------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 89fb1e719..2c726cd2b 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -156,19 +156,21 @@ rule someOtherRuleToRemoveLater(uint256 num){ } */ + /* * Checks that only one user is updated in the system when calling cast vote functions (assuming hasVoted is changing correctly, false->true, with every vote cast) */ -rule oneUserVotesInCast(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector || f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} { - env e; calldataarg args; // ^ ^ - uint256 ghost_Before = hasVoteGhost(pId); // propose castVoteWithReason +rule oneUserVotesInCast(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector || + f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} { + env e; calldataarg args; + uint256 ghost_Before = hasVoteGhost(pId); if (f.selector == castVote(uint256, uint8).selector) - { - castVote(e, pId, sup); - } /*else if (f.selector == 0x7b3c71d3) { + { + castVote(e, pId, sup); + } /*else if (f.selector == 0x7b3c71d3) { require(_pId_Harness() == proposalId); - f(e,args); - }*/ else if (f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) { + f(e,args); + }*/ else if (f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) { uint8 v; bytes32 r; bytes32 s; castVoteBySig(e, pId, sup, v, r, s); } else{ @@ -178,18 +180,33 @@ rule oneUserVotesInCast(uint256 pId, uint8 sup, method f) filtered {f -> f.selec assert(ghost_After == ghost_Before + 1, "Raised by more than 1"); } + /* * Checks that in any call to cast vote functions only the sender's value is updated */ - -rule noVoteForSomeoneElse(uint256 pId, uint8 support){ - env e; +rule noVoteForSomeoneElse(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector || + f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} { + env e; calldataarg args; address voter = e.msg.sender; - bool hasVotedBefore = hasVoted(e, pId, voter); - require(!hasVotedBefore); - castVote(e, pId, support); - bool hasVotedAfter = hasVoted(e, pId, voter); - assert(hasVotedBefore != hasVotedAfter => (forall address user. user != voter => !hasVoted(e, pId, user))); + address user; + bool hasVotedBefore_Voter = hasVoted(e, pId, voter); + bool hasVotedBefore_User = hasVoted(e, pId, user); + require(!hasVotedBefore_Voter); + if (f.selector == castVote(uint256, uint8).selector) + { + castVote(e, pId, sup); + } /*else if (f.selector == 0x7b3c71d3) { + require(_pId_Harness() == proposalId); + f(e,args); + }*/ else if (f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) { + uint8 v; bytes32 r; bytes32 s; + castVoteBySig(e, pId, sup, v, r, s); + } else{ + f(e, args); + } + bool hasVotedAfter_Voter = hasVoted(e, pId, voter); + bool hasVotedAfter_User = hasVoted(e, pId, user); + assert hasVotedBefore_Voter != hasVotedAfter_Voter => (user != voter => hasVotedBefore_User == hasVotedAfter_User); } From a14abd027668f6dc034935b0d8f1d645499932fb Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Sat, 20 Nov 2021 02:06:01 +0200 Subject: [PATCH 099/254] hashProposal summarization removed --- certora/specs/GovernorBase.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 18863c4dc..d183536c7 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -24,7 +24,7 @@ methods { _pId_Harness() returns uint256 envfree; // function summarization - hashProposal(address[], uint256[], bytes[], bytes32) => CONSTANT + // hashProposal(address[], uint256[], bytes[], bytes32) => CONSTANT proposalThreshold() returns uint256 envfree getVotes(address, uint256) returns uint256 => DISPATCHER(true) From 167f175f3a196a2d1b3a7ec9eadcd5311173f81d Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Sun, 21 Nov 2021 18:02:26 +0200 Subject: [PATCH 100/254] harness _execute() --- contracts/governance/extensions/GovernorTimelockControl.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/governance/extensions/GovernorTimelockControl.sol b/contracts/governance/extensions/GovernorTimelockControl.sol index f7a01c06d..1a8d5a90d 100644 --- a/contracts/governance/extensions/GovernorTimelockControl.sol +++ b/contracts/governance/extensions/GovernorTimelockControl.sol @@ -109,7 +109,8 @@ abstract contract GovernorTimelockControl is IGovernorTimelock, Governor { bytes[] memory calldatas, bytes32 descriptionHash ) internal virtual override { - _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); + // HARNESS + // _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); } /** From cd703a5ee059b915dac9db352f5f041d9288c118 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Sun, 21 Nov 2021 18:03:11 +0200 Subject: [PATCH 101/254] cleaned up to doubleVoting (not included) --- certora/specs/GovernorBase.spec | 103 +++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index d183536c7..3d2a8f583 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -24,24 +24,15 @@ methods { _pId_Harness() returns uint256 envfree; // function summarization - // hashProposal(address[], uint256[], bytes[], bytes32) => CONSTANT proposalThreshold() returns uint256 envfree getVotes(address, uint256) returns uint256 => DISPATCHER(true) - //getVotes(address, uint256) => DISPATCHER(true) erc20votes.getPastTotalSupply(uint256) returns uint256 - //getPastTotalSupply(uint256) => DISPATCHER(true) erc20votes.getPastVotes(address, uint256) returns uint256 } -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////// Ghosts //////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -// ghost uniqueHashGhost(bytes32) returns uint256; - ////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Helper Functions /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -58,7 +49,7 @@ function callFunctionWithProposal(uint256 proposalId, method f) { require(result == proposalId); } else if (f.selector == castVote(uint256, uint8).selector) { castVote@withrevert(e, proposalId, support); - } else if (f.selector == 0x7b3c71d3) { + } else if (f.selector == 0x7b3c71d3 /* castVoteWithReason */) { calldataarg args; require(_pId_Harness() == proposalId); f@withrevert(e, args); @@ -92,48 +83,76 @@ function callFunctionWithProposal(uint256 proposalId, method f) { /* - * If any of the properties are non zero, the rest has to be non zero + * Start and end date are either initialized (non zero) or uninitialized (zero) simultaneously */ -invariant proposalInitiated(uint256 pId) - (proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0) && - (isCanceled(pId) => proposalSnapshot(pId) != 0) && - (isExecuted(pId) => proposalSnapshot(pId) != 0) + // To use env with general preserved block first disable type checking then + // use Uri's branch - --staging uri/add_with_env_to_preserved_all +invariant startAndEndDatesNonZero(uint256 pId) + proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0 /*{ preserved with (env e){ - require e.block.number > 0; + require e.block.number > 0; }}*/ /* - * A proposal cannot end unless it started. + * If a proposal is canceled it must have a start and an end date */ -invariant voteStartBeforeVoteEnd(uint256 pId) - (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) // from < to <= because snapshot and deadline can be the same block number if delays are set to 0 - && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) + // To use env with general preserved block first disable type checking then + // use Uri's branch - --staging uri/add_with_env_to_preserved_all +invariant canceledImplyStartAndEndDateNonZero(uint pId) + isCanceled(pId) => proposalSnapshot(pId) != 0 + /*{ preserved with (env e){ + requireInvariant startAndEndDatesNonZero(pId); + require e.block.number > 0; + }}*/ /* - * A proposal cannot be both executed and canceled. + * If a proposal is executed it must have a start and an end date + */ + // To use env with general preserved block first disable type checking then + // use Uri's branch - --staging uri/add_with_env_to_preserved_all +invariant executedImplyStartAndEndDateNonZero(uint pId) + isExecuted(pId) => proposalSnapshot(pId) != 0 + /*{ preserved with (env e){ + requireInvariant startAndEndDatesNonZero(pId); + require e.block.number > 0; + }}*/ + + +/* + * A proposal starting block number must be <= to the proposal end date + */ +invariant voteStartBeforeVoteEnd(uint256 pId) + // from < to <= because snapshot and deadline can be the same block number if delays are set to 0 + // This is possible before the integration of GovernorSettings.sol to the system. + // After integration of GovernorSettings.sol the invariant expression should be changed from <= to < + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) + { preserved { + requireInvariant startAndEndDatesNonZero(pId); + }} + + +/* + * A proposal cannot be both executed and canceled simultaneously. */ invariant noBothExecutedAndCanceled(uint256 pId) !isExecuted(pId) || !isCanceled(pId) -/** +/* * A proposal could be executed only if quorum was reached and vote succeeded */ -//invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e) -// isExecuted(pId) => _quorumReached(e, pId) && _voteSucceeded(pId) - + // the executeBatch line in _execute was commented in GovernorTimelockContril.sol rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f){ bool isExecutedBefore = isExecuted(pId); - + calldataarg args; f(e, args); bool isExecutedAfter = isExecuted(pId); - - assert isExecutedBefore != isExecutedAfter => _quorumReached(e, pId) && _voteSucceeded(pId), "quorum was changed"; + assert ((isExecutedBefore != isExecutedAfter) && !isExecutedBefore) => (_quorumReached(e, pId) && _voteSucceeded(pId)), "quorum was changed"; } /////////////////////////////////////////////////////////////////////////////////////// @@ -144,19 +163,31 @@ rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f){ //------------- Voting Period -------------- //========================================== - /* * A user cannot vote twice */ -rule doubleVoting(uint256 pId, uint8 sup) { - env e; +rule doubleVoting(uint256 pId, uint8 sup, method f) filtered { f-> f.selector == castVote(uint256, uint8).selector || + f.selector == castVoteWithReason(uint256, uint8, string).selector || + f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} { + env e; calldataarg args; address user = e.msg.sender; bool votedCheck = hasVoted(e, pId, user); - castVote@withrevert(e, pId, sup); - bool reverted = lastReverted; + if (f.selector == castVote(uint256, uint8).selector) + { + castVote@withrevert(e, pId, sup); + } else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) { + string reason; + castVoteWithReason@withrevert(e, pId, sup, reason); + } else if (f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) { + uint8 v; bytes32 r; bytes32 s; + castVoteBySig@withrevert(e, pId, sup, v, r, s); + } else{ + f@withrevert(e, args); + } - assert votedCheck => reverted, "double voting accured"; + + assert votedCheck => lastReverted, "double voting accured"; } @@ -287,7 +318,7 @@ rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selec env e; calldataarg args; // ^ ^ uint256 pId; // propose updateTimelock require(isExecuted(pId)); - requireInvariant proposalInitiated(pId); + // requireInvariant proposalInitiated(pId); requireInvariant noBothExecutedAndCanceled(pId); callFunctionWithProposal(pId, f); assert(lastReverted, "Function was not reverted"); @@ -301,7 +332,7 @@ rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selec uint256 pId; // propose updateTimelock require(isCanceled(pId)); requireInvariant noBothExecutedAndCanceled(pId); - requireInvariant proposalInitiated(pId); + // requireInvariant proposalInitiated(pId); callFunctionWithProposal(pId, f); assert(lastReverted, "Function was not reverted"); } From 37fe8c292abc6880ee23dd71ac1cf7e3aec83111 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 22 Nov 2021 09:51:11 +0200 Subject: [PATCH 102/254] FixinigTimeoutsAndTotalVotes --- certora/harnesses/WizardHarness1.sol | 2 +- certora/specs/GovernorBase.spec | 8 ++++- certora/specs/GovernorCountingSimple.spec | 34 +++++++++++++++---- .../extensions/GovernorTimelockControl.sol | 3 +- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/certora/harnesses/WizardHarness1.sol b/certora/harnesses/WizardHarness1.sol index 8cbdf92c6..ccc1d7b34 100644 --- a/certora/harnesses/WizardHarness1.sol +++ b/certora/harnesses/WizardHarness1.sol @@ -76,7 +76,7 @@ contract WizardHarness1 is Governor, GovernorProposalThreshold, GovernorCounting return super.castVoteWithReason(proposalId, support, reason); } - function getExecutor() public returns (address){ + function getExecutor() public view returns (address){ return _executor(); } diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 3d2a8f583..c2ae24606 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -14,6 +14,7 @@ methods { hasVoted(uint256, address) returns bool castVote(uint256, uint8) returns uint256 updateQuorumNumerator(uint256) + queue(address[], uint256[], bytes[], bytes32) returns uint256 // internal functions made public in harness: @@ -29,8 +30,10 @@ methods { getVotes(address, uint256) returns uint256 => DISPATCHER(true) erc20votes.getPastTotalSupply(uint256) returns uint256 - erc20votes.getPastVotes(address, uint256) returns uint256 + + scheduleBatch(address[],uint256[],bytes[],bytes32,bytes32,uint256) => NONDET + executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => NONDET } ////////////////////////////////////////////////////////////////////////////// @@ -55,6 +58,9 @@ function callFunctionWithProposal(uint256 proposalId, method f) { f@withrevert(e, args); } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) { castVoteBySig@withrevert(e, proposalId, support, v, r, s); + } else if (f.selector == queue(address[], uint256[], bytes[], bytes32).selector) { + require targets.length <= 1 && values.length <= 1 && calldatas.length <= 1; + queue@withrevert(e, targets, values, calldatas, descriptionHash); } else { calldataarg args; f@withrevert(e, args); diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 2c726cd2b..62a46b60e 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -16,8 +16,6 @@ methods { erc20votes._getPastVotes(address, uint256) returns uint256 getExecutor() returns address - - //0xe38335e5 => DISPATCHER(true) } ////////////////////////////////////////////////////////////////////////////// @@ -133,11 +131,35 @@ invariant OneIsNotMoreThanAll(uint256 pId) /* * totalVotesPossible >= votePower(id) */ -invariant possibleTotalVotes(uint256 pId, env e) - tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)) +//invariant possibleTotalVotes(uint256 pId, env e) +// tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)) -invariant voteGettersCheck(uint256 pId, address acc, env e) - erc20votes.getPastVotes(e, acc, proposalSnapshot(pId)) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)) +rule possibleTotalVotes(uint256 pId, env e, method f) { + require tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)); + + calldataarg args; + f(e, args); + + assert tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)), "bla bla bla"; +} + +//invariant voteGettersCheck(uint256 pId, address acc, env e) +// erc20votes.getPastVotes(e, acc, proposalSnapshot(pId)) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)) + +rule voteGettersCheck(uint256 pId, address acc, env e, method f){ + address[] targets; + uint256[] values; + bytes[] calldatas; + + require erc20votes.getPastVotes(e, acc, proposalSnapshot(pId)) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)); + + uint256 result = callPropose(e, targets, values, calldatas); + + require result == pId; + + assert erc20votes.getPastVotes(e, acc, proposalSnapshot(pId)) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)), + "getPastVotes is greater"; +} /* * totalVotesPossible >= votePower(id) diff --git a/contracts/governance/extensions/GovernorTimelockControl.sol b/contracts/governance/extensions/GovernorTimelockControl.sol index 1a8d5a90d..b75879b19 100644 --- a/contracts/governance/extensions/GovernorTimelockControl.sol +++ b/contracts/governance/extensions/GovernorTimelockControl.sol @@ -92,7 +92,8 @@ abstract contract GovernorTimelockControl is IGovernorTimelock, Governor { uint256 delay = _timelock.getMinDelay(); _timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash); - _timelock.scheduleBatch(targets, values, calldatas, 0, descriptionHash, delay); + // HARNESS + //_timelock.scheduleBatch(targets, values, calldatas, 0, descriptionHash, delay); emit ProposalQueued(proposalId, block.timestamp + delay); From ff8e17ec2f4cb717af7cb568fab39703f440353f Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 22 Nov 2021 12:17:41 +0200 Subject: [PATCH 103/254] removedHarnessesAnsSummariesAddedComments --- certora/specs/GovernorBase.spec | 7 +++---- contracts/governance/Governor.sol | 2 +- contracts/governance/TimelockController.sol | 1 + .../governance/extensions/GovernorTimelockControl.sol | 6 ++---- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index c2ae24606..b976c790b 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -32,8 +32,8 @@ methods { erc20votes.getPastTotalSupply(uint256) returns uint256 erc20votes.getPastVotes(address, uint256) returns uint256 - scheduleBatch(address[],uint256[],bytes[],bytes32,bytes32,uint256) => NONDET - executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => NONDET + //scheduleBatch(address[],uint256[],bytes[],bytes32,bytes32,uint256) => DISPATCHER(true) + //executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) } ////////////////////////////////////////////////////////////////////////////// @@ -191,8 +191,7 @@ rule doubleVoting(uint256 pId, uint8 sup, method f) filtered { f-> f.selector == } else{ f@withrevert(e, args); } - - + assert votedCheck => lastReverted, "double voting accured"; } diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index f8bbb42c4..e8d369452 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -320,7 +320,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { v, r, s - ); + ); // mention that we assume that hashing works correctly return _castVote(proposalId, voter, support, ""); } diff --git a/contracts/governance/TimelockController.sol b/contracts/governance/TimelockController.sol index affcbbdc6..b3b551dde 100644 --- a/contracts/governance/TimelockController.sol +++ b/contracts/governance/TimelockController.sol @@ -299,6 +299,7 @@ contract TimelockController is AccessControl { _call(id, i, targets[i], values[i], datas[i]); } _afterCall(id); + // ASSUME THAT THERE IS NO REENTRANCY IN WIZARDHARNESS1 } /** diff --git a/contracts/governance/extensions/GovernorTimelockControl.sol b/contracts/governance/extensions/GovernorTimelockControl.sol index b75879b19..892ec3a55 100644 --- a/contracts/governance/extensions/GovernorTimelockControl.sol +++ b/contracts/governance/extensions/GovernorTimelockControl.sol @@ -92,8 +92,7 @@ abstract contract GovernorTimelockControl is IGovernorTimelock, Governor { uint256 delay = _timelock.getMinDelay(); _timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash); - // HARNESS - //_timelock.scheduleBatch(targets, values, calldatas, 0, descriptionHash, delay); + _timelock.scheduleBatch(targets, values, calldatas, 0, descriptionHash, delay); emit ProposalQueued(proposalId, block.timestamp + delay); @@ -110,8 +109,7 @@ abstract contract GovernorTimelockControl is IGovernorTimelock, Governor { bytes[] memory calldatas, bytes32 descriptionHash ) internal virtual override { - // HARNESS - // _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); + _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); } /** From 95321a3516fe988fb17e3f163b103696852a14a1 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Mon, 22 Nov 2021 17:35:13 +0200 Subject: [PATCH 104/254] done up to noStartBeforeCreation including --- certora/specs/GovernorBase.spec | 98 +++++++++++++-------------------- 1 file changed, 37 insertions(+), 61 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index b976c790b..d2d7adf44 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -36,6 +36,8 @@ methods { //executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) } +definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0; + ////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Helper Functions /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -90,6 +92,9 @@ function callFunctionWithProposal(uint256 proposalId, method f) { /* * Start and end date are either initialized (non zero) or uninitialized (zero) simultaneously + * This invariant assumes that the block number cannot be 0 at any stage of the contract cycle + * This is very safe assumption as usually the 0 block is genesis block which is uploaded with data + * by the developers and will not be valid to raise proposals (at the current way that block chain is functioning) */ // To use env with general preserved block first disable type checking then // use Uri's branch - --staging uri/add_with_env_to_preserved_all @@ -108,7 +113,7 @@ invariant startAndEndDatesNonZero(uint256 pId) invariant canceledImplyStartAndEndDateNonZero(uint pId) isCanceled(pId) => proposalSnapshot(pId) != 0 /*{ preserved with (env e){ - requireInvariant startAndEndDatesNonZero(pId); + requireInvariant startAndEndDatesNonZero(pId); //@note maybe unndeeded require e.block.number > 0; }}*/ @@ -121,7 +126,7 @@ invariant canceledImplyStartAndEndDateNonZero(uint pId) invariant executedImplyStartAndEndDateNonZero(uint pId) isExecuted(pId) => proposalSnapshot(pId) != 0 /*{ preserved with (env e){ - requireInvariant startAndEndDatesNonZero(pId); + requireInvariant startAndEndDatesNonZero(pId); //@note maybe unndeeded require e.block.number > 0; }}*/ @@ -149,16 +154,16 @@ invariant noBothExecutedAndCanceled(uint256 pId) /* * A proposal could be executed only if quorum was reached and vote succeeded */ - // the executeBatch line in _execute was commented in GovernorTimelockContril.sol rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f){ - bool isExecutedBefore = isExecuted(pId); + bool quorumReachedBefore = _quorumReached(e, pId); + bool voteSucceededBefore = _voteSucceeded(pId); calldataarg args; f(e, args); bool isExecutedAfter = isExecuted(pId); - assert ((isExecutedBefore != isExecutedAfter) && !isExecutedBefore) => (_quorumReached(e, pId) && _voteSucceeded(pId)), "quorum was changed"; + assert (!isExecutedBefore && isExecutedAfter) => (quorumReachedBefore && voteSucceededBefore), "quorum was changed"; } /////////////////////////////////////////////////////////////////////////////////////// @@ -172,26 +177,19 @@ rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f){ /* * A user cannot vote twice */ -rule doubleVoting(uint256 pId, uint8 sup, method f) filtered { f-> f.selector == castVote(uint256, uint8).selector || - f.selector == castVoteWithReason(uint256, uint8, string).selector || - f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} { - env e; calldataarg args; + // Checked for castVote only. all 3 castVote functions call _castVote, so the completness of the verification is counted on + // the fact that the 3 functions themselves makes no chages, but rather call an internal function to execute. + // That means that we do not check those 3 functions directly, however for castVote & castVoteWithReason it is quite trivial + // to understand why this is ok. For castVoteBySig we basically assume that the signature referendum is correct without checking it. + // We could check each function seperately and pass the rule, but that would have uglyfied the code with no concrete + // benefit, as it is evident that nothing is happening in the first 2 functions (calling a view function), and we do not desire to check the signature verification. +rule doubleVoting(uint256 pId, uint8 sup, method f) { + env e; address user = e.msg.sender; bool votedCheck = hasVoted(e, pId, user); - if (f.selector == castVote(uint256, uint8).selector) - { - castVote@withrevert(e, pId, sup); - } else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) { - string reason; - castVoteWithReason@withrevert(e, pId, sup, reason); - } else if (f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) { - uint8 v; bytes32 r; bytes32 s; - castVoteBySig@withrevert(e, pId, sup, v, r, s); - } else{ - f@withrevert(e, args); - } - + castVote@withrevert(e, pId, sup); + assert votedCheck => lastReverted, "double voting accured"; } @@ -205,22 +203,6 @@ rule doubleVoting(uint256 pId, uint8 sup, method f) filtered { f-> f.selector == //=========================================== -/* - * The voting must start not before the proposal’s creation time - */ -rule noStartBeforeCreation(uint256 pId) { - uint256 previousStart = proposalSnapshot(pId); - require previousStart == 0; - env e; - calldataarg arg; - propose(e, arg); - - uint newStart = proposalSnapshot(pId); - // if created, start is after current block number (creation block) - assert(newStart != previousStart => newStart >= e.block.number); -} - - /* * Once a proposal is created, voteStart and voteEnd are immutable */ @@ -240,6 +222,23 @@ rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { } +/* + * Voting cannot start at a block number prior to proposal’s creation block number + */ +rule noStartBeforeCreation(uint256 pId) { + uint256 previousStart = proposalSnapshot(pId); + // This line makes sure that we see only cases where start date is changed from 0, i.e. creation of proposal + // We proved in immutableFieldsAfterProposalCreation that once dates set for proposal, it cannot be changed + require previousStart == 0; + env e; calldataarg arg; + propose(e, arg); + + uint newStart = proposalSnapshot(pId); + // if created, start is after current block number (creation block) + assert(newStart != previousStart => newStart >= e.block.number); +} + + /* * A proposal cannot be neither executed nor canceled before it starts */ @@ -278,29 +277,6 @@ rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ ////////////////////// Integrity Of Functions (Unit Tests) ///////////////////// //////////////////////////////////////////////////////////////////////////////// -/** - * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) - */ -/* -rule checkHashProposal { - address[] t1; - address[] t2; - uint256[] v1; - uint256[] v2; - bytes[] c1; - bytes[] c2; - bytes32 d1; - bytes32 d2; - - uint256 h1 = hashProposal(t1,v1,c1,d1); - uint256 h2 = hashProposal(t2,v2,c2,d2); - bool equalHashes = h1 == h2; - assert equalHashes => t1.length == t2.length; - assert equalHashes => v1.length == v2.length; - assert equalHashes => c1.length == c2.length; - assert equalHashes => d1 == d2; -} -*/ //////////////////////////////////////////////////////////////////////////////// ////////////////////////////// High Level Rules //////////////////////////////// From 1b4fb6c758c4b0080cfae2e409ccdbca8bbf9229 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Tue, 23 Nov 2021 11:48:07 +0200 Subject: [PATCH 105/254] callPropose and castVoteWithReason removed --- certora/harnesses/WizardHarness1.sol | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/certora/harnesses/WizardHarness1.sol b/certora/harnesses/WizardHarness1.sol index ccc1d7b34..346e18360 100644 --- a/certora/harnesses/WizardHarness1.sol +++ b/certora/harnesses/WizardHarness1.sol @@ -53,29 +53,13 @@ contract WizardHarness1 is Governor, GovernorProposalThreshold, GovernorCounting return deltaWeight; } - - function callPropose(address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas) public virtual returns (uint256) { - return super.propose(targets, values, calldatas, ""); - } + function snapshot(uint256 proposalId) public view returns (uint64) { return _proposals[proposalId].voteStart._deadline; } - // Harness of castVoteWithReason to be able to impose requirement on the proposal ID. - uint256 public _pId_Harness; - function castVoteWithReason(uint256 proposalId, uint8 support, string calldata reason) - public - override(IGovernor, Governor) - returns (uint256) - { - require(proposalId == _pId_Harness); - return super.castVoteWithReason(proposalId, support, reason); - } - function getExecutor() public view returns (address){ return _executor(); } From e01b285780b816bc56b0f470dd0b1497a707be12 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Tue, 23 Nov 2021 11:48:58 +0200 Subject: [PATCH 106/254] helper function fix plus reviewing up to noExecuteOrCancelBeforeDeadline including --- certora/specs/GovernorBase.spec | 112 ++++++++++++++++---------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index d2d7adf44..89cb88a3e 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -16,14 +16,10 @@ methods { updateQuorumNumerator(uint256) queue(address[], uint256[], bytes[], bytes32) returns uint256 - // internal functions made public in harness: _quorumReached(uint256) returns bool _voteSucceeded(uint256) returns bool envfree - - _pId_Harness() returns uint256 envfree; - // function summarization proposalThreshold() returns uint256 envfree @@ -36,28 +32,44 @@ methods { //executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) } +////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// Definitions ///////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +// definition proposalNotExist(uint256 pId) returns bool = +// !isExecuted(pId) && !isCanceled(pId) && proposalSnapshot(pId) == 0 && proposalDeadline(pId) == 0; + +// definition proposalCreatedAndRunning(uint256 pId) returns bool = +// !isExecuted(pId) && !isCanceled(pId) && proposalSnapshot(pId) != 0 && proposalDeadline(pId) != 0; + +// definition proposalCanceled(uint256 pId) returns bool = +// !isExecuted(pId) && isCanceled(pId) && proposalSnapshot(pId) != 0 && proposalDeadline(pId) != 0; + +// definition proposalExecuted(uint256 pId) returns bool = +// isExecuted(pId) && !isCanceled(pId) && proposalSnapshot(pId) != 0 && proposalDeadline(pId) != 0; + +// proposal was created - relation proved in noStartBeforeCreation definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0; + ////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Helper Functions /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// function callFunctionWithProposal(uint256 proposalId, method f) { - address[] targets; uint256[] values; bytes[] calldatas; bytes32 descriptionHash; + address[] targets; uint256[] values; bytes[] calldatas; string reason; bytes32 descriptionHash; uint8 support; uint8 v; bytes32 r; bytes32 s; env e; - if (f.selector == callPropose(address[], uint256[], bytes[]).selector) { - uint256 result = callPropose@withrevert(e, targets, values, calldatas); - require(proposalId == result); + if (f.selector == propose(address[], uint256[], bytes[], string).selector) { + uint256 result = propose@withrevert(e, targets, values, calldatas, reason); + require(result == proposalId); } else if (f.selector == execute(address[], uint256[], bytes[], bytes32).selector) { uint256 result = execute@withrevert(e, targets, values, calldatas, descriptionHash); require(result == proposalId); } else if (f.selector == castVote(uint256, uint8).selector) { castVote@withrevert(e, proposalId, support); - } else if (f.selector == 0x7b3c71d3 /* castVoteWithReason */) { - calldataarg args; - require(_pId_Harness() == proposalId); - f@withrevert(e, args); + } else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) { + castVoteWithReason@withrevert(e, proposalId, support, reason); } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) { castVoteBySig@withrevert(e, proposalId, support, v, r, s); } else if (f.selector == queue(address[], uint256[], bytes[], bytes32).selector) { @@ -207,18 +219,18 @@ rule doubleVoting(uint256 pId, uint8 sup, method f) { * Once a proposal is created, voteStart and voteEnd are immutable */ rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint _voteStart = proposalSnapshot(pId); - uint _voteEnd = proposalDeadline(pId); - require _voteStart > 0; // proposal was created - relation proved in noStartBeforeCreation + uint256 _voteStart = proposalSnapshot(pId); + uint256 _voteEnd = proposalDeadline(pId); - env e; - calldataarg arg; + require proposalCreated(pId); // startDate > 0 + + env e; calldataarg arg; f(e, arg); - uint voteStart_ = proposalSnapshot(pId); - uint voteEnd_ = proposalDeadline(pId); - assert _voteStart == voteStart_; - assert _voteEnd == voteEnd_; + uint256 voteStart_ = proposalSnapshot(pId); + uint256 voteEnd_ = proposalDeadline(pId); + assert _voteStart == voteStart_, "Start date was changed"; + assert _voteEnd == voteEnd_, "End date was changed"; } @@ -229,45 +241,30 @@ rule noStartBeforeCreation(uint256 pId) { uint256 previousStart = proposalSnapshot(pId); // This line makes sure that we see only cases where start date is changed from 0, i.e. creation of proposal // We proved in immutableFieldsAfterProposalCreation that once dates set for proposal, it cannot be changed - require previousStart == 0; + require !proposalCreated(pId); // previousStart == 0; + env e; calldataarg arg; propose(e, arg); - uint newStart = proposalSnapshot(pId); + uint256 newStart = proposalSnapshot(pId); // if created, start is after current block number (creation block) assert(newStart != previousStart => newStart >= e.block.number); } -/* - * A proposal cannot be neither executed nor canceled before it starts - */ -rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){ - env e; - - require !isExecuted(pId) && !isCanceled(pId); - - calldataarg arg; - f(e, arg); - - assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start"; -} - //============================================ //--- End of Voting Period --> End of Time --- //============================================ /* - * A proposal cannot be neither executed nor canceled before proposal's deadline + * A proposal can neither be executed nor canceled before it ends */ + // By induction it cannot be executed nor canceled before it starts, due to voteStartBeforeVoteEnd rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ - env e; - - requireInvariant voteStartBeforeVoteEnd(pId); require !isExecuted(pId) && !isCanceled(pId); - calldataarg arg; + env e; calldataarg arg; f(e, arg); assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before deadline"; @@ -289,32 +286,32 @@ rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ /* - * all non-view functions should revert if proposal is executed + * All non-view functions should revert if proposal is executed */ -// summarization - hashProposal => Const - for any set of arguments passed to the function the same value will be returned. -// that means that for different arguments passed, the same value will be returned, for example: func(a,b,c,d) == func(o,p,g,r) -// the summarization is not an under estimation in this case, because we want to check that for a specific proposal ID (pId), any -// (non view) function call is reverting. We dont care what happen with other pIds, and dont care how the hash function generates the ID. -rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selector != 0x7d5e81e2 && !f.isFallback && f.selector != updateQuorumNumerator(uint256).selector && f.selector != 0xa890c910} { - env e; calldataarg args; // ^ ^ - uint256 pId; // propose updateTimelock +rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selector != updateQuorumNumerator(uint256).selector && + !f.isFallback && f.selector != updateTimelock(address).selector} { + env e; calldataarg args; + uint256 pId; require(isExecuted(pId)); - // requireInvariant proposalInitiated(pId); requireInvariant noBothExecutedAndCanceled(pId); + callFunctionWithProposal(pId, f); + assert(lastReverted, "Function was not reverted"); } /* - * all non-view functions should revert if proposal is canceled + * All non-view functions should revert if proposal is canceled */ -rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selector != 0x7d5e81e2 && !f.isFallback && f.selector != updateQuorumNumerator(uint256).selector && f.selector != 0xa890c910} { - env e; calldataarg args; // ^ ^ - uint256 pId; // propose updateTimelock +rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selector != updateQuorumNumerator(uint256).selector && + !f.isFallback && f.selector != updateTimelock(address).selector} { + env e; calldataarg args; + uint256 pId; require(isCanceled(pId)); requireInvariant noBothExecutedAndCanceled(pId); - // requireInvariant proposalInitiated(pId); + callFunctionWithProposal(pId, f); + assert(lastReverted, "Function was not reverted"); } @@ -326,9 +323,10 @@ rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] c uint256 pId; bool executedBefore = isExecuted(pId); require(!executedBefore); + callFunctionWithProposal(pId, f); require(!lastReverted); - // execute(e, targets, values, calldatas, descriptionHash); + bool executedAfter = isExecuted(pId); assert(executedAfter != executedBefore, "executed property did not change"); } From c38babecd9b049ab0922eca07bde05ba0d1912da Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Tue, 23 Nov 2021 12:47:21 +0200 Subject: [PATCH 107/254] helper function name change --- certora/specs/GovernorBase.spec | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 89cb88a3e..9a986877d 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -56,7 +56,7 @@ definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0 ///////////////////////////// Helper Functions /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -function callFunctionWithProposal(uint256 proposalId, method f) { +function helperFunctionWithRevert(uint256 proposalId, method f) { address[] targets; uint256[] values; bytes[] calldatas; string reason; bytes32 descriptionHash; uint8 support; uint8 v; bytes32 r; bytes32 s; env e; @@ -294,8 +294,9 @@ rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selec uint256 pId; require(isExecuted(pId)); requireInvariant noBothExecutedAndCanceled(pId); + requireInvariant executedImplyStartAndEndDateNonZero(pId); - callFunctionWithProposal(pId, f); + helperFunctionWithRevert(pId, f); assert(lastReverted, "Function was not reverted"); } @@ -309,8 +310,9 @@ rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selec uint256 pId; require(isCanceled(pId)); requireInvariant noBothExecutedAndCanceled(pId); + requireInvariant canceledImplyStartAndEndDateNonZero(pId); - callFunctionWithProposal(pId, f); + helperFunctionWithRevert(pId, f); assert(lastReverted, "Function was not reverted"); } @@ -324,25 +326,9 @@ rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] c bool executedBefore = isExecuted(pId); require(!executedBefore); - callFunctionWithProposal(pId, f); + helperFunctionWithRevert(pId, f); require(!lastReverted); - + bool executedAfter = isExecuted(pId); assert(executedAfter != executedBefore, "executed property did not change"); } - - -/* -* User should not be able to affect proposal threshold -*/ -rule unaffectedThreshhold(method f){ - uint256 thresholdBefore = proposalThreshold(); - - env e; - calldataarg args; - f(e, args); - - uint256 thresholdAfter = proposalThreshold(); - - assert thresholdBefore == thresholdAfter, "threshold was changed"; -} From 38e42f92c26ad12625315c80bf8ae91ac1eaf3ec Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 23 Nov 2021 12:52:51 +0200 Subject: [PATCH 108/254] helperFunctionArgumentEnv --- certora/specs/GovernorBase.spec | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 9a986877d..cf5086061 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -36,17 +36,6 @@ methods { //////////////////////////////// Definitions ///////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -// definition proposalNotExist(uint256 pId) returns bool = -// !isExecuted(pId) && !isCanceled(pId) && proposalSnapshot(pId) == 0 && proposalDeadline(pId) == 0; - -// definition proposalCreatedAndRunning(uint256 pId) returns bool = -// !isExecuted(pId) && !isCanceled(pId) && proposalSnapshot(pId) != 0 && proposalDeadline(pId) != 0; - -// definition proposalCanceled(uint256 pId) returns bool = -// !isExecuted(pId) && isCanceled(pId) && proposalSnapshot(pId) != 0 && proposalDeadline(pId) != 0; - -// definition proposalExecuted(uint256 pId) returns bool = -// isExecuted(pId) && !isCanceled(pId) && proposalSnapshot(pId) != 0 && proposalDeadline(pId) != 0; // proposal was created - relation proved in noStartBeforeCreation definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0; @@ -56,10 +45,9 @@ definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0 ///////////////////////////// Helper Functions /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -function helperFunctionWithRevert(uint256 proposalId, method f) { +function helperFunctionsWithRevert(uint256 proposalId, method f, env e) { address[] targets; uint256[] values; bytes[] calldatas; string reason; bytes32 descriptionHash; uint8 support; uint8 v; bytes32 r; bytes32 s; - env e; if (f.selector == propose(address[], uint256[], bytes[], string).selector) { uint256 result = propose@withrevert(e, targets, values, calldatas, reason); require(result == proposalId); @@ -296,7 +284,7 @@ rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selec requireInvariant noBothExecutedAndCanceled(pId); requireInvariant executedImplyStartAndEndDateNonZero(pId); - helperFunctionWithRevert(pId, f); + helperFunctionsWithRevert(pId, f, e); assert(lastReverted, "Function was not reverted"); } @@ -312,7 +300,7 @@ rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selec requireInvariant noBothExecutedAndCanceled(pId); requireInvariant canceledImplyStartAndEndDateNonZero(pId); - helperFunctionWithRevert(pId, f); + helperFunctionsWithRevert(pId, f, e); assert(lastReverted, "Function was not reverted"); } @@ -326,7 +314,7 @@ rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] c bool executedBefore = isExecuted(pId); require(!executedBefore); - helperFunctionWithRevert(pId, f); + helperFunctionsWithRevert(pId, f, e); require(!lastReverted); bool executedAfter = isExecuted(pId); From 4c3ad9c95a290b7cb14f8a338d380ecda8e5af10 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 23 Nov 2021 15:27:22 +0200 Subject: [PATCH 109/254] GovernorCountingSimple cleaning --- certora/specs/GovernorCountingSimple.spec | 256 +++++++++------------- 1 file changed, 106 insertions(+), 150 deletions(-) diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 62a46b60e..e53313988 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -4,8 +4,6 @@ using ERC20VotesHarness as erc20votes methods { ghost_sum_vote_power_by_id(uint256) returns uint256 envfree - // castVote(uint256, uint8) returns uint256 - // castVoteBySig(uint256,uint8,uint8,bytes32,bytes32) returns uint256 quorum(uint256) returns uint256 proposalVotes(uint256) returns (uint256, uint256, uint256) envfree @@ -16,12 +14,19 @@ methods { erc20votes._getPastVotes(address, uint256) returns uint256 getExecutor() returns address + + timelock() returns address } + ////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// GHOSTS ///////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + +/* + * ghost to keep track of changes in hasVoted status of users + */ ghost hasVoteGhost(uint256) returns uint256 { init_state axiom forall uint256 pId. hasVoteGhost(pId) == 0; } @@ -31,6 +36,13 @@ hook Sstore _proposalVotes[KEY uint256 pId].hasVoted[KEY address user] bool curr (hasVoteGhost@new(p) == hasVoteGhost@old(p))); } + + +//////////// ghosts to keep track of votes counting //////////// + +/* + * the sum of voting power of those who voted + */ ghost sum_all_votes_power() returns uint256 { init_state axiom sum_all_votes_power() == 0; } @@ -39,21 +51,37 @@ hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(u havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; } +/* + * sum of all votes casted per proposal + */ ghost tracked_weight(uint256) returns uint256 { init_state axiom forall uint256 p. tracked_weight(p) == 0; } + +/* + * sum of all votes casted + */ ghost sum_tracked_weight() returns uint256 { init_state axiom sum_tracked_weight() == 0; } +/* + * getter for _proposalVotes.againstVotes + */ ghost votesAgainst() returns uint256 { init_state axiom votesAgainst() == 0; } +/* + * getter for _proposalVotes.forVotes + */ ghost votesFor() returns uint256 { init_state axiom votesFor() == 0; } +/* + * getter for _proposalVotes.abstainVotes + */ ghost votesAbstain() returns uint256 { init_state axiom votesAbstain() == 0; } @@ -80,29 +108,6 @@ hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 } -ghost totalVotesPossible(uint256) returns uint256 { - init_state axiom forall uint256 bn. totalVotesPossible(bn) == 0; -} - -// Was done with _getVotes by mistake. Should check GovernorBasicHarness but _getVotes is from GovernorHarness -hook Sstore erc20votes._getPastVotes[KEY address uId][KEY uint256 blockNumber] uint256 voteWeight(uint256 old_voteWeight) STORAGE { - havoc totalVotesPossible assuming forall uint256 bn. - (bn == blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn) - old_voteWeight + voteWeight) - && (bn != blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn)); -} - -invariant checkGetVotesGhost(address uId, uint256 blockNumber, env e) - erc20votes._getPastVotes(e, uId, blockNumber) == erc20votes.getPastVotes(e, uId, blockNumber) - -rule whatCahnges(uint256 blockNumber, method f) { - env e; - calldataarg args; - uint256 ghostBefore = totalVotesPossible(blockNumber); - f(e,args); - uint256 ghostAfter = totalVotesPossible(blockNumber); - assert ghostAfter == ghostBefore, "ghost was changed"; -} - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -113,125 +118,100 @@ rule whatCahnges(uint256 blockNumber, method f) { */ invariant SumOfVotesCastEqualSumOfPowerOfVotedPerProposal(uint256 pId) tracked_weight(pId) == ghost_sum_vote_power_by_id(pId) - + + /* * sum of all votes casted is equal to the sum of voting power of those who voted */ invariant SumOfVotesCastEqualSumOfPowerOfVoted() sum_tracked_weight() == sum_all_votes_power() - + + /* -* totalVoted >= vote(id) +* sum of all votes casted is greater or equal to the sum of voting power of those who voted at a specific proposal */ invariant OneIsNotMoreThanAll(uint256 pId) sum_all_votes_power() >= tracked_weight(pId) -//NEED GHOST FIX -/* -* totalVotesPossible >= votePower(id) -*/ -//invariant possibleTotalVotes(uint256 pId, env e) -// tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)) +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// RULES ////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// -rule possibleTotalVotes(uint256 pId, env e, method f) { +//NOT FINISHED +/* +* the sum of voting power of those who voted is less or equal to the maximum possible votes, per each proposal +*/ +rule possibleTotalVotes(uint256 pId, uint8 sup, env e, method f) { + + // add requireinvariant for all i, j. i = i - 1 && i < j => checkpointlookup[i] < checkpointlookup[j]; require tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)); + uint256 againstB; + uint256 forB; + uint256 absatinB; + againstB, forB, absatinB = proposalVotes(pId); + calldataarg args; - f(e, args); + //f(e, args); + + castVote(e, pId, sup); + + uint256 against; + uint256 for; + uint256 absatin; + against, for, absatin = proposalVotes(pId); + + uint256 ps = proposalSnapshot(pId); assert tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)), "bla bla bla"; } -//invariant voteGettersCheck(uint256 pId, address acc, env e) -// erc20votes.getPastVotes(e, acc, proposalSnapshot(pId)) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)) - -rule voteGettersCheck(uint256 pId, address acc, env e, method f){ - address[] targets; - uint256[] values; - bytes[] calldatas; - - require erc20votes.getPastVotes(e, acc, proposalSnapshot(pId)) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)); - - uint256 result = callPropose(e, targets, values, calldatas); - - require result == pId; - - assert erc20votes.getPastVotes(e, acc, proposalSnapshot(pId)) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)), - "getPastVotes is greater"; -} - -/* -* totalVotesPossible >= votePower(id) -*/ -// invariant possibleTotalVotes(uint pId) -//invariant trackedVsTotal(uint256 pId) -// tracked_weight(pId) <= possibleMaxOfVoters(pId) - - -/* -rule someOtherRuleToRemoveLater(uint256 num){ - env e; calldataarg args; method f; - uint256 x = hasVoteGhost(num); - f(e, args); - assert(false); -} -*/ - /* * Checks that only one user is updated in the system when calling cast vote functions (assuming hasVoted is changing correctly, false->true, with every vote cast) */ -rule oneUserVotesInCast(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector || - f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} { +rule oneUserVotesInCast(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector + || f.selector == castVoteWithReason(uint256, uint8, string).selector + || f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} { env e; calldataarg args; - uint256 ghost_Before = hasVoteGhost(pId); - if (f.selector == castVote(uint256, uint8).selector) - { - castVote(e, pId, sup); - } /*else if (f.selector == 0x7b3c71d3) { - require(_pId_Harness() == proposalId); - f(e,args); - }*/ else if (f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) { - uint8 v; bytes32 r; bytes32 s; - castVoteBySig(e, pId, sup, v, r, s); - } else{ - f(e, args); - } + uint256 ghost_Before = hasVoteGhost(pId); + + helperFunctionsWithRevert(pId, f, e); + require(!lastReverted); + uint256 ghost_After = hasVoteGhost(pId); assert(ghost_After == ghost_Before + 1, "Raised by more than 1"); } /* - * Checks that in any call to cast vote functions only the sender's value is updated + * Only sender's voting status can be changed by execution of any cast vote function */ -rule noVoteForSomeoneElse(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector || - f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} { +rule noVoteForSomeoneElse(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector + || f.selector == castVoteWithReason(uint256, uint8, string).selector + || f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector } { env e; calldataarg args; + address voter = e.msg.sender; address user; + bool hasVotedBefore_Voter = hasVoted(e, pId, voter); bool hasVotedBefore_User = hasVoted(e, pId, user); - require(!hasVotedBefore_Voter); - if (f.selector == castVote(uint256, uint8).selector) - { - castVote(e, pId, sup); - } /*else if (f.selector == 0x7b3c71d3) { - require(_pId_Harness() == proposalId); - f(e,args); - }*/ else if (f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) { - uint8 v; bytes32 r; bytes32 s; - castVoteBySig(e, pId, sup, v, r, s); - } else{ - f(e, args); - } + + helperFunctionsWithRevert(pId, f, e); + require(!lastReverted); + bool hasVotedAfter_Voter = hasVoted(e, pId, voter); bool hasVotedAfter_User = hasVoted(e, pId, user); - assert hasVotedBefore_Voter != hasVotedAfter_Voter => (user != voter => hasVotedBefore_User == hasVotedAfter_User); + + assert !hasVotedBefore_Voter && hasVotedAfter_Voter && (user != voter => hasVotedBefore_User == hasVotedAfter_User); } +/* +* Total voting tally is monotonically non-decreasing in every operation +*/ rule votingWeightMonotonicity(method f){ uint256 votingWeightBefore = sum_tracked_weight(); @@ -245,78 +225,54 @@ rule votingWeightMonotonicity(method f){ } -function callFunctionWithParams(method f, uint256 proposalId, env e) { - address[] targets; - uint256[] values; - bytes[] calldatas; - bytes32 descriptionHash; - uint8 support; - uint8 v; bytes32 r; bytes32 s; - if (f.selector == callPropose(address[], uint256[], bytes[]).selector) { - uint256 result = callPropose(e, targets, values, calldatas); - require result == proposalId; - } else if (f.selector == execute(address[], uint256[], bytes[], bytes32).selector) { - uint256 result = execute(e, targets, values, calldatas, descriptionHash); - require result == proposalId; - } else if (f.selector == castVote(uint256, uint8).selector) { - castVote(e, proposalId, support); - //} else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) { - // castVoteWithReason(e, proposalId, support, reason); - } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) { - castVoteBySig(e, proposalId, support, v, r, s); - } else { - calldataarg args; - f(e,args); - } -} - -// run on ALex's branch to avoid tiomeouts: --staging alex/external-timeout-for-solvers -// --staging shelly/forSasha +/* +* A change in hasVoted must be correlated with an non-decreasing of the vote supports (nondecrease because user can vote with weight 0) +*/ rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f -> f.selector != 0x7d5e81e2}{ + address acc = e.msg.sender; + uint256 againstBefore = votesAgainst(); uint256 forBefore = votesFor(); uint256 abstainBefore = votesAbstain(); - //againstBefore, forBefore, abstainBefore = proposalVotes(pId); - - address acc = e.msg.sender; bool hasVotedBefore = hasVoted(e, pId, acc); - uint256 votesBefore = getVotes(e, acc, bn); - require votesBefore > 0; - //calldataarg args; - //f(e, args); - callFunctionWithParams(f, pId, e); + helperFunctionsWithRevert(pId, f, e); uint256 againstAfter = votesAgainst(); uint256 forAfter = votesFor(); uint256 abstainAfter = votesAbstain(); - //againstAfter, forAfter, abstainAfter = proposalVotes(pId); - - uint256 votesAfter = getVotes(e, acc, bn); + bool hasVotedAfter = hasVoted(e, pId, acc); - assert hasVotedBefore != hasVotedAfter => againstBefore <= againstAfter || forBefore <= forAfter || abstainBefore <= abstainAfter, "no correlation"; + assert (!hasVotedBefore && hasVotedAfter) => againstBefore <= againstAfter || forBefore <= forAfter || abstainBefore <= abstainAfter, "no correlation"; } /* -* Check privileged operations +* Only privileged users can execute privileged operations, e.g. change _quorumNumerator or _timelock */ -rule privilegedOnly(method f, uint256 newQuorumNumerator){ +rule privilegedOnlyNumerator(method f, uint256 newQuorumNumerator){ env e; calldataarg arg; uint256 quorumNumBefore = quorumNumerator(e); - //require e.msg.sender == currentContract; f(e, arg); - //updateQuorumNumerator(e, newQuorumNumerator); - uint256 quorumNumAfter = quorumNumerator(e); - address executorCheck = getExecutor(e); - // address executorCheck = _executor(e); assert quorumNumBefore != quorumNumAfter => e.msg.sender == executorCheck, "non priveleged user changed quorum numerator"; -} \ No newline at end of file +} + +rule privilegedOnlyTimelock(method f, uint256 newQuorumNumerator){ + env e; + calldataarg arg; + uint256 timelockBefore = timelock(e); + + f(e, arg); + + uint256 timelockAfter = timelock(e); + + assert timelockBefore != timelockAfter => e.msg.sender == timelockBefore, "non priveleged user changed timelock"; +} From 9344f697f94d0cb81f5d8ef1da091dd16dfc507d Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 23 Nov 2021 16:33:28 +0200 Subject: [PATCH 110/254] removed oneUserVotesInCast --- certora/specs/GovernorCountingSimple.spec | 36 ++--------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index e53313988..1d9b92af1 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -24,20 +24,6 @@ methods { ////////////////////////////////////////////////////////////////////////////// -/* - * ghost to keep track of changes in hasVoted status of users - */ -ghost hasVoteGhost(uint256) returns uint256 { - init_state axiom forall uint256 pId. hasVoteGhost(pId) == 0; -} - -hook Sstore _proposalVotes[KEY uint256 pId].hasVoted[KEY address user] bool current_voting_State (bool old_voting_state) STORAGE{ - havoc hasVoteGhost assuming forall uint256 p. ((p == pId && current_voting_State && !old_voting_state) ? (hasVoteGhost@new(p) == hasVoteGhost@old(p) + 1) : - (hasVoteGhost@new(p) == hasVoteGhost@old(p))); -} - - - //////////// ghosts to keep track of votes counting //////////// /* @@ -138,6 +124,7 @@ invariant OneIsNotMoreThanAll(uint256 pId) ///////////////////////////////// RULES ////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + //NOT FINISHED /* * the sum of voting power of those who voted is less or equal to the maximum possible votes, per each proposal @@ -168,23 +155,6 @@ rule possibleTotalVotes(uint256 pId, uint8 sup, env e, method f) { } -/* - * Checks that only one user is updated in the system when calling cast vote functions (assuming hasVoted is changing correctly, false->true, with every vote cast) - */ -rule oneUserVotesInCast(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector - || f.selector == castVoteWithReason(uint256, uint8, string).selector - || f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} { - env e; calldataarg args; - uint256 ghost_Before = hasVoteGhost(pId); - - helperFunctionsWithRevert(pId, f, e); - require(!lastReverted); - - uint256 ghost_After = hasVoteGhost(pId); - assert(ghost_After == ghost_Before + 1, "Raised by more than 1"); -} - - /* * Only sender's voting status can be changed by execution of any cast vote function */ @@ -196,16 +166,14 @@ rule noVoteForSomeoneElse(uint256 pId, uint8 sup, method f) filtered {f -> f.sel address voter = e.msg.sender; address user; - bool hasVotedBefore_Voter = hasVoted(e, pId, voter); bool hasVotedBefore_User = hasVoted(e, pId, user); helperFunctionsWithRevert(pId, f, e); require(!lastReverted); - bool hasVotedAfter_Voter = hasVoted(e, pId, voter); bool hasVotedAfter_User = hasVoted(e, pId, user); - assert !hasVotedBefore_Voter && hasVotedAfter_Voter && (user != voter => hasVotedBefore_User == hasVotedAfter_User); + assert user != voter => hasVotedBefore_User == hasVotedAfter_User; } From 089472449699d9f4710f1d15d70b60faf3104d8d Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Tue, 23 Nov 2021 16:57:14 +0200 Subject: [PATCH 111/254] all rules checked no structure organization --- certora/specs/GovernorBase.spec | 71 ++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index cf5086061..d6149d02e 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -272,39 +272,6 @@ rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ ///////////////////////////// Not Categorized Yet ////////////////////////////// //////////////////////////////////////////////////////////////////////////////// - -/* - * All non-view functions should revert if proposal is executed - */ -rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selector != updateQuorumNumerator(uint256).selector && - !f.isFallback && f.selector != updateTimelock(address).selector} { - env e; calldataarg args; - uint256 pId; - require(isExecuted(pId)); - requireInvariant noBothExecutedAndCanceled(pId); - requireInvariant executedImplyStartAndEndDateNonZero(pId); - - helperFunctionsWithRevert(pId, f, e); - - assert(lastReverted, "Function was not reverted"); -} - -/* - * All non-view functions should revert if proposal is canceled - */ -rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selector != updateQuorumNumerator(uint256).selector && - !f.isFallback && f.selector != updateTimelock(address).selector} { - env e; calldataarg args; - uint256 pId; - require(isCanceled(pId)); - requireInvariant noBothExecutedAndCanceled(pId); - requireInvariant canceledImplyStartAndEndDateNonZero(pId); - - helperFunctionsWithRevert(pId, f, e); - - assert(lastReverted, "Function was not reverted"); -} - /* * Shows that executed can only change due to execute() */ @@ -320,3 +287,41 @@ rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] c bool executedAfter = isExecuted(pId); assert(executedAfter != executedBefore, "executed property did not change"); } + + + +/* + * All proposal specific (non-view) functions should revert if proposal is executed + */ + // In this rule we show that if a function is executed, i.e. execute() was called on the proposal ID, + // non of the proposal specific functions can make changes again. In executedOnlyAfterExecuteFunc + // we connected the executed attribute to the execute() function, showing that only execute() can + // change it, and that it will always change it. +rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selector != updateQuorumNumerator(uint256).selector && + !f.isFallback && f.selector != updateTimelock(address).selector} { + env e; calldataarg args; + uint256 pId; + require(isExecuted(pId)); + requireInvariant noBothExecutedAndCanceled(pId); + requireInvariant executedImplyStartAndEndDateNonZero(pId); + + helperFunctionsWithRevert(pId, f, e); + + assert(lastReverted, "Function was not reverted"); +} + +/* + * All proposal specific (non-view) functions should revert if proposal is canceled + */ +rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selector != updateQuorumNumerator(uint256).selector && + !f.isFallback && f.selector != updateTimelock(address).selector} { + env e; calldataarg args; + uint256 pId; + require(isCanceled(pId)); + requireInvariant noBothExecutedAndCanceled(pId); + requireInvariant canceledImplyStartAndEndDateNonZero(pId); + + helperFunctionsWithRevert(pId, f, e); + + assert(lastReverted, "Function was not reverted"); +} \ No newline at end of file From 108be781a40a98b35d015070990e6aad236f82dc Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 24 Nov 2021 17:52:25 +0200 Subject: [PATCH 112/254] RemovedUnnecessaryHarnesses --- .../GovernorCountingSimpleHarness.sol | 29 ----- certora/harnesses/GovernorHarness.sol | 120 ------------------ .../GovernorProposalThresholdHarness.sol | 63 --------- .../GovernorTimelockCompoundHarness.sol | 58 --------- certora/harnesses/GovernorVotesHarness.sol | 51 -------- .../GovernorVotesQuorumFractionHarness.sol | 47 ------- 6 files changed, 368 deletions(-) delete mode 100644 certora/harnesses/GovernorCountingSimpleHarness.sol delete mode 100644 certora/harnesses/GovernorHarness.sol delete mode 100644 certora/harnesses/GovernorProposalThresholdHarness.sol delete mode 100644 certora/harnesses/GovernorTimelockCompoundHarness.sol delete mode 100644 certora/harnesses/GovernorVotesHarness.sol delete mode 100644 certora/harnesses/GovernorVotesQuorumFractionHarness.sol diff --git a/certora/harnesses/GovernorCountingSimpleHarness.sol b/certora/harnesses/GovernorCountingSimpleHarness.sol deleted file mode 100644 index 52e980a81..000000000 --- a/certora/harnesses/GovernorCountingSimpleHarness.sol +++ /dev/null @@ -1,29 +0,0 @@ -import "../../contracts/governance/extensions/GovernorCountingSimple.sol"; - -contract GovernorCountingSimpleHarness is GovernorCountingSimple { - - mapping(uint256 => uint256) _quorum; - - function quorum(uint256 blockNumber) public view override virtual returns (uint256) { - return _quorum[blockNumber]; - } - - mapping (address => mapping (uint256 => uint256)) _getVotes; - - function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { - return _getVotes[account][blockNumber]; - } - - uint256 _votingDelay; - function votingDelay() public view override virtual returns (uint256) { - return _votingDelay; - } - - uint256 _votingPeriod; - function votingPeriod() public view override virtual returns (uint256) { - return _votingPeriod; - } - - constructor(string memory name) Governor(name) {} - -} \ No newline at end of file diff --git a/certora/harnesses/GovernorHarness.sol b/certora/harnesses/GovernorHarness.sol deleted file mode 100644 index 56cee4afa..000000000 --- a/certora/harnesses/GovernorHarness.sol +++ /dev/null @@ -1,120 +0,0 @@ -import "../../contracts/governance/Governor.sol"; - -contract GovernorHarness is Governor { - - function isExecuted(uint256 proposalId) public view returns (bool) { - return _proposals[proposalId].executed; - } - - function isCanceled(uint256 proposalId) public view returns (bool) { - return _proposals[proposalId].canceled; - } - - function snapshot(uint256 proposalId) public view returns (uint64) { - return _proposals[proposalId].voteStart._deadline; - } - - - function initialized(uint256 proposalId) public view returns (bool){ - if (_proposals[proposalId].voteStart._deadline != 0 && _proposals[proposalId].voteEnd._deadline != 0) { - return true; - } - return false; - } - - - mapping(uint256 => uint256) _quorum; - - function quorum(uint256 blockNumber) public view override virtual returns (uint256) { - return _quorum[blockNumber]; - } - - - mapping (address => mapping (uint256 => uint256)) _getVotes; - - function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { - return _getVotes[account][blockNumber]; - } - - - mapping (uint256 => bool) __quoromReached; - - function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { - return __quoromReached[proposalId]; - } - - - mapping (uint256 => bool) __voteSucceeded; - - function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { - return __voteSucceeded[proposalId]; - } - - - //string _COUNTING_MODE; - function COUNTING_MODE() public pure override virtual returns (string memory) { - return "dummy"; - } - - - mapping(uint256 => mapping(address => bool)) _hasVoted; - - function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { - return _hasVoted[proposalId][account]; - } - - - uint256 _votingDelay; - - function votingDelay() public view override virtual returns (uint256) { - return _votingDelay; - } - - - uint256 _votingPeriod; - - function votingPeriod() public view override virtual returns (uint256) { - return _votingPeriod; - } - - - function _countVote( - uint256 proposalId, - address account, - uint8 support, - uint256 weight - ) internal override virtual { - // havoc something - } - - - constructor(string memory name) Governor(name) {} - - // _countVots == Sum of castVote - // - // RHS: - // 1) use counter_vote_power as a counter - // 2) use counter_vote_power as a temp var for a ghost - // - // LHS: - // mapping of count - // countMap - - // uint decision; - // uint numberOfOptions; - - function callPropose(address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas) public virtual returns (uint256) { - return super.propose(targets, values, calldatas, ""); - } - - - // Harness of castVoteWithReason to be able to impose requirement on the proposal ID. - uint256 public _pId_Harness; - function castVoteWithReason(uint256 proposalId, uint8 support, string calldata reason) - public override returns (uint256) { - require(proposalId == _pId_Harness); - return super.castVoteWithReason(proposalId, support, reason); - } -} \ No newline at end of file diff --git a/certora/harnesses/GovernorProposalThresholdHarness.sol b/certora/harnesses/GovernorProposalThresholdHarness.sol deleted file mode 100644 index 1d0559a60..000000000 --- a/certora/harnesses/GovernorProposalThresholdHarness.sol +++ /dev/null @@ -1,63 +0,0 @@ -import "../../contracts/governance/extensions/GovernorProposalThreshold.sol"; - -contract GovernorProposalThresholdHarness is GovernorProposalThreshold { - - mapping(uint256 => uint256) _quorum; - - function quorum(uint256 blockNumber) public view override virtual returns (uint256) { - return _quorum[blockNumber]; - } - - mapping (address => mapping (uint256 => uint256)) _getVotes; - - function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { - return _getVotes[account][blockNumber]; - } - - mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { - return __quoromReached[proposalId]; - } - - mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { - return __voteSucceeded[proposalId]; - } - - //string _COUNTING_MODE; - function COUNTING_MODE() public pure override virtual returns (string memory) { - return "dummy"; - } - - mapping(uint256 => mapping(address => bool)) _hasVoted; - function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { - return _hasVoted[proposalId][account]; - } - - uint256 _votingDelay; - function votingDelay() public view override virtual returns (uint256) { - return _votingDelay; - } - - uint256 _votingPeriod; - function votingPeriod() public view override virtual returns (uint256) { - return _votingPeriod; - } - - function _countVote( - uint256 proposalId, - address account, - uint8 support, - uint256 weight - ) internal override virtual { - // havoc something - } - - uint256 _proposalThreshold; - function proposalThreshold() public view override virtual returns (uint256) { - return _proposalThreshold; - } - - constructor(string memory name) Governor(name) {} - -} \ No newline at end of file diff --git a/certora/harnesses/GovernorTimelockCompoundHarness.sol b/certora/harnesses/GovernorTimelockCompoundHarness.sol deleted file mode 100644 index f8a85e53f..000000000 --- a/certora/harnesses/GovernorTimelockCompoundHarness.sol +++ /dev/null @@ -1,58 +0,0 @@ -import "../../contracts/governance/extensions/GovernorTimelockCompound.sol"; - -contract GovernorTimelockCompoundHarness is GovernorTimelockCompound { - - mapping(uint256 => uint256) _quorum; - - function quorum(uint256 blockNumber) public view override virtual returns (uint256) { - return _quorum[blockNumber]; - } - - mapping (address => mapping (uint256 => uint256)) _getVotes; - - function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) { - return _getVotes[account][blockNumber]; - } - - mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { - return __quoromReached[proposalId]; - } - - mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { - return __voteSucceeded[proposalId]; - } - - //string _COUNTING_MODE; - function COUNTING_MODE() public pure override virtual returns (string memory) { - return "dummy"; - } - - mapping(uint256 => mapping(address => bool)) _hasVoted; - function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { - return _hasVoted[proposalId][account]; - } - - uint256 _votingDelay; - function votingDelay() public view override virtual returns (uint256) { - return _votingDelay; - } - - uint256 _votingPeriod; - function votingPeriod() public view override virtual returns (uint256) { - return _votingPeriod; - } - - function _countVote( - uint256 proposalId, - address account, - uint8 support, - uint256 weight - ) internal override virtual { - // havoc something - } - - constructor(string memory name, ICompoundTimelock timelock) Governor(name) GovernorTimelockCompound(timelock) {} - -} \ No newline at end of file diff --git a/certora/harnesses/GovernorVotesHarness.sol b/certora/harnesses/GovernorVotesHarness.sol deleted file mode 100644 index 3e06c8daa..000000000 --- a/certora/harnesses/GovernorVotesHarness.sol +++ /dev/null @@ -1,51 +0,0 @@ -import "../../contracts/governance/extensions/GovernorVotes.sol"; - -contract GovernorVotesHarness is GovernorVotes { - - mapping(uint256 => uint256) _quorum; - - function quorum(uint256 blockNumber) public view override virtual returns (uint256) { - return _quorum[blockNumber]; - } - - mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { - return __quoromReached[proposalId]; - } - - mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { - return __voteSucceeded[proposalId]; - } - - //string _COUNTING_MODE; - function COUNTING_MODE() public pure override virtual returns (string memory) { - return "dummy"; - } - - mapping(uint256 => mapping(address => bool)) _hasVoted; - function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { - return _hasVoted[proposalId][account]; - } - - uint256 _votingDelay; - function votingDelay() public view override virtual returns (uint256) { - return _votingDelay; - } - - uint256 _votingPeriod; - function votingPeriod() public view override virtual returns (uint256) { - return _votingPeriod; - } - - function _countVote( - uint256 proposalId, - address account, - uint8 support, - uint256 weight - ) internal override virtual { - // havoc something - } - - constructor(ERC20Votes tokenAddr, string memory name) GovernorVotes(tokenAddr) Governor(name) {} -} \ No newline at end of file diff --git a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol b/certora/harnesses/GovernorVotesQuorumFractionHarness.sol deleted file mode 100644 index 86ae202ad..000000000 --- a/certora/harnesses/GovernorVotesQuorumFractionHarness.sol +++ /dev/null @@ -1,47 +0,0 @@ -import "../../contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; - -contract GovernorVotesQuorumFractionHarness is GovernorVotesQuorumFraction { - - mapping (uint256 => bool) __quoromReached; - function _quorumReached(uint256 proposalId) public view override virtual returns (bool) { - return __quoromReached[proposalId]; - } - - mapping (uint256 => bool) __voteSucceeded; - function _voteSucceeded(uint256 proposalId) public view override virtual returns (bool) { - return __voteSucceeded[proposalId]; - } - - //string _COUNTING_MODE; - function COUNTING_MODE() public pure override virtual returns (string memory) { - return "dummy"; - } - - mapping(uint256 => mapping(address => bool)) _hasVoted; - function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) { - return _hasVoted[proposalId][account]; - } - - uint256 _votingDelay; - function votingDelay() public view override virtual returns (uint256) { - return _votingDelay; - } - - uint256 _votingPeriod; - function votingPeriod() public view override virtual returns (uint256) { - return _votingPeriod; - } - - function _countVote( - uint256 proposalId, - address account, - uint8 support, - uint256 weight - ) internal override virtual { - // havoc something - } - - constructor(ERC20Votes tokenAddr, string memory name, uint256 quorumNumeratorValue) - GovernorVotesQuorumFraction(quorumNumeratorValue) GovernorVotes(tokenAddr) Governor(name) {} - -} \ No newline at end of file From b3dd1e03868625024c9b0cfa49762a87c3331e04 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 24 Nov 2021 17:53:01 +0200 Subject: [PATCH 113/254] RulesCleaning --- certora/specs/GovernorBase.spec | 54 +++++++++++------------ certora/specs/GovernorCountingSimple.spec | 3 +- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index d6149d02e..e56960347 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -112,8 +112,7 @@ invariant startAndEndDatesNonZero(uint256 pId) // use Uri's branch - --staging uri/add_with_env_to_preserved_all invariant canceledImplyStartAndEndDateNonZero(uint pId) isCanceled(pId) => proposalSnapshot(pId) != 0 - /*{ preserved with (env e){ - requireInvariant startAndEndDatesNonZero(pId); //@note maybe unndeeded + /*{preserved with (env e){ require e.block.number > 0; }}*/ @@ -126,22 +125,22 @@ invariant canceledImplyStartAndEndDateNonZero(uint pId) invariant executedImplyStartAndEndDateNonZero(uint pId) isExecuted(pId) => proposalSnapshot(pId) != 0 /*{ preserved with (env e){ - requireInvariant startAndEndDatesNonZero(pId); //@note maybe unndeeded require e.block.number > 0; }}*/ /* - * A proposal starting block number must be <= to the proposal end date + * A proposal starting block number must be less or equal than the proposal end date */ invariant voteStartBeforeVoteEnd(uint256 pId) // from < to <= because snapshot and deadline can be the same block number if delays are set to 0 // This is possible before the integration of GovernorSettings.sol to the system. // After integration of GovernorSettings.sol the invariant expression should be changed from <= to < (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) - { preserved { + // (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) + /*{ preserved { requireInvariant startAndEndDatesNonZero(pId); - }} + }}*/ /* @@ -231,8 +230,8 @@ rule noStartBeforeCreation(uint256 pId) { // We proved in immutableFieldsAfterProposalCreation that once dates set for proposal, it cannot be changed require !proposalCreated(pId); // previousStart == 0; - env e; calldataarg arg; - propose(e, arg); + env e; calldataarg args; + propose(e, args); uint256 newStart = proposalSnapshot(pId); // if created, start is after current block number (creation block) @@ -252,8 +251,8 @@ rule noStartBeforeCreation(uint256 pId) { rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ require !isExecuted(pId) && !isCanceled(pId); - env e; calldataarg arg; - f(e, arg); + env e; calldataarg args; + f(e, args); assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before deadline"; } @@ -272,23 +271,6 @@ rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ ///////////////////////////// Not Categorized Yet ////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/* - * Shows that executed can only change due to execute() - */ -rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) { - env e; calldataarg args; - uint256 pId; - bool executedBefore = isExecuted(pId); - require(!executedBefore); - - helperFunctionsWithRevert(pId, f, e); - require(!lastReverted); - - bool executedAfter = isExecuted(pId); - assert(executedAfter != executedBefore, "executed property did not change"); -} - - /* * All proposal specific (non-view) functions should revert if proposal is executed @@ -324,4 +306,20 @@ rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selec helperFunctionsWithRevert(pId, f, e); assert(lastReverted, "Function was not reverted"); -} \ No newline at end of file +} + +/* + * Proposal can be switched to executed only via execute() function + */ +rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) { + env e; calldataarg args; + uint256 pId; + bool executedBefore = isExecuted(pId); + require(!executedBefore); + + helperFunctionsWithRevert(pId, f, e); + require(!lastReverted); + + bool executedAfter = isExecuted(pId); + assert(executedAfter != executedBefore, "executed property did not change"); +} diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 1d9b92af1..8f12516f4 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -196,7 +196,7 @@ rule votingWeightMonotonicity(method f){ /* * A change in hasVoted must be correlated with an non-decreasing of the vote supports (nondecrease because user can vote with weight 0) */ -rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f -> f.selector != 0x7d5e81e2}{ +rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) { address acc = e.msg.sender; uint256 againstBefore = votesAgainst(); @@ -206,6 +206,7 @@ rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f - bool hasVotedBefore = hasVoted(e, pId, acc); helperFunctionsWithRevert(pId, f, e); + require(!lastReverted); uint256 againstAfter = votesAgainst(); uint256 forAfter = votesFor(); From 37725a0f2c4fdcaea229ff5aecddee7e6a96f515 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 24 Nov 2021 19:48:39 +0200 Subject: [PATCH 114/254] CleaningAndScriptForAllAndReadme --- certora/README.md | 49 +++++++++++++++++++ ...rdHarness1.sol => WizardFirstPriority.sol} | 2 +- ...norBasicHarness.sol => WizardFirstTry.sol} | 2 +- certora/scripts/AvengersAssemble.sh | 28 +++++++++++ ...zardHarness1.sh => WizardFirstPriority.sh} | 6 +-- .../{GovernorBasic.sh => WizardFirstTry.sh} | 4 +- certora/scripts/sanity.sh | 4 +- certora/specs/Privileged.spec | 31 ------------ 8 files changed, 86 insertions(+), 40 deletions(-) create mode 100644 certora/README.md rename certora/harnesses/{WizardHarness1.sol => WizardFirstPriority.sol} (96%) rename certora/harnesses/{GovernorBasicHarness.sol => WizardFirstTry.sol} (96%) create mode 100644 certora/scripts/AvengersAssemble.sh rename certora/scripts/{WizardHarness1.sh => WizardFirstPriority.sh} (57%) rename certora/scripts/{GovernorBasic.sh => WizardFirstTry.sh} (69%) delete mode 100644 certora/specs/Privileged.spec diff --git a/certora/README.md b/certora/README.md new file mode 100644 index 000000000..b6cdc810e --- /dev/null +++ b/certora/README.md @@ -0,0 +1,49 @@ +# Running the certora verification tool + +These instructions detail the process for running CVT on the OpenZeppelin (Wizard/Governor) contracts. + +Documentation for CVT and the specification language are available +[here](https://certora.atlassian.net/wiki/spaces/CPD/overview) + +## Running the verification + +Due to current time and space limitations in the tool, many of the rules need to +be verified separately, so there are many steps required to reverify everything. + +The scripts in the `certora/scripts` directory are used to submit verification +jobs to the Certora verification service. These scripts should be run from the +root directory; for example by running + +``` +sh certora/scripts/WizardFirstPriority.sh +``` + +After the job is complete, the results will be available on +[the Certora portal](https://vaas-stg.certora.com/). + +The `AvengersAssemble` script runs all spec files agains all contracts in the `certora/harness` that start with `Wizard` meaning that a contract generated in [wizard](`certora/scripts`). If you want to verify new wizard's instance you also need to harness this contract. We don't recommend to do it because a list of harnesses may go beyond wizard's contract. The main goal of this script is to run all specs written by the team against all contracts properly harnessed. + +The `WizardFirstPriority` and `WizardFirstTry` scripts run one of the scripts for the corresponding contract. In order to run another spec you should change spec file name `` in the third line of a script: + +``` +--verify WizardFirstPriority:certora/specs/.spec \ + +for example: + +--verify WizardFirstPriority:certora/specs/GovernorCountingSimple.spec \ +``` + + +MENTION TIMEOUTS ISSUES + + +## Adapting to changes + +Some of our rules require the code to be simplified in various ways. Our +primary tool for performing these simplifications is to run verification on a +contract that extends the original contracts and overrides some of the methods. +These "harness" contracts can be found in the `certora/harness` directory. + +This pattern does require some modifications to the original code: some methods +need to be made virtual or public, for example. These changes are handled by +applying a patch to the code before verification. diff --git a/certora/harnesses/WizardHarness1.sol b/certora/harnesses/WizardFirstPriority.sol similarity index 96% rename from certora/harnesses/WizardHarness1.sol rename to certora/harnesses/WizardFirstPriority.sol index 346e18360..d35cbb227 100644 --- a/certora/harnesses/WizardHarness1.sol +++ b/certora/harnesses/WizardFirstPriority.sol @@ -15,7 +15,7 @@ ERC20Votes TimelockCOntroller */ -contract WizardHarness1 is Governor, GovernorProposalThreshold, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { +contract WizardFirstPriority is Governor, GovernorProposalThreshold, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { constructor(ERC20Votes _token, TimelockController _timelock, string memory name, uint256 quorumFraction) Governor(name) GovernorVotes(_token) diff --git a/certora/harnesses/GovernorBasicHarness.sol b/certora/harnesses/WizardFirstTry.sol similarity index 96% rename from certora/harnesses/GovernorBasicHarness.sol rename to certora/harnesses/WizardFirstTry.sol index f63aaa161..f4d95c841 100644 --- a/certora/harnesses/GovernorBasicHarness.sol +++ b/certora/harnesses/WizardFirstTry.sol @@ -13,7 +13,7 @@ ERC20Votes TimelockCompound */ -contract GovernorBasicHarness is Governor, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockCompound { +contract WizardFirstTry is Governor, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockCompound { constructor(ERC20Votes _token, ICompoundTimelock _timelock, string memory name, uint256 quorumFraction) Governor(name) GovernorVotes(_token) diff --git a/certora/scripts/AvengersAssemble.sh b/certora/scripts/AvengersAssemble.sh new file mode 100644 index 000000000..1e59bde43 --- /dev/null +++ b/certora/scripts/AvengersAssemble.sh @@ -0,0 +1,28 @@ +for contract in certora/harnesses/Wizard*.sol; +do + for spec in certora/specs/*.spec; + do + contractFile=$(basename $contract) + specFile=$(basename $spec) + echo "Processing ${contractFile%.*} with $specFile" + if [ "${contractFile%.*}" = "WizardFirstPriority" ]; + then + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ + --link WizardFirstPriority:token=ERC20VotesHarness \ + --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --msg "checking $spec on ${contractFile%.*}" + else + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ + --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --msg "checking $spec on ${contractFile%.*}" + fi + done +done diff --git a/certora/scripts/WizardHarness1.sh b/certora/scripts/WizardFirstPriority.sh similarity index 57% rename from certora/scripts/WizardHarness1.sh rename to certora/scripts/WizardFirstPriority.sh index 64fd52d33..980f617ea 100644 --- a/certora/scripts/WizardHarness1.sh +++ b/certora/scripts/WizardFirstPriority.sh @@ -1,6 +1,6 @@ -certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardHarness1.sol \ - --link WizardHarness1:token=ERC20VotesHarness \ - --verify WizardHarness1:certora/specs/GovernorCountingSimple.spec \ +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardFirstPriority.sol \ + --link WizardFirstPriority:token=ERC20VotesHarness \ + --verify WizardFirstPriority:certora/specs/GovernorCountingSimple.spec \ --solc solc8.2 \ --staging shelly/forSasha \ --optimistic_loop \ diff --git a/certora/scripts/GovernorBasic.sh b/certora/scripts/WizardFirstTry.sh similarity index 69% rename from certora/scripts/GovernorBasic.sh rename to certora/scripts/WizardFirstTry.sh index 6f9c84059..8aa46e8cb 100644 --- a/certora/scripts/GovernorBasic.sh +++ b/certora/scripts/WizardFirstTry.sh @@ -1,5 +1,5 @@ -certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorBasicHarness.sol \ - --verify GovernorBasicHarness:certora/specs/GovernorBase.spec \ +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardFirstTry.sol \ + --verify WizardFirstTry:certora/specs/GovernorBase.spec \ --solc solc8.2 \ --staging shelly/forSasha \ --optimistic_loop \ diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index 7ffaf52f7..58b27cc7d 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -1,11 +1,11 @@ -for f in certora/harnesses/*.sol +for f in certora/harnesses/Wizard*.sol do echo "Processing $f" file=$(basename $f) echo ${file%.*} certoraRun certora/harnesses/$file \ --verify ${file%.*}:certora/specs/sanity.spec "$@" \ - --solc solc8.0 --staging \ + --solc solc8.2 --staging shelly/forSasha \ --optimistic_loop \ --msg "checking sanity on ${file%.*}" --settings -copyLoopUnroll=4 diff --git a/certora/specs/Privileged.spec b/certora/specs/Privileged.spec deleted file mode 100644 index f9615a619..000000000 --- a/certora/specs/Privileged.spec +++ /dev/null @@ -1,31 +0,0 @@ -definition knownAsNonPrivileged(method f) returns bool = false -/* ( f.selector == isWhitelistedOtoken(address).selector || - f.selector == isWhitelistedProduct(address,address,address,bool).selector || - f.selector == owner().selector || - f.selector == isWhitelistedCallee(address).selector || - f.selector == whitelistOtoken(address).selector || - f.selector == addressBook().selector || - f.selector == isWhitelistedCollateral(address).selector )*/; - - - -rule privilegedOperation(method f, address privileged) -description "$f can be called by more than one user without reverting" -{ - env e1; - calldataarg arg; - require !knownAsNonPrivileged(f); - require e1.msg.sender == privileged; - - storage initialStorage = lastStorage; - invoke f(e1, arg); // privileged succeeds executing candidate privileged operation. - bool firstSucceeded = !lastReverted; - - env e2; - calldataarg arg2; - require e2.msg.sender != privileged; - invoke f(e2, arg2) at initialStorage; // unprivileged - bool secondSucceeded = !lastReverted; - - assert !(firstSucceeded && secondSucceeded), "${f.selector} can be called by both ${e1.msg.sender} and ${e2.msg.sender}, so it is not privileged"; -} From 73080c79d030eab88d377021b183ca1efd5f72f7 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Thu, 25 Nov 2021 13:30:09 +0200 Subject: [PATCH 115/254] cleaning in process --- certora/harnesses/WizardFirstPriority.sol | 3 +- certora/harnesses/WizardFirstTry.sol | 37 ++++++++----------- certora/scripts/AvengersAssemble.sh | 28 -------------- certora/scripts/GovernorCountingSimple.sh | 8 ---- certora/scripts/GovernorProposalThreshold.sh | 2 - certora/scripts/GovernorTimelockCompound.sh | 2 - certora/scripts/GovernorVotes.sh | 2 - .../GovernorVotesQuorumFractionHarness.sh | 2 - certora/scripts/check.sh | 9 ----- 9 files changed, 16 insertions(+), 77 deletions(-) delete mode 100644 certora/scripts/AvengersAssemble.sh delete mode 100755 certora/scripts/GovernorCountingSimple.sh delete mode 100755 certora/scripts/GovernorProposalThreshold.sh delete mode 100755 certora/scripts/GovernorTimelockCompound.sh delete mode 100755 certora/scripts/GovernorVotes.sh delete mode 100755 certora/scripts/GovernorVotesQuorumFractionHarness.sh delete mode 100755 certora/scripts/check.sh diff --git a/certora/harnesses/WizardFirstPriority.sol b/certora/harnesses/WizardFirstPriority.sol index d35cbb227..7cf7f5223 100644 --- a/certora/harnesses/WizardFirstPriority.sol +++ b/certora/harnesses/WizardFirstPriority.sol @@ -54,7 +54,6 @@ contract WizardFirstPriority is Governor, GovernorProposalThreshold, GovernorCou return deltaWeight; } - function snapshot(uint256 proposalId) public view returns (uint64) { return _proposals[proposalId].voteStart._deadline; } @@ -64,7 +63,7 @@ contract WizardFirstPriority is Governor, GovernorProposalThreshold, GovernorCou return _executor(); } - // original code + // original code, harnessed function votingDelay() public view override returns (uint256) { // HARNESS: pure -> view return _votingDelay; // HARNESS: parametric diff --git a/certora/harnesses/WizardFirstTry.sol b/certora/harnesses/WizardFirstTry.sol index f4d95c841..cdcd3960e 100644 --- a/certora/harnesses/WizardFirstTry.sol +++ b/certora/harnesses/WizardFirstTry.sol @@ -21,7 +21,7 @@ contract WizardFirstTry is Governor, GovernorCountingSimple, GovernorVotes, Gove GovernorTimelockCompound(_timelock) {} - + //HARNESS function isExecuted(uint256 proposalId) public view returns (bool) { return _proposals[proposalId].executed; @@ -31,18 +31,18 @@ contract WizardFirstTry is Governor, GovernorCountingSimple, GovernorVotes, Gove return _proposals[proposalId].canceled; } + function snapshot(uint256 proposalId) public view returns (uint64) { + return _proposals[proposalId].voteStart._deadline; + } + + function getExecutor() public view returns (address){ + return _executor(); + } + uint256 _votingDelay; - function votingDelay() public view override virtual returns (uint256) { // HARNESS: pure -> view - return _votingDelay; - } - uint256 _votingPeriod; - function votingPeriod() public view override virtual returns (uint256) { // HARNESS: pure -> view - return _votingPeriod; - } - mapping(uint256 => uint256) public ghost_sum_vote_power_by_id; function _castVote( @@ -58,21 +58,14 @@ contract WizardFirstTry is Governor, GovernorCountingSimple, GovernorVotes, Gove return deltaWeight; } - function callPropose(address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas) public virtual returns (uint256) { - return super.propose(targets, values, calldatas, ""); + // original code, harnessed + + function votingDelay() public view override virtual returns (uint256) { // HARNESS: pure -> view + return _votingDelay; // HARNESS: parametric } - // Harness of castVoteWithReason to be able to impose requirement on the proposal ID. - uint256 public _pId_Harness; - function castVoteWithReason(uint256 proposalId, uint8 support, string calldata reason) - public - override(IGovernor, Governor) - returns (uint256) - { - require(proposalId == _pId_Harness); - return super.castVoteWithReason(proposalId, support, reason); + function votingPeriod() public view override virtual returns (uint256) { // HARNESS: pure -> view + return _votingPeriod; // HARNESS: parametric } // The following functions are overrides required by Solidity. diff --git a/certora/scripts/AvengersAssemble.sh b/certora/scripts/AvengersAssemble.sh deleted file mode 100644 index 1e59bde43..000000000 --- a/certora/scripts/AvengersAssemble.sh +++ /dev/null @@ -1,28 +0,0 @@ -for contract in certora/harnesses/Wizard*.sol; -do - for spec in certora/specs/*.spec; - do - contractFile=$(basename $contract) - specFile=$(basename $spec) - echo "Processing ${contractFile%.*} with $specFile" - if [ "${contractFile%.*}" = "WizardFirstPriority" ]; - then - certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ - --link WizardFirstPriority:token=ERC20VotesHarness \ - --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ - --solc solc8.2 \ - --staging shelly/forSasha \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --msg "checking $spec on ${contractFile%.*}" - else - certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ - --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ - --solc solc8.2 \ - --staging shelly/forSasha \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --msg "checking $spec on ${contractFile%.*}" - fi - done -done diff --git a/certora/scripts/GovernorCountingSimple.sh b/certora/scripts/GovernorCountingSimple.sh deleted file mode 100755 index da013a4ef..000000000 --- a/certora/scripts/GovernorCountingSimple.sh +++ /dev/null @@ -1,8 +0,0 @@ -certoraRun certora/harnesses/GovernorCountingSimpleHarness.sol \ - --verify GovernorCountingSimpleHarness:certora/specs/GovernorBase.spec \ - --solc solc8.0 \ - --staging \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --rule doubleVoting \ - --msg "$1" diff --git a/certora/scripts/GovernorProposalThreshold.sh b/certora/scripts/GovernorProposalThreshold.sh deleted file mode 100755 index cb10572fc..000000000 --- a/certora/scripts/GovernorProposalThreshold.sh +++ /dev/null @@ -1,2 +0,0 @@ -certoraRun certora/harnesses/GovernorProposalThresholdHarness.sol \ - --verify GovernorProposalThresholdHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorTimelockCompound.sh b/certora/scripts/GovernorTimelockCompound.sh deleted file mode 100755 index 9cbdd1ea0..000000000 --- a/certora/scripts/GovernorTimelockCompound.sh +++ /dev/null @@ -1,2 +0,0 @@ -certoraRun certora/harnesses/GovernorTimelockCompoundHarness.sol \ - --verify GovernorTimelockCompoundHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorVotes.sh b/certora/scripts/GovernorVotes.sh deleted file mode 100755 index 1dc476445..000000000 --- a/certora/scripts/GovernorVotes.sh +++ /dev/null @@ -1,2 +0,0 @@ -certoraRun certora/harnesses/GovernorVotesHarness.sol \ - --verify GovernorVotesHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/GovernorVotesQuorumFractionHarness.sh b/certora/scripts/GovernorVotesQuorumFractionHarness.sh deleted file mode 100755 index 239339ade..000000000 --- a/certora/scripts/GovernorVotesQuorumFractionHarness.sh +++ /dev/null @@ -1,2 +0,0 @@ -certoraRun certora/harnesses/GovernorVotesQuorumFractionHarness.sol \ - --verify GovernorVotesQuorumFractionHarness:certora/specs/Privileged.spec \ No newline at end of file diff --git a/certora/scripts/check.sh b/certora/scripts/check.sh deleted file mode 100755 index e3291eb1c..000000000 --- a/certora/scripts/check.sh +++ /dev/null @@ -1,9 +0,0 @@ -echo "Usage: Contract Spec" -echo "e.g. GovernorVotes Privileged" -Contract=$1 -Spec=$2 -shift 2 -certoraRun certora/harnesses/${Contract}Harness.sol \ - --verify ${Contract}Harness:certora/specs/${Spec}.spec "$@" \ - --solc solc8.0 --staging --rule noBothExecutedAndCanceled - \ No newline at end of file From 1d25a222018a45181ebf93b782c7affc3c3055ad Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Thu, 25 Nov 2021 13:33:36 +0200 Subject: [PATCH 116/254] runAllwithoutTypeCheckAndPolishingIt --- certora/README.md | 11 ++++----- certora/scripts/WizardFirstPriority.sh | 2 +- certora/scripts/verifyAll.sh | 30 +++++++++++++++++++++++ certora/specs/GovernorBase.spec | 22 +++++++---------- certora/specs/GovernorCountingSimple.spec | 12 ++++++--- 5 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 certora/scripts/verifyAll.sh diff --git a/certora/README.md b/certora/README.md index b6cdc810e..1ad6507f2 100644 --- a/certora/README.md +++ b/certora/README.md @@ -7,29 +7,28 @@ Documentation for CVT and the specification language are available ## Running the verification -Due to current time and space limitations in the tool, many of the rules need to -be verified separately, so there are many steps required to reverify everything. - The scripts in the `certora/scripts` directory are used to submit verification jobs to the Certora verification service. These scripts should be run from the root directory; for example by running ``` -sh certora/scripts/WizardFirstPriority.sh +sh certora/scripts/WizardFirstPriority.sh ``` After the job is complete, the results will be available on [the Certora portal](https://vaas-stg.certora.com/). -The `AvengersAssemble` script runs all spec files agains all contracts in the `certora/harness` that start with `Wizard` meaning that a contract generated in [wizard](`certora/scripts`). If you want to verify new wizard's instance you also need to harness this contract. We don't recommend to do it because a list of harnesses may go beyond wizard's contract. The main goal of this script is to run all specs written by the team against all contracts properly harnessed. +The `verifyAll` script runs all spec files agains all contracts in the `certora/harness` that start with `Wizard` meaning that a contract generated in [wizard](https://wizard.openzeppelin.com/#governor). If you want to verify new wizard's instance you also need to harness this contract. We don't recommend to do it because a list of harnesses may go beyond wizard's contract. Moreover, the set of setting to run the Certora verification service may vary. The main goal of this script is to run all specs written by the team against all contracts properly harnessed. -The `WizardFirstPriority` and `WizardFirstTry` scripts run one of the scripts for the corresponding contract. In order to run another spec you should change spec file name `` in the third line of a script: +The `WizardFirstPriority` and `WizardFirstTry` scripts run one of the scripts for the corresponding contract. In order to run another spec you should change spec file name `` in the script (flag `--verify`): ``` --verify WizardFirstPriority:certora/specs/.spec \ +``` for example: +``` --verify WizardFirstPriority:certora/specs/GovernorCountingSimple.spec \ ``` diff --git a/certora/scripts/WizardFirstPriority.sh b/certora/scripts/WizardFirstPriority.sh index 980f617ea..1a491bbda 100644 --- a/certora/scripts/WizardFirstPriority.sh +++ b/certora/scripts/WizardFirstPriority.sh @@ -5,5 +5,5 @@ certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardFirst --staging shelly/forSasha \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule hasVotedCorrelation \ + --rule noVoteForSomeoneElse \ --msg "$1" \ No newline at end of file diff --git a/certora/scripts/verifyAll.sh b/certora/scripts/verifyAll.sh new file mode 100644 index 000000000..d2ce48350 --- /dev/null +++ b/certora/scripts/verifyAll.sh @@ -0,0 +1,30 @@ +for contract in certora/harnesses/Wizard*.sol; +do + for spec in certora/specs/*.spec; + do + contractFile=$(basename $contract) + specFile=$(basename $spec) + echo "Processing ${contractFile%.*} with $specFile" + if [ "${contractFile%.*}" = "WizardFirstPriority" ]; + then + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ + --link WizardFirstPriority:token=ERC20VotesHarness \ + --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --disableLocalTypeChecking \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --msg "checking $specFile on ${contractFile%.*}" + else + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ + --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --disableLocalTypeChecking \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --msg "checking $specFile on ${contractFile%.*}" + fi + done +done \ No newline at end of file diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index e56960347..ef2d68baf 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -61,7 +61,6 @@ function helperFunctionsWithRevert(uint256 proposalId, method f, env e) { } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) { castVoteBySig@withrevert(e, proposalId, support, v, r, s); } else if (f.selector == queue(address[], uint256[], bytes[], bytes32).selector) { - require targets.length <= 1 && values.length <= 1 && calldatas.length <= 1; queue@withrevert(e, targets, values, calldatas, descriptionHash); } else { calldataarg args; @@ -96,8 +95,7 @@ function helperFunctionsWithRevert(uint256 proposalId, method f, env e) { * This is very safe assumption as usually the 0 block is genesis block which is uploaded with data * by the developers and will not be valid to raise proposals (at the current way that block chain is functioning) */ - // To use env with general preserved block first disable type checking then - // use Uri's branch - --staging uri/add_with_env_to_preserved_all + // To use env with general preserved block disable type checking [--disableLocalTypeChecking] invariant startAndEndDatesNonZero(uint256 pId) proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0 /*{ preserved with (env e){ @@ -108,25 +106,23 @@ invariant startAndEndDatesNonZero(uint256 pId) /* * If a proposal is canceled it must have a start and an end date */ - // To use env with general preserved block first disable type checking then - // use Uri's branch - --staging uri/add_with_env_to_preserved_all + // To use env with general preserved block disable type checking [--disableLocalTypeChecking] invariant canceledImplyStartAndEndDateNonZero(uint pId) isCanceled(pId) => proposalSnapshot(pId) != 0 - /*{preserved with (env e){ + {preserved with (env e){ require e.block.number > 0; - }}*/ + }} /* * If a proposal is executed it must have a start and an end date */ - // To use env with general preserved block first disable type checking then - // use Uri's branch - --staging uri/add_with_env_to_preserved_all + // To use env with general preserved block disable type checking [--disableLocalTypeChecking] invariant executedImplyStartAndEndDateNonZero(uint pId) isExecuted(pId) => proposalSnapshot(pId) != 0 - /*{ preserved with (env e){ + { preserved with (env e){ require e.block.number > 0; - }}*/ + }} /* @@ -138,9 +134,9 @@ invariant voteStartBeforeVoteEnd(uint256 pId) // After integration of GovernorSettings.sol the invariant expression should be changed from <= to < (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) // (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) - /*{ preserved { + { preserved { requireInvariant startAndEndDatesNonZero(pId); - }}*/ + }} /* diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 8f12516f4..47f67aa90 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -158,9 +158,13 @@ rule possibleTotalVotes(uint256 pId, uint8 sup, env e, method f) { /* * Only sender's voting status can be changed by execution of any cast vote function */ -rule noVoteForSomeoneElse(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector - || f.selector == castVoteWithReason(uint256, uint8, string).selector - || f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector } { +// Checked for castVote only. all 3 castVote functions call _castVote, so the completness of the verification is counted on + // the fact that the 3 functions themselves makes no chages, but rather call an internal function to execute. + // That means that we do not check those 3 functions directly, however for castVote & castVoteWithReason it is quite trivial + // to understand why this is ok. For castVoteBySig we basically assume that the signature referendum is correct without checking it. + // We could check each function seperately and pass the rule, but that would have uglyfied the code with no concrete + // benefit, as it is evident that nothing is happening in the first 2 functions (calling a view function), and we do not desire to check the signature verification. +rule noVoteForSomeoneElse(uint256 pId, uint8 sup, method f) { env e; calldataarg args; address voter = e.msg.sender; @@ -168,7 +172,7 @@ rule noVoteForSomeoneElse(uint256 pId, uint8 sup, method f) filtered {f -> f.sel bool hasVotedBefore_User = hasVoted(e, pId, user); - helperFunctionsWithRevert(pId, f, e); + castVote@withrevert(e, pId, sup); require(!lastReverted); bool hasVotedAfter_User = hasVoted(e, pId, user); From 43e37f0184b09768a85d40ffd07553965743c69b Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Thu, 25 Nov 2021 16:25:40 +0200 Subject: [PATCH 117/254] executedImplyStartAndEndDateNonZero inv fix --- certora/specs/GovernorBase.spec | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index ef2d68baf..8487804ab 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -98,9 +98,9 @@ function helperFunctionsWithRevert(uint256 proposalId, method f, env e) { // To use env with general preserved block disable type checking [--disableLocalTypeChecking] invariant startAndEndDatesNonZero(uint256 pId) proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0 - /*{ preserved with (env e){ + { preserved with (env e){ require e.block.number > 0; - }}*/ + }} /* @@ -121,7 +121,8 @@ invariant canceledImplyStartAndEndDateNonZero(uint pId) invariant executedImplyStartAndEndDateNonZero(uint pId) isExecuted(pId) => proposalSnapshot(pId) != 0 { preserved with (env e){ - require e.block.number > 0; + requireInvariant startAndEndDatesNonZero(pId); + require e.block.number > 0; }} From f40c48a83d529b4cda7163345044e739bf8c366d Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Sat, 27 Nov 2021 20:22:25 +0200 Subject: [PATCH 118/254] madeVeryfyAllMoreFlexible --- certora/README.md | 2 +- ...irstPriority.sol => WizardControlFirstPriority.sol} | 2 +- certora/scripts/WizardControlFirstPriority.sh | 10 ++++++++++ certora/scripts/WizardFirstPriority.sh | 9 --------- certora/scripts/verifyAll.sh | 4 ++-- 5 files changed, 14 insertions(+), 13 deletions(-) rename certora/harnesses/{WizardFirstPriority.sol => WizardControlFirstPriority.sol} (96%) create mode 100644 certora/scripts/WizardControlFirstPriority.sh delete mode 100644 certora/scripts/WizardFirstPriority.sh diff --git a/certora/README.md b/certora/README.md index 1ad6507f2..16fcd69bf 100644 --- a/certora/README.md +++ b/certora/README.md @@ -18,7 +18,7 @@ sh certora/scripts/WizardFirstPriority.sh After the job is complete, the results will be available on [the Certora portal](https://vaas-stg.certora.com/). -The `verifyAll` script runs all spec files agains all contracts in the `certora/harness` that start with `Wizard` meaning that a contract generated in [wizard](https://wizard.openzeppelin.com/#governor). If you want to verify new wizard's instance you also need to harness this contract. We don't recommend to do it because a list of harnesses may go beyond wizard's contract. Moreover, the set of setting to run the Certora verification service may vary. The main goal of this script is to run all specs written by the team against all contracts properly harnessed. +The `verifyAll` script runs all spec files agains all contracts in the `certora/harness` that start with `Wizard` meaning that a contract generated in [wizard](https://wizard.openzeppelin.com/#governor). If you want to verify new wizard's instance you also need to give it a name starting with "Wizard..." or "WizardControl..." if you use `TimelockController` and harness this contract. We don't recommend to do it because a list of harnesses may go beyond wizard's contract. Moreover, the set of setting to run the Certora verification service may vary. The main goal of this script is to run all specs written by the team against all contracts properly harnessed. The `WizardFirstPriority` and `WizardFirstTry` scripts run one of the scripts for the corresponding contract. In order to run another spec you should change spec file name `` in the script (flag `--verify`): diff --git a/certora/harnesses/WizardFirstPriority.sol b/certora/harnesses/WizardControlFirstPriority.sol similarity index 96% rename from certora/harnesses/WizardFirstPriority.sol rename to certora/harnesses/WizardControlFirstPriority.sol index 7cf7f5223..ffb4f2d97 100644 --- a/certora/harnesses/WizardFirstPriority.sol +++ b/certora/harnesses/WizardControlFirstPriority.sol @@ -15,7 +15,7 @@ ERC20Votes TimelockCOntroller */ -contract WizardFirstPriority is Governor, GovernorProposalThreshold, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { +contract WizardControlFirstPriority is Governor, GovernorProposalThreshold, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { constructor(ERC20Votes _token, TimelockController _timelock, string memory name, uint256 quorumFraction) Governor(name) GovernorVotes(_token) diff --git a/certora/scripts/WizardControlFirstPriority.sh b/certora/scripts/WizardControlFirstPriority.sh new file mode 100644 index 000000000..ba7b26482 --- /dev/null +++ b/certora/scripts/WizardControlFirstPriority.sh @@ -0,0 +1,10 @@ +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardControlFirstPriority.sol \ + --link WizardControlFirstPriority:token=ERC20VotesHarness \ + --verify WizardFirstPriority:certora/specs/GovernorBase.spec \ + --solc solc8.2 \ + --disableLocalTypeChecking \ + --staging shelly/forSasha \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule executedImplyStartAndEndDateNonZero \ + --msg "$1" \ No newline at end of file diff --git a/certora/scripts/WizardFirstPriority.sh b/certora/scripts/WizardFirstPriority.sh deleted file mode 100644 index 1a491bbda..000000000 --- a/certora/scripts/WizardFirstPriority.sh +++ /dev/null @@ -1,9 +0,0 @@ -certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardFirstPriority.sol \ - --link WizardFirstPriority:token=ERC20VotesHarness \ - --verify WizardFirstPriority:certora/specs/GovernorCountingSimple.spec \ - --solc solc8.2 \ - --staging shelly/forSasha \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --rule noVoteForSomeoneElse \ - --msg "$1" \ No newline at end of file diff --git a/certora/scripts/verifyAll.sh b/certora/scripts/verifyAll.sh index d2ce48350..864aa47bd 100644 --- a/certora/scripts/verifyAll.sh +++ b/certora/scripts/verifyAll.sh @@ -5,10 +5,10 @@ do contractFile=$(basename $contract) specFile=$(basename $spec) echo "Processing ${contractFile%.*} with $specFile" - if [ "${contractFile%.*}" = "WizardFirstPriority" ]; + if [[ "${contractFile%.*}" = *"WizardControl"* ]]; then certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ - --link WizardFirstPriority:token=ERC20VotesHarness \ + --link ${contractFile%.*}:token=ERC20VotesHarness \ --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ --solc solc8.2 \ --staging shelly/forSasha \ From de594921cc88dfde77f0743d8bd807ccbb8911a8 Mon Sep 17 00:00:00 2001 From: Michael M <91594326+MichaelMorami@users.noreply.github.com> Date: Tue, 30 Nov 2021 18:10:20 +0200 Subject: [PATCH 119/254] fix script --- certora/scripts/WizardControlFirstPriority.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/certora/scripts/WizardControlFirstPriority.sh b/certora/scripts/WizardControlFirstPriority.sh index ba7b26482..18fccf804 100644 --- a/certora/scripts/WizardControlFirstPriority.sh +++ b/certora/scripts/WizardControlFirstPriority.sh @@ -1,10 +1,9 @@ certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardControlFirstPriority.sol \ --link WizardControlFirstPriority:token=ERC20VotesHarness \ - --verify WizardFirstPriority:certora/specs/GovernorBase.spec \ + --verify WizardControlFirstPriority:certora/specs/GovernorBase.spec \ --solc solc8.2 \ - --disableLocalTypeChecking \ --staging shelly/forSasha \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule executedImplyStartAndEndDateNonZero \ + --rule canVoteDuringVotingPeriod \ --msg "$1" \ No newline at end of file From 96c6120609fdff6ea762d3ed43e30f4a22d8560a Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 1 Dec 2021 11:59:30 +0200 Subject: [PATCH 120/254] NewFileForRulesInProgress --- certora/specs/RulesInProgress.spec | 139 +++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 certora/specs/RulesInProgress.spec diff --git a/certora/specs/RulesInProgress.spec b/certora/specs/RulesInProgress.spec new file mode 100644 index 000000000..cbad3336e --- /dev/null +++ b/certora/specs/RulesInProgress.spec @@ -0,0 +1,139 @@ +////////////////////////////////////////////////////////////////////////////// +////////////// THIS SPEC IS A RESERVE FOR NOT IN PROGRESS ////////////// +////////////////////////////////////////////////////////////////////////////// + +import "GovernorBase.spec" + +using ERC20VotesHarness as erc20votes + +methods { + ghost_sum_vote_power_by_id(uint256) returns uint256 envfree + + quorum(uint256) returns uint256 + proposalVotes(uint256) returns (uint256, uint256, uint256) envfree + + quorumNumerator() returns uint256 + _executor() returns address + + erc20votes._getPastVotes(address, uint256) returns uint256 + + getExecutor() returns address + + timelock() returns address +} + + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// GHOSTS ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +//////////// ghosts to keep track of votes counting //////////// + +/* + * the sum of voting power of those who voted + */ +ghost sum_all_votes_power() returns uint256 { + init_state axiom sum_all_votes_power() == 0; +} + +hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE { + havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; +} + +/* + * sum of all votes casted per proposal + */ +ghost tracked_weight(uint256) returns uint256 { + init_state axiom forall uint256 p. tracked_weight(p) == 0; +} + +/* + * sum of all votes casted + */ +ghost sum_tracked_weight() returns uint256 { + init_state axiom sum_tracked_weight() == 0; +} + +/* + * getter for _proposalVotes.againstVotes + */ +ghost votesAgainst() returns uint256 { + init_state axiom votesAgainst() == 0; +} + +/* + * getter for _proposalVotes.forVotes + */ +ghost votesFor() returns uint256 { + init_state axiom votesFor() == 0; +} + +/* + * getter for _proposalVotes.abstainVotes + */ +ghost votesAbstain() returns uint256 { + init_state axiom votesAbstain() == 0; +} + +hook Sstore _proposalVotes [KEY uint256 pId].againstVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesAgainst assuming votesAgainst@new() == votesAgainst@old() - old_votes + votes; +} + +hook Sstore _proposalVotes [KEY uint256 pId].forVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesFor assuming votesFor@new() == votesFor@old() - old_votes + votes; +} + +hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesAbstain assuming votesAbstain@new() == votesAbstain@old() - old_votes + votes; +} + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////// INVARIANTS //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// RULES ////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +//NOT FINISHED +/* +* the sum of voting power of those who voted is less or equal to the maximum possible votes, per each proposal +*/ +rule possibleTotalVotes(uint256 pId, uint8 sup, env e, method f) { + + // add requireinvariant for all i, j. i = i - 1 && i < j => checkpointlookup[i] < checkpointlookup[j]; + require tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)); + + uint256 againstB; + uint256 forB; + uint256 absatinB; + againstB, forB, absatinB = proposalVotes(pId); + + calldataarg args; + //f(e, args); + + castVote(e, pId, sup); + + uint256 against; + uint256 for; + uint256 absatin; + against, for, absatin = proposalVotes(pId); + + uint256 ps = proposalSnapshot(pId); + + assert tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)), "bla bla bla"; +} \ No newline at end of file From dae72a7e1bca00f278eed617001b9a1db4475777 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 1 Dec 2021 11:59:59 +0200 Subject: [PATCH 121/254] FixingScriptsToWorkWithNewChanges --- certora/scripts/WizardControlFirstPriority.sh | 1 + certora/scripts/verifyAll.sh | 43 ++++++++++--------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/certora/scripts/WizardControlFirstPriority.sh b/certora/scripts/WizardControlFirstPriority.sh index 18fccf804..3f367de2c 100644 --- a/certora/scripts/WizardControlFirstPriority.sh +++ b/certora/scripts/WizardControlFirstPriority.sh @@ -2,6 +2,7 @@ certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardContr --link WizardControlFirstPriority:token=ERC20VotesHarness \ --verify WizardControlFirstPriority:certora/specs/GovernorBase.spec \ --solc solc8.2 \ + --disableLocalTypeChecking \ --staging shelly/forSasha \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ diff --git a/certora/scripts/verifyAll.sh b/certora/scripts/verifyAll.sh index 864aa47bd..6a7a42fa9 100644 --- a/certora/scripts/verifyAll.sh +++ b/certora/scripts/verifyAll.sh @@ -4,27 +4,30 @@ do do contractFile=$(basename $contract) specFile=$(basename $spec) - echo "Processing ${contractFile%.*} with $specFile" - if [[ "${contractFile%.*}" = *"WizardControl"* ]]; + if [[ "${specFile%.*}" != "RulesInProgress" ]]; then - certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ - --link ${contractFile%.*}:token=ERC20VotesHarness \ - --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ - --solc solc8.2 \ - --staging shelly/forSasha \ - --disableLocalTypeChecking \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --msg "checking $specFile on ${contractFile%.*}" - else - certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ - --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ - --solc solc8.2 \ - --staging shelly/forSasha \ - --disableLocalTypeChecking \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --msg "checking $specFile on ${contractFile%.*}" + echo "Processing ${contractFile%.*} with $specFile" + if [[ "${contractFile%.*}" = *"WizardControl"* ]]; + then + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ + --link ${contractFile%.*}:token=ERC20VotesHarness \ + --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --disableLocalTypeChecking \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --msg "checking $specFile on ${contractFile%.*}" + else + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ + --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --disableLocalTypeChecking \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --msg "checking $specFile on ${contractFile%.*}" + fi fi done done \ No newline at end of file From 7d0eeab6f76a1595d0f345f05483a7337735fb00 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 1 Dec 2021 12:02:15 +0200 Subject: [PATCH 122/254] HarnessCleaning --- certora/harnesses/ERC20VotesHarness.sol | 11 +---------- certora/harnesses/WizardControlFirstPriority.sol | 3 ++- certora/harnesses/WizardFirstTry.sol | 1 + 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/certora/harnesses/ERC20VotesHarness.sol b/certora/harnesses/ERC20VotesHarness.sol index b83fc077f..818a2ab0f 100644 --- a/certora/harnesses/ERC20VotesHarness.sol +++ b/certora/harnesses/ERC20VotesHarness.sol @@ -25,13 +25,4 @@ contract ERC20VotesHarness is ERC20Votes { _getPastVotes[delegator][block.number] -= balanceOf(delegator); _getPastVotes[delegatee][block.number] += balanceOf(delegator); } - - /* - function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { - uint256 gpv = super.getPastVotes(account, blockNumber); - require (_getPastVotes[account][blockNumber] == gpv); - return gpv; - } - */ - -} \ No newline at end of file +} diff --git a/certora/harnesses/WizardControlFirstPriority.sol b/certora/harnesses/WizardControlFirstPriority.sol index ffb4f2d97..5e2eff17f 100644 --- a/certora/harnesses/WizardControlFirstPriority.sol +++ b/certora/harnesses/WizardControlFirstPriority.sol @@ -12,7 +12,7 @@ import "../../contracts/governance/extensions/GovernorProposalThreshold.sol"; Wizard options: ProposalThreshhold = 10 ERC20Votes -TimelockCOntroller +TimelockController */ contract WizardControlFirstPriority is Governor, GovernorProposalThreshold, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { @@ -77,6 +77,7 @@ contract WizardControlFirstPriority is Governor, GovernorProposalThreshold, Gove return _proposalThreshold; // HARNESS: parametric } + // original code, not harnessed // The following functions are overrides required by Solidity. function quorum(uint256 blockNumber) diff --git a/certora/harnesses/WizardFirstTry.sol b/certora/harnesses/WizardFirstTry.sol index cdcd3960e..026e3d041 100644 --- a/certora/harnesses/WizardFirstTry.sol +++ b/certora/harnesses/WizardFirstTry.sol @@ -68,6 +68,7 @@ contract WizardFirstTry is Governor, GovernorCountingSimple, GovernorVotes, Gove return _votingPeriod; // HARNESS: parametric } + // original code, not harnessed // The following functions are overrides required by Solidity. function quorum(uint256 blockNumber) From 749738f2aa273c443aeeed16dc7dbb9422fe2b1b Mon Sep 17 00:00:00 2001 From: Michael George Date: Wed, 1 Dec 2021 10:03:21 -0500 Subject: [PATCH 123/254] moved contract modifications into munged directory --- certora/munged/access/AccessControl.sol | 223 ++++++++ .../munged/access/AccessControlEnumerable.sol | 64 +++ certora/munged/access/IAccessControl.sol | 88 +++ .../access/IAccessControlEnumerable.sol | 31 + certora/munged/access/Ownable.sol | 76 +++ certora/munged/access/README.adoc | 21 + certora/munged/finance/PaymentSplitter.sol | 189 ++++++ certora/munged/finance/README.adoc | 20 + certora/munged/finance/VestingWallet.sol | 135 +++++ certora/munged/governance/Governor.sol | 357 ++++++++++++ certora/munged/governance/IGovernor.sol | 218 +++++++ certora/munged/governance/README.adoc | 168 ++++++ .../munged/governance/TimelockController.sol | 354 ++++++++++++ .../GovernorCompatibilityBravo.sol | 288 ++++++++++ .../IGovernorCompatibilityBravo.sol | 114 ++++ .../extensions/GovernorCountingSimple.sol | 106 ++++ .../extensions/GovernorProposalThreshold.sol | 23 + .../extensions/GovernorSettings.sol | 114 ++++ .../extensions/GovernorTimelockCompound.sol | 244 ++++++++ .../extensions/GovernorTimelockControl.sol | 154 +++++ .../governance/extensions/GovernorVotes.sol | 28 + .../extensions/GovernorVotesComp.sol | 27 + .../GovernorVotesQuorumFraction.sol | 50 ++ .../extensions/IGovernorTimelock.sol | 26 + certora/munged/interfaces/IERC1155.sol | 6 + .../munged/interfaces/IERC1155MetadataURI.sol | 6 + .../munged/interfaces/IERC1155Receiver.sol | 6 + certora/munged/interfaces/IERC1271.sol | 19 + certora/munged/interfaces/IERC1363.sol | 95 +++ .../munged/interfaces/IERC1363Receiver.sol | 32 ++ certora/munged/interfaces/IERC1363Spender.sol | 30 + certora/munged/interfaces/IERC165.sol | 6 + .../munged/interfaces/IERC1820Implementer.sol | 6 + .../munged/interfaces/IERC1820Registry.sol | 6 + certora/munged/interfaces/IERC20.sol | 6 + certora/munged/interfaces/IERC20Metadata.sol | 6 + certora/munged/interfaces/IERC2981.sol | 23 + certora/munged/interfaces/IERC3156.sol | 7 + .../interfaces/IERC3156FlashBorrower.sol | 29 + .../munged/interfaces/IERC3156FlashLender.sol | 43 ++ certora/munged/interfaces/IERC721.sol | 6 + .../munged/interfaces/IERC721Enumerable.sol | 6 + certora/munged/interfaces/IERC721Metadata.sol | 6 + certora/munged/interfaces/IERC721Receiver.sol | 6 + certora/munged/interfaces/IERC777.sol | 6 + .../munged/interfaces/IERC777Recipient.sol | 6 + certora/munged/interfaces/IERC777Sender.sol | 6 + certora/munged/interfaces/README.adoc | 50 ++ certora/munged/interfaces/draft-IERC2612.sol | 8 + certora/munged/metatx/ERC2771Context.sol | 40 ++ certora/munged/metatx/MinimalForwarder.sol | 59 ++ certora/munged/metatx/README.adoc | 12 + .../mocks/AccessControlEnumerableMock.sol | 17 + certora/munged/mocks/AccessControlMock.sol | 17 + certora/munged/mocks/AddressImpl.sol | 46 ++ certora/munged/mocks/ArraysImpl.sol | 19 + certora/munged/mocks/BadBeacon.sol | 11 + certora/munged/mocks/BitmapMock.sol | 27 + certora/munged/mocks/CallReceiverMock.sol | 50 ++ .../munged/mocks/ClashingImplementation.sol | 18 + certora/munged/mocks/ClonesMock.sol | 36 ++ .../munged/mocks/ConditionalEscrowMock.sol | 18 + certora/munged/mocks/ContextMock.sol | 33 ++ certora/munged/mocks/CountersImpl.sol | 27 + certora/munged/mocks/Create2Impl.sol | 34 ++ certora/munged/mocks/DummyImplementation.sol | 61 ++ certora/munged/mocks/ECDSAMock.sol | 41 ++ certora/munged/mocks/EIP712External.sol | 31 + certora/munged/mocks/ERC1155BurnableMock.sol | 18 + certora/munged/mocks/ERC1155Mock.sol | 51 ++ certora/munged/mocks/ERC1155PausableMock.sol | 29 + certora/munged/mocks/ERC1155ReceiverMock.sol | 52 ++ certora/munged/mocks/ERC1155SupplyMock.sol | 21 + certora/munged/mocks/ERC1271WalletMock.sol | 17 + .../ERC165/ERC165InterfacesSupported.sol | 58 ++ .../munged/mocks/ERC165/ERC165MissingData.sol | 7 + .../mocks/ERC165/ERC165NotSupported.sol | 5 + certora/munged/mocks/ERC165CheckerMock.sol | 25 + certora/munged/mocks/ERC165Mock.sol | 7 + certora/munged/mocks/ERC165StorageMock.sol | 11 + .../munged/mocks/ERC1820ImplementerMock.sol | 11 + certora/munged/mocks/ERC20BurnableMock.sol | 16 + certora/munged/mocks/ERC20CappedMock.sol | 17 + certora/munged/mocks/ERC20DecimalsMock.sol | 21 + certora/munged/mocks/ERC20FlashMintMock.sol | 16 + certora/munged/mocks/ERC20Mock.sol | 41 ++ certora/munged/mocks/ERC20PausableMock.sol | 33 ++ certora/munged/mocks/ERC20PermitMock.sol | 20 + certora/munged/mocks/ERC20SnapshotMock.sol | 28 + certora/munged/mocks/ERC20VotesCompMock.sol | 21 + certora/munged/mocks/ERC20VotesMock.sol | 21 + certora/munged/mocks/ERC20WrapperMock.sol | 17 + certora/munged/mocks/ERC2771ContextMock.sol | 19 + .../munged/mocks/ERC3156FlashBorrowerMock.sol | 53 ++ certora/munged/mocks/ERC721BurnableMock.sol | 29 + certora/munged/mocks/ERC721EnumerableMock.sol | 51 ++ certora/munged/mocks/ERC721Mock.sol | 41 ++ certora/munged/mocks/ERC721PausableMock.sol | 45 ++ certora/munged/mocks/ERC721ReceiverMock.sol | 42 ++ certora/munged/mocks/ERC721URIStorageMock.sol | 55 ++ certora/munged/mocks/ERC777Mock.sol | 56 ++ .../mocks/ERC777SenderRecipientMock.sol | 161 ++++++ certora/munged/mocks/EnumerableMapMock.sol | 47 ++ certora/munged/mocks/EnumerableSetMock.sol | 110 ++++ certora/munged/mocks/EtherReceiverMock.sol | 17 + certora/munged/mocks/GovernorCompMock.sol | 41 ++ .../mocks/GovernorCompatibilityBravoMock.sol | 140 +++++ certora/munged/mocks/GovernorMock.sol | 60 ++ .../mocks/GovernorTimelockCompoundMock.sol | 108 ++++ .../mocks/GovernorTimelockControlMock.sol | 108 ++++ certora/munged/mocks/InitializableMock.sol | 34 ++ certora/munged/mocks/MathMock.sol | 23 + certora/munged/mocks/MerkleProofWrapper.sol | 19 + certora/munged/mocks/MulticallTest.sol | 23 + certora/munged/mocks/MulticallTokenMock.sol | 10 + .../MultipleInheritanceInitializableMocks.sol | 81 +++ certora/munged/mocks/OwnableMock.sol | 7 + certora/munged/mocks/PausableMock.sol | 31 + certora/munged/mocks/PullPaymentMock.sol | 15 + certora/munged/mocks/ReentrancyAttack.sol | 12 + certora/munged/mocks/ReentrancyMock.sol | 43 ++ .../munged/mocks/RegressionImplementation.sol | 61 ++ certora/munged/mocks/SafeCastMock.sol | 66 +++ certora/munged/mocks/SafeERC20Helper.sol | 144 +++++ certora/munged/mocks/SafeMathMock.sol | 138 +++++ certora/munged/mocks/SignatureCheckerMock.sol | 17 + certora/munged/mocks/SignedSafeMathMock.sol | 23 + .../SingleInheritanceInitializableMocks.sol | 49 ++ certora/munged/mocks/StorageSlotMock.sol | 41 ++ certora/munged/mocks/StringsMock.sol | 19 + .../munged/mocks/TimersBlockNumberImpl.sol | 39 ++ certora/munged/mocks/TimersTimestampImpl.sol | 39 ++ certora/munged/mocks/UUPS/TestInProd.sol | 31 + .../munged/mocks/compound/CompTimelock.sol | 174 ++++++ certora/munged/mocks/wizard/MyGovernor1.sol | 96 ++++ certora/munged/mocks/wizard/MyGovernor2.sol | 102 ++++ certora/munged/mocks/wizard/MyGovernor3.sol | 105 ++++ certora/munged/package.json | 32 ++ certora/munged/proxy/Clones.sol | 84 +++ certora/munged/proxy/ERC1967/ERC1967Proxy.sol | 33 ++ .../munged/proxy/ERC1967/ERC1967Upgrade.sol | 194 +++++++ certora/munged/proxy/Proxy.sol | 86 +++ certora/munged/proxy/README.adoc | 83 +++ certora/munged/proxy/beacon/BeaconProxy.sol | 62 ++ certora/munged/proxy/beacon/IBeacon.sol | 16 + .../munged/proxy/beacon/UpgradeableBeacon.sol | 65 +++ .../munged/proxy/transparent/ProxyAdmin.sol | 81 +++ .../TransparentUpgradeableProxy.sol | 125 ++++ certora/munged/proxy/utils/Initializable.sol | 62 ++ .../munged/proxy/utils/UUPSUpgradeable.sol | 73 +++ certora/munged/security/Pausable.sol | 91 +++ certora/munged/security/PullPayment.sol | 70 +++ certora/munged/security/README.adoc | 20 + certora/munged/security/ReentrancyGuard.sol | 63 ++ certora/munged/token/ERC1155/ERC1155.sol | 464 +++++++++++++++ certora/munged/token/ERC1155/IERC1155.sol | 125 ++++ .../munged/token/ERC1155/IERC1155Receiver.sol | 58 ++ certora/munged/token/ERC1155/README.adoc | 47 ++ .../ERC1155/extensions/ERC1155Burnable.sol | 40 ++ .../ERC1155/extensions/ERC1155Pausable.sol | 38 ++ .../ERC1155/extensions/ERC1155Supply.sol | 58 ++ .../extensions/IERC1155MetadataURI.sol | 22 + .../presets/ERC1155PresetMinterPauser.sol | 126 ++++ .../token/ERC1155/utils/ERC1155Holder.sol | 36 ++ .../token/ERC1155/utils/ERC1155Receiver.sol | 19 + certora/munged/token/ERC20/ERC20.sol | 356 ++++++++++++ certora/munged/token/ERC20/IERC20.sol | 82 +++ certora/munged/token/ERC20/README.adoc | 83 +++ .../token/ERC20/extensions/ERC20Burnable.sol | 43 ++ .../token/ERC20/extensions/ERC20Capped.sol | 37 ++ .../token/ERC20/extensions/ERC20FlashMint.sol | 77 +++ .../token/ERC20/extensions/ERC20Pausable.sol | 33 ++ .../token/ERC20/extensions/ERC20Snapshot.sol | 195 +++++++ .../token/ERC20/extensions/ERC20Votes.sol | 260 +++++++++ .../token/ERC20/extensions/ERC20VotesComp.sol | 48 ++ .../token/ERC20/extensions/ERC20Wrapper.sol | 52 ++ .../token/ERC20/extensions/IERC20Metadata.sol | 28 + .../ERC20/extensions/draft-ERC20Permit.sol | 87 +++ .../ERC20/extensions/draft-IERC20Permit.sol | 60 ++ .../ERC20/presets/ERC20PresetFixedSupply.sol | 33 ++ .../ERC20/presets/ERC20PresetMinterPauser.sol | 92 +++ .../munged/token/ERC20/utils/SafeERC20.sol | 99 ++++ .../token/ERC20/utils/TokenTimelock.sol | 70 +++ certora/munged/token/ERC721/ERC721.sol | 424 ++++++++++++++ certora/munged/token/ERC721/IERC721.sol | 143 +++++ .../munged/token/ERC721/IERC721Receiver.sol | 27 + certora/munged/token/ERC721/README.adoc | 52 ++ .../ERC721/extensions/ERC721Burnable.sol | 26 + .../ERC721/extensions/ERC721Enumerable.sol | 163 ++++++ .../ERC721/extensions/ERC721Pausable.sol | 33 ++ .../ERC721/extensions/ERC721URIStorage.sol | 67 +++ .../ERC721/extensions/IERC721Enumerable.sol | 29 + .../ERC721/extensions/IERC721Metadata.sol | 27 + .../ERC721PresetMinterPauserAutoId.sol | 137 +++++ .../token/ERC721/utils/ERC721Holder.sol | 28 + certora/munged/token/ERC777/ERC777.sol | 539 ++++++++++++++++++ certora/munged/token/ERC777/IERC777.sol | 193 +++++++ .../munged/token/ERC777/IERC777Recipient.sol | 35 ++ certora/munged/token/ERC777/IERC777Sender.sol | 35 ++ certora/munged/token/ERC777/README.adoc | 30 + .../presets/ERC777PresetFixedSupply.sol | 30 + certora/munged/utils/Address.sol | 217 +++++++ certora/munged/utils/Arrays.sol | 48 ++ certora/munged/utils/Context.sol | 24 + certora/munged/utils/Counters.sol | 43 ++ certora/munged/utils/Create2.sol | 65 +++ certora/munged/utils/Multicall.sol | 24 + certora/munged/utils/README.adoc | 103 ++++ certora/munged/utils/StorageSlot.sol | 84 +++ certora/munged/utils/Strings.sol | 67 +++ certora/munged/utils/Timers.sol | 73 +++ certora/munged/utils/cryptography/ECDSA.sol | 234 ++++++++ .../munged/utils/cryptography/MerkleProof.sol | 52 ++ .../utils/cryptography/SignatureChecker.sol | 36 ++ .../utils/cryptography/draft-EIP712.sol | 104 ++++ .../munged/utils/escrow/ConditionalEscrow.sol | 25 + certora/munged/utils/escrow/Escrow.sol | 63 ++ certora/munged/utils/escrow/RefundEscrow.sol | 100 ++++ certora/munged/utils/introspection/ERC165.sol | 29 + .../utils/introspection/ERC165Checker.sol | 113 ++++ .../utils/introspection/ERC165Storage.sol | 42 ++ .../introspection/ERC1820Implementer.sol | 44 ++ .../munged/utils/introspection/IERC165.sol | 25 + .../introspection/IERC1820Implementer.sol | 20 + .../utils/introspection/IERC1820Registry.sol | 116 ++++ certora/munged/utils/math/Math.sol | 43 ++ certora/munged/utils/math/SafeCast.sol | 241 ++++++++ certora/munged/utils/math/SafeMath.sol | 227 ++++++++ certora/munged/utils/math/SignedSafeMath.sol | 68 +++ certora/munged/utils/structs/BitMaps.sol | 55 ++ .../munged/utils/structs/EnumerableMap.sol | 240 ++++++++ .../munged/utils/structs/EnumerableSet.sol | 357 ++++++++++++ contracts/governance/Governor.sol | 10 +- contracts/governance/TimelockController.sol | 1 - .../GovernorCompatibilityBravo.sol | 4 +- .../extensions/GovernorCountingSimple.sol | 4 +- .../extensions/GovernorTimelockControl.sol | 2 +- .../token/ERC20/extensions/ERC20Votes.sol | 2 +- 238 files changed, 16348 insertions(+), 12 deletions(-) create mode 100644 certora/munged/access/AccessControl.sol create mode 100644 certora/munged/access/AccessControlEnumerable.sol create mode 100644 certora/munged/access/IAccessControl.sol create mode 100644 certora/munged/access/IAccessControlEnumerable.sol create mode 100644 certora/munged/access/Ownable.sol create mode 100644 certora/munged/access/README.adoc create mode 100644 certora/munged/finance/PaymentSplitter.sol create mode 100644 certora/munged/finance/README.adoc create mode 100644 certora/munged/finance/VestingWallet.sol create mode 100644 certora/munged/governance/Governor.sol create mode 100644 certora/munged/governance/IGovernor.sol create mode 100644 certora/munged/governance/README.adoc create mode 100644 certora/munged/governance/TimelockController.sol create mode 100644 certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol create mode 100644 certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol create mode 100644 certora/munged/governance/extensions/GovernorCountingSimple.sol create mode 100644 certora/munged/governance/extensions/GovernorProposalThreshold.sol create mode 100644 certora/munged/governance/extensions/GovernorSettings.sol create mode 100644 certora/munged/governance/extensions/GovernorTimelockCompound.sol create mode 100644 certora/munged/governance/extensions/GovernorTimelockControl.sol create mode 100644 certora/munged/governance/extensions/GovernorVotes.sol create mode 100644 certora/munged/governance/extensions/GovernorVotesComp.sol create mode 100644 certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol create mode 100644 certora/munged/governance/extensions/IGovernorTimelock.sol create mode 100644 certora/munged/interfaces/IERC1155.sol create mode 100644 certora/munged/interfaces/IERC1155MetadataURI.sol create mode 100644 certora/munged/interfaces/IERC1155Receiver.sol create mode 100644 certora/munged/interfaces/IERC1271.sol create mode 100644 certora/munged/interfaces/IERC1363.sol create mode 100644 certora/munged/interfaces/IERC1363Receiver.sol create mode 100644 certora/munged/interfaces/IERC1363Spender.sol create mode 100644 certora/munged/interfaces/IERC165.sol create mode 100644 certora/munged/interfaces/IERC1820Implementer.sol create mode 100644 certora/munged/interfaces/IERC1820Registry.sol create mode 100644 certora/munged/interfaces/IERC20.sol create mode 100644 certora/munged/interfaces/IERC20Metadata.sol create mode 100644 certora/munged/interfaces/IERC2981.sol create mode 100644 certora/munged/interfaces/IERC3156.sol create mode 100644 certora/munged/interfaces/IERC3156FlashBorrower.sol create mode 100644 certora/munged/interfaces/IERC3156FlashLender.sol create mode 100644 certora/munged/interfaces/IERC721.sol create mode 100644 certora/munged/interfaces/IERC721Enumerable.sol create mode 100644 certora/munged/interfaces/IERC721Metadata.sol create mode 100644 certora/munged/interfaces/IERC721Receiver.sol create mode 100644 certora/munged/interfaces/IERC777.sol create mode 100644 certora/munged/interfaces/IERC777Recipient.sol create mode 100644 certora/munged/interfaces/IERC777Sender.sol create mode 100644 certora/munged/interfaces/README.adoc create mode 100644 certora/munged/interfaces/draft-IERC2612.sol create mode 100644 certora/munged/metatx/ERC2771Context.sol create mode 100644 certora/munged/metatx/MinimalForwarder.sol create mode 100644 certora/munged/metatx/README.adoc create mode 100644 certora/munged/mocks/AccessControlEnumerableMock.sol create mode 100644 certora/munged/mocks/AccessControlMock.sol create mode 100644 certora/munged/mocks/AddressImpl.sol create mode 100644 certora/munged/mocks/ArraysImpl.sol create mode 100644 certora/munged/mocks/BadBeacon.sol create mode 100644 certora/munged/mocks/BitmapMock.sol create mode 100644 certora/munged/mocks/CallReceiverMock.sol create mode 100644 certora/munged/mocks/ClashingImplementation.sol create mode 100644 certora/munged/mocks/ClonesMock.sol create mode 100644 certora/munged/mocks/ConditionalEscrowMock.sol create mode 100644 certora/munged/mocks/ContextMock.sol create mode 100644 certora/munged/mocks/CountersImpl.sol create mode 100644 certora/munged/mocks/Create2Impl.sol create mode 100644 certora/munged/mocks/DummyImplementation.sol create mode 100644 certora/munged/mocks/ECDSAMock.sol create mode 100644 certora/munged/mocks/EIP712External.sol create mode 100644 certora/munged/mocks/ERC1155BurnableMock.sol create mode 100644 certora/munged/mocks/ERC1155Mock.sol create mode 100644 certora/munged/mocks/ERC1155PausableMock.sol create mode 100644 certora/munged/mocks/ERC1155ReceiverMock.sol create mode 100644 certora/munged/mocks/ERC1155SupplyMock.sol create mode 100644 certora/munged/mocks/ERC1271WalletMock.sol create mode 100644 certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol create mode 100644 certora/munged/mocks/ERC165/ERC165MissingData.sol create mode 100644 certora/munged/mocks/ERC165/ERC165NotSupported.sol create mode 100644 certora/munged/mocks/ERC165CheckerMock.sol create mode 100644 certora/munged/mocks/ERC165Mock.sol create mode 100644 certora/munged/mocks/ERC165StorageMock.sol create mode 100644 certora/munged/mocks/ERC1820ImplementerMock.sol create mode 100644 certora/munged/mocks/ERC20BurnableMock.sol create mode 100644 certora/munged/mocks/ERC20CappedMock.sol create mode 100644 certora/munged/mocks/ERC20DecimalsMock.sol create mode 100644 certora/munged/mocks/ERC20FlashMintMock.sol create mode 100644 certora/munged/mocks/ERC20Mock.sol create mode 100644 certora/munged/mocks/ERC20PausableMock.sol create mode 100644 certora/munged/mocks/ERC20PermitMock.sol create mode 100644 certora/munged/mocks/ERC20SnapshotMock.sol create mode 100644 certora/munged/mocks/ERC20VotesCompMock.sol create mode 100644 certora/munged/mocks/ERC20VotesMock.sol create mode 100644 certora/munged/mocks/ERC20WrapperMock.sol create mode 100644 certora/munged/mocks/ERC2771ContextMock.sol create mode 100644 certora/munged/mocks/ERC3156FlashBorrowerMock.sol create mode 100644 certora/munged/mocks/ERC721BurnableMock.sol create mode 100644 certora/munged/mocks/ERC721EnumerableMock.sol create mode 100644 certora/munged/mocks/ERC721Mock.sol create mode 100644 certora/munged/mocks/ERC721PausableMock.sol create mode 100644 certora/munged/mocks/ERC721ReceiverMock.sol create mode 100644 certora/munged/mocks/ERC721URIStorageMock.sol create mode 100644 certora/munged/mocks/ERC777Mock.sol create mode 100644 certora/munged/mocks/ERC777SenderRecipientMock.sol create mode 100644 certora/munged/mocks/EnumerableMapMock.sol create mode 100644 certora/munged/mocks/EnumerableSetMock.sol create mode 100644 certora/munged/mocks/EtherReceiverMock.sol create mode 100644 certora/munged/mocks/GovernorCompMock.sol create mode 100644 certora/munged/mocks/GovernorCompatibilityBravoMock.sol create mode 100644 certora/munged/mocks/GovernorMock.sol create mode 100644 certora/munged/mocks/GovernorTimelockCompoundMock.sol create mode 100644 certora/munged/mocks/GovernorTimelockControlMock.sol create mode 100644 certora/munged/mocks/InitializableMock.sol create mode 100644 certora/munged/mocks/MathMock.sol create mode 100644 certora/munged/mocks/MerkleProofWrapper.sol create mode 100644 certora/munged/mocks/MulticallTest.sol create mode 100644 certora/munged/mocks/MulticallTokenMock.sol create mode 100644 certora/munged/mocks/MultipleInheritanceInitializableMocks.sol create mode 100644 certora/munged/mocks/OwnableMock.sol create mode 100644 certora/munged/mocks/PausableMock.sol create mode 100644 certora/munged/mocks/PullPaymentMock.sol create mode 100644 certora/munged/mocks/ReentrancyAttack.sol create mode 100644 certora/munged/mocks/ReentrancyMock.sol create mode 100644 certora/munged/mocks/RegressionImplementation.sol create mode 100644 certora/munged/mocks/SafeCastMock.sol create mode 100644 certora/munged/mocks/SafeERC20Helper.sol create mode 100644 certora/munged/mocks/SafeMathMock.sol create mode 100644 certora/munged/mocks/SignatureCheckerMock.sol create mode 100644 certora/munged/mocks/SignedSafeMathMock.sol create mode 100644 certora/munged/mocks/SingleInheritanceInitializableMocks.sol create mode 100644 certora/munged/mocks/StorageSlotMock.sol create mode 100644 certora/munged/mocks/StringsMock.sol create mode 100644 certora/munged/mocks/TimersBlockNumberImpl.sol create mode 100644 certora/munged/mocks/TimersTimestampImpl.sol create mode 100644 certora/munged/mocks/UUPS/TestInProd.sol create mode 100644 certora/munged/mocks/compound/CompTimelock.sol create mode 100644 certora/munged/mocks/wizard/MyGovernor1.sol create mode 100644 certora/munged/mocks/wizard/MyGovernor2.sol create mode 100644 certora/munged/mocks/wizard/MyGovernor3.sol create mode 100644 certora/munged/package.json create mode 100644 certora/munged/proxy/Clones.sol create mode 100644 certora/munged/proxy/ERC1967/ERC1967Proxy.sol create mode 100644 certora/munged/proxy/ERC1967/ERC1967Upgrade.sol create mode 100644 certora/munged/proxy/Proxy.sol create mode 100644 certora/munged/proxy/README.adoc create mode 100644 certora/munged/proxy/beacon/BeaconProxy.sol create mode 100644 certora/munged/proxy/beacon/IBeacon.sol create mode 100644 certora/munged/proxy/beacon/UpgradeableBeacon.sol create mode 100644 certora/munged/proxy/transparent/ProxyAdmin.sol create mode 100644 certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol create mode 100644 certora/munged/proxy/utils/Initializable.sol create mode 100644 certora/munged/proxy/utils/UUPSUpgradeable.sol create mode 100644 certora/munged/security/Pausable.sol create mode 100644 certora/munged/security/PullPayment.sol create mode 100644 certora/munged/security/README.adoc create mode 100644 certora/munged/security/ReentrancyGuard.sol create mode 100644 certora/munged/token/ERC1155/ERC1155.sol create mode 100644 certora/munged/token/ERC1155/IERC1155.sol create mode 100644 certora/munged/token/ERC1155/IERC1155Receiver.sol create mode 100644 certora/munged/token/ERC1155/README.adoc create mode 100644 certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol create mode 100644 certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol create mode 100644 certora/munged/token/ERC1155/extensions/ERC1155Supply.sol create mode 100644 certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol create mode 100644 certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol create mode 100644 certora/munged/token/ERC1155/utils/ERC1155Holder.sol create mode 100644 certora/munged/token/ERC1155/utils/ERC1155Receiver.sol create mode 100644 certora/munged/token/ERC20/ERC20.sol create mode 100644 certora/munged/token/ERC20/IERC20.sol create mode 100644 certora/munged/token/ERC20/README.adoc create mode 100644 certora/munged/token/ERC20/extensions/ERC20Burnable.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20Capped.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20FlashMint.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20Pausable.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20Snapshot.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20Votes.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20VotesComp.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20Wrapper.sol create mode 100644 certora/munged/token/ERC20/extensions/IERC20Metadata.sol create mode 100644 certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol create mode 100644 certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol create mode 100644 certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol create mode 100644 certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol create mode 100644 certora/munged/token/ERC20/utils/SafeERC20.sol create mode 100644 certora/munged/token/ERC20/utils/TokenTimelock.sol create mode 100644 certora/munged/token/ERC721/ERC721.sol create mode 100644 certora/munged/token/ERC721/IERC721.sol create mode 100644 certora/munged/token/ERC721/IERC721Receiver.sol create mode 100644 certora/munged/token/ERC721/README.adoc create mode 100644 certora/munged/token/ERC721/extensions/ERC721Burnable.sol create mode 100644 certora/munged/token/ERC721/extensions/ERC721Enumerable.sol create mode 100644 certora/munged/token/ERC721/extensions/ERC721Pausable.sol create mode 100644 certora/munged/token/ERC721/extensions/ERC721URIStorage.sol create mode 100644 certora/munged/token/ERC721/extensions/IERC721Enumerable.sol create mode 100644 certora/munged/token/ERC721/extensions/IERC721Metadata.sol create mode 100644 certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol create mode 100644 certora/munged/token/ERC721/utils/ERC721Holder.sol create mode 100644 certora/munged/token/ERC777/ERC777.sol create mode 100644 certora/munged/token/ERC777/IERC777.sol create mode 100644 certora/munged/token/ERC777/IERC777Recipient.sol create mode 100644 certora/munged/token/ERC777/IERC777Sender.sol create mode 100644 certora/munged/token/ERC777/README.adoc create mode 100644 certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol create mode 100644 certora/munged/utils/Address.sol create mode 100644 certora/munged/utils/Arrays.sol create mode 100644 certora/munged/utils/Context.sol create mode 100644 certora/munged/utils/Counters.sol create mode 100644 certora/munged/utils/Create2.sol create mode 100644 certora/munged/utils/Multicall.sol create mode 100644 certora/munged/utils/README.adoc create mode 100644 certora/munged/utils/StorageSlot.sol create mode 100644 certora/munged/utils/Strings.sol create mode 100644 certora/munged/utils/Timers.sol create mode 100644 certora/munged/utils/cryptography/ECDSA.sol create mode 100644 certora/munged/utils/cryptography/MerkleProof.sol create mode 100644 certora/munged/utils/cryptography/SignatureChecker.sol create mode 100644 certora/munged/utils/cryptography/draft-EIP712.sol create mode 100644 certora/munged/utils/escrow/ConditionalEscrow.sol create mode 100644 certora/munged/utils/escrow/Escrow.sol create mode 100644 certora/munged/utils/escrow/RefundEscrow.sol create mode 100644 certora/munged/utils/introspection/ERC165.sol create mode 100644 certora/munged/utils/introspection/ERC165Checker.sol create mode 100644 certora/munged/utils/introspection/ERC165Storage.sol create mode 100644 certora/munged/utils/introspection/ERC1820Implementer.sol create mode 100644 certora/munged/utils/introspection/IERC165.sol create mode 100644 certora/munged/utils/introspection/IERC1820Implementer.sol create mode 100644 certora/munged/utils/introspection/IERC1820Registry.sol create mode 100644 certora/munged/utils/math/Math.sol create mode 100644 certora/munged/utils/math/SafeCast.sol create mode 100644 certora/munged/utils/math/SafeMath.sol create mode 100644 certora/munged/utils/math/SignedSafeMath.sol create mode 100644 certora/munged/utils/structs/BitMaps.sol create mode 100644 certora/munged/utils/structs/EnumerableMap.sol create mode 100644 certora/munged/utils/structs/EnumerableSet.sol diff --git a/certora/munged/access/AccessControl.sol b/certora/munged/access/AccessControl.sol new file mode 100644 index 000000000..dae9f7077 --- /dev/null +++ b/certora/munged/access/AccessControl.sol @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (access/AccessControl.sol) + +pragma solidity ^0.8.0; + +import "./IAccessControl.sol"; +import "../utils/Context.sol"; +import "../utils/Strings.sol"; +import "../utils/introspection/ERC165.sol"; + +/** + * @dev Contract module that allows children to implement role-based access + * control mechanisms. This is a lightweight version that doesn't allow enumerating role + * members except through off-chain means by accessing the contract event logs. Some + * applications may benefit from on-chain enumerability, for those cases see + * {AccessControlEnumerable}. + * + * Roles are referred to by their `bytes32` identifier. These should be exposed + * in the external API and be unique. The best way to achieve this is by + * using `public constant` hash digests: + * + * ``` + * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); + * ``` + * + * Roles can be used to represent a set of permissions. To restrict access to a + * function call, use {hasRole}: + * + * ``` + * function foo() public { + * require(hasRole(MY_ROLE, msg.sender)); + * ... + * } + * ``` + * + * Roles can be granted and revoked dynamically via the {grantRole} and + * {revokeRole} functions. Each role has an associated admin role, and only + * accounts that have a role's admin role can call {grantRole} and {revokeRole}. + * + * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means + * that only accounts with this role will be able to grant or revoke other + * roles. More complex role relationships can be created by using + * {_setRoleAdmin}. + * + * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to + * grant and revoke this role. Extra precautions should be taken to secure + * accounts that have been granted it. + */ +abstract contract AccessControl is Context, IAccessControl, ERC165 { + struct RoleData { + mapping(address => bool) members; + bytes32 adminRole; + } + + mapping(bytes32 => RoleData) private _roles; + + bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; + + /** + * @dev Modifier that checks that an account has a specific role. Reverts + * with a standardized message including the required role. + * + * The format of the revert reason is given by the following regular expression: + * + * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ + * + * _Available since v4.1._ + */ + modifier onlyRole(bytes32 role) { + _checkRole(role, _msgSender()); + _; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Returns `true` if `account` has been granted `role`. + */ + function hasRole(bytes32 role, address account) public view override returns (bool) { + return _roles[role].members[account]; + } + + /** + * @dev Revert with a standard message if `account` is missing `role`. + * + * The format of the revert reason is given by the following regular expression: + * + * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ + */ + function _checkRole(bytes32 role, address account) internal view { + if (!hasRole(role, account)) { + revert( + string( + abi.encodePacked( + "AccessControl: account ", + Strings.toHexString(uint160(account), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ) + ); + } + } + + /** + * @dev Returns the admin role that controls `role`. See {grantRole} and + * {revokeRole}. + * + * To change a role's admin, use {_setRoleAdmin}. + */ + function getRoleAdmin(bytes32 role) public view override returns (bytes32) { + return _roles[role].adminRole; + } + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { + _grantRole(role, account); + } + + /** + * @dev Revokes `role` from `account`. + * + * If `account` had been granted `role`, emits a {RoleRevoked} event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { + _revokeRole(role, account); + } + + /** + * @dev Revokes `role` from the calling account. + * + * Roles are often managed via {grantRole} and {revokeRole}: this function's + * purpose is to provide a mechanism for accounts to lose their privileges + * if they are compromised (such as when a trusted device is misplaced). + * + * If the calling account had been revoked `role`, emits a {RoleRevoked} + * event. + * + * Requirements: + * + * - the caller must be `account`. + */ + function renounceRole(bytes32 role, address account) public virtual override { + require(account == _msgSender(), "AccessControl: can only renounce roles for self"); + + _revokeRole(role, account); + } + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. Note that unlike {grantRole}, this function doesn't perform any + * checks on the calling account. + * + * [WARNING] + * ==== + * This function should only be called from the constructor when setting + * up the initial roles for the system. + * + * Using this function in any other way is effectively circumventing the admin + * system imposed by {AccessControl}. + * ==== + * + * NOTE: This function is deprecated in favor of {_grantRole}. + */ + function _setupRole(bytes32 role, address account) internal virtual { + _grantRole(role, account); + } + + /** + * @dev Sets `adminRole` as ``role``'s admin role. + * + * Emits a {RoleAdminChanged} event. + */ + function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { + bytes32 previousAdminRole = getRoleAdmin(role); + _roles[role].adminRole = adminRole; + emit RoleAdminChanged(role, previousAdminRole, adminRole); + } + + /** + * @dev Grants `role` to `account`. + * + * Internal function without access restriction. + */ + function _grantRole(bytes32 role, address account) internal virtual { + if (!hasRole(role, account)) { + _roles[role].members[account] = true; + emit RoleGranted(role, account, _msgSender()); + } + } + + /** + * @dev Revokes `role` from `account`. + * + * Internal function without access restriction. + */ + function _revokeRole(bytes32 role, address account) internal virtual { + if (hasRole(role, account)) { + _roles[role].members[account] = false; + emit RoleRevoked(role, account, _msgSender()); + } + } +} diff --git a/certora/munged/access/AccessControlEnumerable.sol b/certora/munged/access/AccessControlEnumerable.sol new file mode 100644 index 000000000..655195d9a --- /dev/null +++ b/certora/munged/access/AccessControlEnumerable.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (access/AccessControlEnumerable.sol) + +pragma solidity ^0.8.0; + +import "./IAccessControlEnumerable.sol"; +import "./AccessControl.sol"; +import "../utils/structs/EnumerableSet.sol"; + +/** + * @dev Extension of {AccessControl} that allows enumerating the members of each role. + */ +abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { + using EnumerableSet for EnumerableSet.AddressSet; + + mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Returns one of the accounts that have `role`. `index` must be a + * value between 0 and {getRoleMemberCount}, non-inclusive. + * + * Role bearers are not sorted in any particular way, and their ordering may + * change at any point. + * + * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure + * you perform all queries on the same block. See the following + * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] + * for more information. + */ + function getRoleMember(bytes32 role, uint256 index) public view override returns (address) { + return _roleMembers[role].at(index); + } + + /** + * @dev Returns the number of accounts that have `role`. Can be used + * together with {getRoleMember} to enumerate all bearers of a role. + */ + function getRoleMemberCount(bytes32 role) public view override returns (uint256) { + return _roleMembers[role].length(); + } + + /** + * @dev Overload {_grantRole} to track enumerable memberships + */ + function _grantRole(bytes32 role, address account) internal virtual override { + super._grantRole(role, account); + _roleMembers[role].add(account); + } + + /** + * @dev Overload {_revokeRole} to track enumerable memberships + */ + function _revokeRole(bytes32 role, address account) internal virtual override { + super._revokeRole(role, account); + _roleMembers[role].remove(account); + } +} diff --git a/certora/munged/access/IAccessControl.sol b/certora/munged/access/IAccessControl.sol new file mode 100644 index 000000000..ac606aabc --- /dev/null +++ b/certora/munged/access/IAccessControl.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (access/IAccessControl.sol) + +pragma solidity ^0.8.0; + +/** + * @dev External interface of AccessControl declared to support ERC165 detection. + */ +interface IAccessControl { + /** + * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` + * + * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite + * {RoleAdminChanged} not being emitted signaling this. + * + * _Available since v3.1._ + */ + event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); + + /** + * @dev Emitted when `account` is granted `role`. + * + * `sender` is the account that originated the contract call, an admin role + * bearer except when using {AccessControl-_setupRole}. + */ + event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + + /** + * @dev Emitted when `account` is revoked `role`. + * + * `sender` is the account that originated the contract call: + * - if using `revokeRole`, it is the admin role bearer + * - if using `renounceRole`, it is the role bearer (i.e. `account`) + */ + event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); + + /** + * @dev Returns `true` if `account` has been granted `role`. + */ + function hasRole(bytes32 role, address account) external view returns (bool); + + /** + * @dev Returns the admin role that controls `role`. See {grantRole} and + * {revokeRole}. + * + * To change a role's admin, use {AccessControl-_setRoleAdmin}. + */ + function getRoleAdmin(bytes32 role) external view returns (bytes32); + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function grantRole(bytes32 role, address account) external; + + /** + * @dev Revokes `role` from `account`. + * + * If `account` had been granted `role`, emits a {RoleRevoked} event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function revokeRole(bytes32 role, address account) external; + + /** + * @dev Revokes `role` from the calling account. + * + * Roles are often managed via {grantRole} and {revokeRole}: this function's + * purpose is to provide a mechanism for accounts to lose their privileges + * if they are compromised (such as when a trusted device is misplaced). + * + * If the calling account had been granted `role`, emits a {RoleRevoked} + * event. + * + * Requirements: + * + * - the caller must be `account`. + */ + function renounceRole(bytes32 role, address account) external; +} diff --git a/certora/munged/access/IAccessControlEnumerable.sol b/certora/munged/access/IAccessControlEnumerable.sol new file mode 100644 index 000000000..3af4d10af --- /dev/null +++ b/certora/munged/access/IAccessControlEnumerable.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (access/IAccessControlEnumerable.sol) + +pragma solidity ^0.8.0; + +import "./IAccessControl.sol"; + +/** + * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. + */ +interface IAccessControlEnumerable is IAccessControl { + /** + * @dev Returns one of the accounts that have `role`. `index` must be a + * value between 0 and {getRoleMemberCount}, non-inclusive. + * + * Role bearers are not sorted in any particular way, and their ordering may + * change at any point. + * + * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure + * you perform all queries on the same block. See the following + * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] + * for more information. + */ + function getRoleMember(bytes32 role, uint256 index) external view returns (address); + + /** + * @dev Returns the number of accounts that have `role`. Can be used + * together with {getRoleMember} to enumerate all bearers of a role. + */ + function getRoleMemberCount(bytes32 role) external view returns (uint256); +} diff --git a/certora/munged/access/Ownable.sol b/certora/munged/access/Ownable.sol new file mode 100644 index 000000000..2ea238caa --- /dev/null +++ b/certora/munged/access/Ownable.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (access/Ownable.sol) + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * By default, the owner account will be the one that deploys the contract. This + * can later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract Ownable is Context { + address private _owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor() { + _transferOwnership(_msgSender()); + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + return _owner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(owner() == _msgSender(), "Ownable: caller is not the owner"); + _; + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions anymore. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby removing any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _transferOwnership(address(0)); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual { + address oldOwner = _owner; + _owner = newOwner; + emit OwnershipTransferred(oldOwner, newOwner); + } +} diff --git a/certora/munged/access/README.adoc b/certora/munged/access/README.adoc new file mode 100644 index 000000000..2e84c09ad --- /dev/null +++ b/certora/munged/access/README.adoc @@ -0,0 +1,21 @@ += Access Control + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/access + +This directory provides ways to restrict who can access the functions of a contract or when they can do it. + +- {AccessControl} provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts. +- {Ownable} is a simpler mechanism with a single owner "role" that can be assigned to a single account. This simpler mechanism can be useful for quick tests but projects with production concerns are likely to outgrow it. + +== Authorization + +{{Ownable}} + +{{IAccessControl}} + +{{AccessControl}} + +{{IAccessControlEnumerable}} + +{{AccessControlEnumerable}} diff --git a/certora/munged/finance/PaymentSplitter.sol b/certora/munged/finance/PaymentSplitter.sol new file mode 100644 index 000000000..df9345d77 --- /dev/null +++ b/certora/munged/finance/PaymentSplitter.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (finance/PaymentSplitter.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/utils/SafeERC20.sol"; +import "../utils/Address.sol"; +import "../utils/Context.sol"; + +/** + * @title PaymentSplitter + * @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware + * that the Ether will be split in this way, since it is handled transparently by the contract. + * + * The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each + * account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim + * an amount proportional to the percentage of total shares they were assigned. + * + * `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the + * accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release} + * function. + * + * NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and + * tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you + * to run tests before sending real value to this contract. + */ +contract PaymentSplitter is Context { + event PayeeAdded(address account, uint256 shares); + event PaymentReleased(address to, uint256 amount); + event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount); + event PaymentReceived(address from, uint256 amount); + + uint256 private _totalShares; + uint256 private _totalReleased; + + mapping(address => uint256) private _shares; + mapping(address => uint256) private _released; + address[] private _payees; + + mapping(IERC20 => uint256) private _erc20TotalReleased; + mapping(IERC20 => mapping(address => uint256)) private _erc20Released; + + /** + * @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at + * the matching position in the `shares` array. + * + * All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no + * duplicates in `payees`. + */ + constructor(address[] memory payees, uint256[] memory shares_) payable { + require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch"); + require(payees.length > 0, "PaymentSplitter: no payees"); + + for (uint256 i = 0; i < payees.length; i++) { + _addPayee(payees[i], shares_[i]); + } + } + + /** + * @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully + * reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the + * reliability of the events, and not the actual splitting of Ether. + * + * To learn more about this see the Solidity documentation for + * https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback + * functions]. + */ + receive() external payable virtual { + emit PaymentReceived(_msgSender(), msg.value); + } + + /** + * @dev Getter for the total shares held by payees. + */ + function totalShares() public view returns (uint256) { + return _totalShares; + } + + /** + * @dev Getter for the total amount of Ether already released. + */ + function totalReleased() public view returns (uint256) { + return _totalReleased; + } + + /** + * @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20 + * contract. + */ + function totalReleased(IERC20 token) public view returns (uint256) { + return _erc20TotalReleased[token]; + } + + /** + * @dev Getter for the amount of shares held by an account. + */ + function shares(address account) public view returns (uint256) { + return _shares[account]; + } + + /** + * @dev Getter for the amount of Ether already released to a payee. + */ + function released(address account) public view returns (uint256) { + return _released[account]; + } + + /** + * @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an + * IERC20 contract. + */ + function released(IERC20 token, address account) public view returns (uint256) { + return _erc20Released[token][account]; + } + + /** + * @dev Getter for the address of the payee number `index`. + */ + function payee(uint256 index) public view returns (address) { + return _payees[index]; + } + + /** + * @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the + * total shares and their previous withdrawals. + */ + function release(address payable account) public virtual { + require(_shares[account] > 0, "PaymentSplitter: account has no shares"); + + uint256 totalReceived = address(this).balance + totalReleased(); + uint256 payment = _pendingPayment(account, totalReceived, released(account)); + + require(payment != 0, "PaymentSplitter: account is not due payment"); + + _released[account] += payment; + _totalReleased += payment; + + Address.sendValue(account, payment); + emit PaymentReleased(account, payment); + } + + /** + * @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their + * percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20 + * contract. + */ + function release(IERC20 token, address account) public virtual { + require(_shares[account] > 0, "PaymentSplitter: account has no shares"); + + uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token); + uint256 payment = _pendingPayment(account, totalReceived, released(token, account)); + + require(payment != 0, "PaymentSplitter: account is not due payment"); + + _erc20Released[token][account] += payment; + _erc20TotalReleased[token] += payment; + + SafeERC20.safeTransfer(token, account, payment); + emit ERC20PaymentReleased(token, account, payment); + } + + /** + * @dev internal logic for computing the pending payment of an `account` given the token historical balances and + * already released amounts. + */ + function _pendingPayment( + address account, + uint256 totalReceived, + uint256 alreadyReleased + ) private view returns (uint256) { + return (totalReceived * _shares[account]) / _totalShares - alreadyReleased; + } + + /** + * @dev Add a new payee to the contract. + * @param account The address of the payee to add. + * @param shares_ The number of shares owned by the payee. + */ + function _addPayee(address account, uint256 shares_) private { + require(account != address(0), "PaymentSplitter: account is the zero address"); + require(shares_ > 0, "PaymentSplitter: shares are 0"); + require(_shares[account] == 0, "PaymentSplitter: account already has shares"); + + _payees.push(account); + _shares[account] = shares_; + _totalShares = _totalShares + shares_; + emit PayeeAdded(account, shares_); + } +} diff --git a/certora/munged/finance/README.adoc b/certora/munged/finance/README.adoc new file mode 100644 index 000000000..b64af3125 --- /dev/null +++ b/certora/munged/finance/README.adoc @@ -0,0 +1,20 @@ += Finance + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/finance + +This directory includes primitives for financial systems: + +- {PaymentSplitter} allows to split Ether and ERC20 payments among a group of accounts. The sender does not need to be + aware that the assets will be split in this way, since it is handled transparently by the contract. The split can be + in equal parts or in any other arbitrary proportion. + +- {VestingWallet} handles the vesting of Ether and ERC20 tokens for a given beneficiary. Custody of multiple tokens can + be given to this contract, which will release the token to the beneficiary following a given, customizable, vesting + schedule. + +== Contracts + +{{PaymentSplitter}} + +{{VestingWallet}} diff --git a/certora/munged/finance/VestingWallet.sol b/certora/munged/finance/VestingWallet.sol new file mode 100644 index 000000000..f0513bdc6 --- /dev/null +++ b/certora/munged/finance/VestingWallet.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (finance/VestingWallet.sol) +pragma solidity ^0.8.0; + +import "../token/ERC20/utils/SafeERC20.sol"; +import "../utils/Address.sol"; +import "../utils/Context.sol"; +import "../utils/math/Math.sol"; + +/** + * @title VestingWallet + * @dev This contract handles the vesting of Eth and ERC20 tokens for a given beneficiary. Custody of multiple tokens + * can be given to this contract, which will release the token to the beneficiary following a given vesting schedule. + * The vesting schedule is customizable through the {vestedAmount} function. + * + * Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning. + * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly) + * be immediately releasable. + */ +contract VestingWallet is Context { + event EtherReleased(uint256 amount); + event ERC20Released(address token, uint256 amount); + + uint256 private _released; + mapping(address => uint256) private _erc20Released; + address private immutable _beneficiary; + uint64 private immutable _start; + uint64 private immutable _duration; + + /** + * @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet. + */ + constructor( + address beneficiaryAddress, + uint64 startTimestamp, + uint64 durationSeconds + ) { + require(beneficiaryAddress != address(0), "VestingWallet: beneficiary is zero address"); + _beneficiary = beneficiaryAddress; + _start = startTimestamp; + _duration = durationSeconds; + } + + /** + * @dev The contract should be able to receive Eth. + */ + receive() external payable virtual {} + + /** + * @dev Getter for the beneficiary address. + */ + function beneficiary() public view virtual returns (address) { + return _beneficiary; + } + + /** + * @dev Getter for the start timestamp. + */ + function start() public view virtual returns (uint256) { + return _start; + } + + /** + * @dev Getter for the vesting duration. + */ + function duration() public view virtual returns (uint256) { + return _duration; + } + + /** + * @dev Amount of eth already released + */ + function released() public view virtual returns (uint256) { + return _released; + } + + /** + * @dev Amount of token already released + */ + function released(address token) public view virtual returns (uint256) { + return _erc20Released[token]; + } + + /** + * @dev Release the native token (ether) that have already vested. + * + * Emits a {TokensReleased} event. + */ + function release() public virtual { + uint256 releasable = vestedAmount(uint64(block.timestamp)) - released(); + _released += releasable; + emit EtherReleased(releasable); + Address.sendValue(payable(beneficiary()), releasable); + } + + /** + * @dev Release the tokens that have already vested. + * + * Emits a {TokensReleased} event. + */ + function release(address token) public virtual { + uint256 releasable = vestedAmount(token, uint64(block.timestamp)) - released(token); + _erc20Released[token] += releasable; + emit ERC20Released(token, releasable); + SafeERC20.safeTransfer(IERC20(token), beneficiary(), releasable); + } + + /** + * @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve. + */ + function vestedAmount(uint64 timestamp) public view virtual returns (uint256) { + return _vestingSchedule(address(this).balance + released(), timestamp); + } + + /** + * @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve. + */ + function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) { + return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp); + } + + /** + * @dev Virtual implementation of the vesting formula. This returns the amout vested, as a function of time, for + * an asset given its total historical allocation. + */ + function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) { + if (timestamp < start()) { + return 0; + } else if (timestamp > start() + duration()) { + return totalAllocation; + } else { + return (totalAllocation * (timestamp - start())) / duration(); + } + } +} diff --git a/certora/munged/governance/Governor.sol b/certora/munged/governance/Governor.sol new file mode 100644 index 000000000..e8d369452 --- /dev/null +++ b/certora/munged/governance/Governor.sol @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/Governor.sol) + +pragma solidity ^0.8.0; + +import "../utils/cryptography/ECDSA.sol"; +import "../utils/cryptography/draft-EIP712.sol"; +import "../utils/introspection/ERC165.sol"; +import "../utils/math/SafeCast.sol"; +import "../utils/Address.sol"; +import "../utils/Context.sol"; +import "../utils/Timers.sol"; +import "./IGovernor.sol"; + +/** + * @dev Core of the governance system, designed to be extended though various modules. + * + * This contract is abstract and requires several function to be implemented in various modules: + * + * - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote} + * - A voting module must implement {getVotes} + * - Additionanly, the {votingPeriod} must also be implemented + * + * _Available since v4.3._ + */ +abstract contract Governor is Context, ERC165, EIP712, IGovernor { + using SafeCast for uint256; + using Timers for Timers.BlockNumber; + + bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); + + struct ProposalCore { + Timers.BlockNumber voteStart; + Timers.BlockNumber voteEnd; + bool executed; + bool canceled; + } + + string private _name; + + mapping(uint256 => ProposalCore) public _proposals; + + /** + * @dev Restrict access to governor executing address. Some module might override the _executor function to make + * sure this modifier is consistant with the execution model. + */ + modifier onlyGovernance() { + require(_msgSender() == _executor(), "Governor: onlyGovernance"); + _; + } + + /** + * @dev Sets the value for {name} and {version} + */ + constructor(string memory name_) EIP712(name_, version()) { + _name = name_; + } + + /** + * @dev Function to receive ETH that will be handled by the governor (disabled if executor is a third party contract) + */ + receive() external payable virtual { + require(_executor() == address(this)); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { + return interfaceId == type(IGovernor).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev See {IGovernor-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IGovernor-version}. + */ + function version() public view virtual override returns (string memory) { + return "1"; + } + + /** + * @dev See {IGovernor-hashProposal}. + * + * The proposal id is produced by hashing the RLC encoded `targets` array, the `values` array, the `calldatas` array + * and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id + * can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in + * advance, before the proposal is submitted. + * + * Note that the chainId and the governor address are not part of the proposal id computation. Consequently, the + * same proposal (with same operation and same description) will have the same id if submitted on multiple governors + * accross multiple networks. This also means that in order to execute the same operation twice (on the same + * governor) the proposer will have to change the description in order to avoid proposal id conflicts. + */ + function hashProposal( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public pure virtual override returns (uint256) { + return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash))); + } + + /** + * @dev See {IGovernor-state}. + */ + function state(uint256 proposalId) public view virtual override returns (ProposalState) { + ProposalCore memory proposal = _proposals[proposalId]; + + if (proposal.executed) { + return ProposalState.Executed; + } else if (proposal.canceled) { + return ProposalState.Canceled; + } else if (proposal.voteStart.getDeadline() >= block.number) { + return ProposalState.Pending; + } else if (proposal.voteEnd.getDeadline() >= block.number) { + return ProposalState.Active; + } else if (proposal.voteEnd.isExpired()) { + return + _quorumReached(proposalId) && _voteSucceeded(proposalId) + ? ProposalState.Succeeded + : ProposalState.Defeated; + } else { + revert("Governor: unknown proposal id"); + } + } + + /** + * @dev See {IGovernor-proposalSnapshot}. + */ + function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) { + return _proposals[proposalId].voteStart.getDeadline(); + } + + /** + * @dev See {IGovernor-proposalDeadline}. + */ + function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) { + return _proposals[proposalId].voteEnd.getDeadline(); + } + + /** + * @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_. + */ + function proposalThreshold() public view virtual returns (uint256) { + return 0; + } + + /** + * @dev Amount of votes already cast passes the threshold limit. + */ + function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal + + /** + * @dev Is the proposal successful or not. + */ + function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal + + /** + * @dev Register a vote with a given support and voting weight. + * + * Note: Support is generic and can represent various things depending on the voting system used. + */ + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal virtual; + + /** + * @dev See {IGovernor-propose}. + */ + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + require( + getVotes(msg.sender, block.number - 1) >= proposalThreshold(), + "GovernorCompatibilityBravo: proposer votes below proposal threshold" + ); + + uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description))); + + require(targets.length == values.length, "Governor: invalid proposal length"); + require(targets.length == calldatas.length, "Governor: invalid proposal length"); + require(targets.length > 0, "Governor: empty proposal"); + + ProposalCore storage proposal = _proposals[proposalId]; + require(proposal.voteStart.isUnset(), "Governor: proposal already exists"); + + uint64 snapshot = block.number.toUint64() + votingDelay().toUint64(); + uint64 deadline = snapshot + votingPeriod().toUint64(); + + proposal.voteStart.setDeadline(snapshot); + proposal.voteEnd.setDeadline(deadline); + + emit ProposalCreated( + proposalId, + _msgSender(), + targets, + values, + new string[](targets.length), + calldatas, + snapshot, + deadline, + description + ); + + return proposalId; + } + + /** + * @dev See {IGovernor-execute}. + */ + function execute( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public payable virtual override returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + + ProposalState status = state(proposalId); + require( + status == ProposalState.Succeeded || status == ProposalState.Queued, + "Governor: proposal not successful" + ); + _proposals[proposalId].executed = true; + + emit ProposalExecuted(proposalId); + + _execute(proposalId, targets, values, calldatas, descriptionHash); + + return proposalId; + } + + /** + * @dev Internal execution mechanism. Can be overriden to implement different execution mechanism + */ + function _execute( + uint256, /* proposalId */ + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 /*descriptionHash*/ + ) internal virtual { + string memory errorMessage = "Governor: call reverted without message"; + for (uint256 i = 0; i < targets.length; ++i) { + (bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]); + Address.verifyCallResult(success, returndata, errorMessage); + } + } + + /** + * @dev Internal cancel mechanism: locks up the proposal timer, preventing it from being re-submitted. Marks it as + * canceled to allow distinguishing it from executed proposals. + * + * Emits a {IGovernor-ProposalCanceled} event. + */ + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + ProposalState status = state(proposalId); + + require( + status != ProposalState.Canceled && status != ProposalState.Expired && status != ProposalState.Executed, + "Governor: proposal not active" + ); + _proposals[proposalId].canceled = true; + + emit ProposalCanceled(proposalId); + + return proposalId; + } + + /** + * @dev See {IGovernor-castVote}. + */ + function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { + address voter = _msgSender(); + return _castVote(proposalId, voter, support, ""); + } + + /** + * @dev See {IGovernor-castVoteWithReason}. + */ + function castVoteWithReason( + uint256 proposalId, + uint8 support, + string calldata reason + ) public virtual override returns (uint256) { + address voter = _msgSender(); + return _castVote(proposalId, voter, support, reason); + } + + /** + * @dev See {IGovernor-castVoteBySig}. + */ + function castVoteBySig( + uint256 proposalId, + uint8 support, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override returns (uint256) { + address voter = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))), + v, + r, + s + ); // mention that we assume that hashing works correctly + return _castVote(proposalId, voter, support, ""); + } + + /** + * @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve + * voting weight using {IGovernor-getVotes} and call the {_countVote} internal function. + * + * Emits a {IGovernor-VoteCast} event. + */ + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason + ) internal virtual returns (uint256) { + ProposalCore storage proposal = _proposals[proposalId]; + require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active"); + + uint256 weight = getVotes(account, proposal.voteStart.getDeadline()); + _countVote(proposalId, account, support, weight); + + emit VoteCast(account, proposalId, support, weight, reason); + + return weight; + } + + /** + * @dev Address through which the governor executes action. Will be overloaded by module that execute actions + * through another contract such as a timelock. + */ + function _executor() internal view virtual returns (address) { + return address(this); + } +} diff --git a/certora/munged/governance/IGovernor.sol b/certora/munged/governance/IGovernor.sol new file mode 100644 index 000000000..b30a2aa0e --- /dev/null +++ b/certora/munged/governance/IGovernor.sol @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/IGovernor.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165.sol"; + +/** + * @dev Interface of the {Governor} core. + * + * _Available since v4.3._ + */ +abstract contract IGovernor is IERC165 { + enum ProposalState { + Pending, + Active, + Canceled, + Defeated, + Succeeded, + Queued, + Expired, + Executed + } + + /** + * @dev Emitted when a proposal is created. + */ + event ProposalCreated( + uint256 proposalId, + address proposer, + address[] targets, + uint256[] values, + string[] signatures, + bytes[] calldatas, + uint256 startBlock, + uint256 endBlock, + string description + ); + + /** + * @dev Emitted when a proposal is canceled. + */ + event ProposalCanceled(uint256 proposalId); + + /** + * @dev Emitted when a proposal is executed. + */ + event ProposalExecuted(uint256 proposalId); + + /** + * @dev Emitted when a vote is cast. + * + * Note: `support` values should be seen as buckets. There interpretation depends on the voting module used. + */ + event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason); + + /** + * @notice module:core + * @dev Name of the governor instance (used in building the ERC712 domain separator). + */ + function name() public view virtual returns (string memory); + + /** + * @notice module:core + * @dev Version of the governor instance (used in building the ERC712 domain separator). Default: "1" + */ + function version() public view virtual returns (string memory); + + /** + * @notice module:voting + * @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to + * be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of + * key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`. + * + * There are 2 standard keys: `support` and `quorum`. + * + * - `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`. + * - `quorum=bravo` means that only For votes are counted towards quorum. + * - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum. + * + * NOTE: The string can be decoded by the standard + * https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`] + * JavaScript class. + */ + // solhint-disable-next-line func-name-mixedcase + function COUNTING_MODE() public pure virtual returns (string memory); + + /** + * @notice module:core + * @dev Hashing function used to (re)build the proposal id from the proposal details.. + */ + function hashProposal( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata calldatas, + bytes32 descriptionHash + ) public pure virtual returns (uint256); + + /** + * @notice module:core + * @dev Current state of a proposal, following Compound's convention + */ + function state(uint256 proposalId) public view virtual returns (ProposalState); + + /** + * @notice module:core + * @dev Block number used to retrieve user's votes and quorum. As per Compound's Comp and OpenZeppelin's + * ERC20Votes, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the + * beginning of the following block. + */ + function proposalSnapshot(uint256 proposalId) public view virtual returns (uint256); + + /** + * @notice module:core + * @dev Block number at which votes close. Votes close at the end of this block, so it is possible to cast a vote + * during this block. + */ + function proposalDeadline(uint256 proposalId) public view virtual returns (uint256); + + /** + * @notice module:user-config + * @dev Delay, in number of block, between the proposal is created and the vote starts. This can be increassed to + * leave time for users to buy voting power, of delegate it, before the voting of a proposal starts. + */ + function votingDelay() public view virtual returns (uint256); + + /** + * @notice module:user-config + * @dev Delay, in number of blocks, between the vote start and vote ends. + * + * NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting + * duration compared to the voting delay. + */ + function votingPeriod() public view virtual returns (uint256); + + /** + * @notice module:user-config + * @dev Minimum number of cast voted required for a proposal to be successful. + * + * Note: The `blockNumber` parameter corresponds to the snaphot used for counting vote. This allows to scale the + * quroum depending on values such as the totalSupply of a token at this block (see {ERC20Votes}). + */ + function quorum(uint256 blockNumber) public view virtual returns (uint256); + + /** + * @notice module:reputation + * @dev Voting power of an `account` at a specific `blockNumber`. + * + * Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or + * multiple), {ERC20Votes} tokens. + */ + function getVotes(address account, uint256 blockNumber) public view virtual returns (uint256); + + /** + * @notice module:voting + * @dev Returns weither `account` has cast a vote on `proposalId`. + */ + function hasVoted(uint256 proposalId, address account) public view virtual returns (bool); + + /** + * @dev Create a new proposal. Vote start {IGovernor-votingDelay} blocks after the proposal is created and ends + * {IGovernor-votingPeriod} blocks after the voting starts. + * + * Emits a {ProposalCreated} event. + */ + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual returns (uint256 proposalId); + + /** + * @dev Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the + * deadline to be reached. + * + * Emits a {ProposalExecuted} event. + * + * Note: some module can modify the requirements for execution, for example by adding an additional timelock. + */ + function execute( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public payable virtual returns (uint256 proposalId); + + /** + * @dev Cast a vote + * + * Emits a {VoteCast} event. + */ + function castVote(uint256 proposalId, uint8 support) public virtual returns (uint256 balance); + + /** + * @dev Cast a with a reason + * + * Emits a {VoteCast} event. + */ + function castVoteWithReason( + uint256 proposalId, + uint8 support, + string calldata reason + ) public virtual returns (uint256 balance); + + /** + * @dev Cast a vote using the user cryptographic signature. + * + * Emits a {VoteCast} event. + */ + function castVoteBySig( + uint256 proposalId, + uint8 support, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual returns (uint256 balance); +} diff --git a/certora/munged/governance/README.adoc b/certora/munged/governance/README.adoc new file mode 100644 index 000000000..d198c9f93 --- /dev/null +++ b/certora/munged/governance/README.adoc @@ -0,0 +1,168 @@ += Governance + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance + +This directory includes primitives for on-chain governance. + +== Governor + +This modular system of Governor contracts allows the deployment on-chain voting protocols similar to https://compound.finance/docs/governance[Compound's Governor Alpha & Bravo] and beyond, through the ability to easily customize multiple aspects of the protocol. + +[TIP] +==== +For a guided experience, set up your Governor contract using https://wizard.openzeppelin.com/#governor[Contracts Wizard]. + +For a written walkthrough, check out our guide on xref:ROOT:governance.adoc[How to set up on-chain governance]. +==== + +* {Governor}: The core contract that contains all the logic and primitives. It is abstract and requires choosing one of each of the modules below, or custom ones. + +Votes modules determine the source of voting power, and sometimes quorum number. + +* {GovernorVotes}: Extracts voting weight from an {ERC20Votes} token. + +* {GovernorVotesComp}: Extracts voting weight from a COMP-like or {ERC20VotesComp} token. + +* {GovernorVotesQuorumFraction}: Combines with `GovernorVotes` to set the quorum as a fraction of the total token supply. + +Counting modules determine valid voting options. + +* {GovernorCountingSimple}: Simple voting mechanism with 3 voting options: Against, For and Abstain. + +Timelock extensions add a delay for governance decisions to be executed. The workflow is extended to require a `queue` step before execution. With these modules, proposals are executed by the external timelock contract, thus it is the timelock that has to hold the assets that are being governed. + +* {GovernorTimelockControl}: Connects with an instance of {TimelockController}. Allows multiple proposers and executors, in addition to the Governor itself. + +* {GovernorTimelockCompound}: Connects with an instance of Compound's https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol[`Timelock`] contract. + +Other extensions can customize the behavior or interface in multiple ways. + +* {GovernorCompatibilityBravo}: Extends the interface to be fully `GovernorBravo`-compatible. Note that events are compatible regardless of whether this extension is included or not. + +* {GovernorSettings}: Manages some of the settings (voting delay, voting period duration, and proposal threshold) in a way that can be updated through a governance proposal, without requiering an upgrade. + +In addition to modules and extensions, the core contract requires a few virtual functions to be implemented to your particular specifications: + +* <>: Delay (in number of blocks) since the proposal is submitted until voting power is fixed and voting starts. This can be used to enforce a delay after a proposal is published for users to buy tokens, or delegate their votes. +* <>: Delay (in number of blocks) since the proposal starts until voting ends. +* <>: Quorum required for a proposal to be successful. This function includes a `blockNumber` argument so the quorum can adapt through time, for example, to follow a token's `totalSupply`. + +NOTE: Functions of the `Governor` contract do not include access control. If you want to restrict access, you should add these checks by overloading the particular functions. Among these, {Governor-_cancel} is internal by default, and you will have to expose it (which the right access control mechanism) yourself if this function is needed. + +=== Core + +{{IGovernor}} + +{{Governor}} + +=== Modules + +{{GovernorCountingSimple}} + +{{GovernorVotes}} + +{{GovernorVotesQuorumFraction}} + +{{GovernorVotesComp}} + +=== Extensions + +{{GovernorTimelockControl}} + +{{GovernorTimelockCompound}} + +{{GovernorSettings}} + +{{GovernorCompatibilityBravo}} + +=== Deprecated + +{{GovernorProposalThreshold}} + +== Timelock + +In a governance system, the {TimelockController} contract is in carge of introducing a delay between a proposal and its execution. It can be used with or without a {Governor}. + +{{TimelockController}} + +[[timelock-terminology]] +==== Terminology + +* *Operation:* A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution (see xref:access-control.adoc#operation_lifecycle[operation lifecycle]). If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content. +* *Operation status:* +** *Unset:* An operation that is not part of the timelock mechanism. +** *Pending:* An operation that has been scheduled, before the timer expires. +** *Ready:* An operation that has been scheduled, after the timer expires. +** *Done:* An operation that has been executed. +* *Predecessor*: An (optional) dependency between operations. An operation can depend on another operation (its predecessor), forcing the execution order of these two operations. +* *Role*: +** *Admin:* An address (smart contract or EOA) that is in charge of granting the roles of Proposer and Executor. +** *Proposer:* An address (smart contract or EOA) that is in charge of scheduling (and cancelling) operations. +** *Executor:* An address (smart contract or EOA) that is in charge of executing operations once the timelock has expired. This role can be given to the zero address to allow anyone to execute operations. + +[[timelock-operation]] +==== Operation structure + +Operation executed by the xref:api:governance.adoc#TimelockController[`TimelockController`] can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations. + +Both operations contain: + +* *Target*, the address of the smart contract that the timelock should operate on. +* *Value*, in wei, that should be sent with the transaction. Most of the time this will be 0. Ether can be deposited before-end or passed along when executing the transaction. +* *Data*, containing the encoded function selector and parameters of the call. This can be produced using a number of tools. For example, a maintenance operation granting role `ROLE` to `ACCOUNT` can be encode using web3js as follows: + +```javascript +const data = timelock.contract.methods.grantRole(ROLE, ACCOUNT).encodeABI() +``` + +* *Predecessor*, that specifies a dependency between operations. This dependency is optional. Use `bytes32(0)` if the operation does not have any dependency. +* *Salt*, used to disambiguate two otherwise identical operations. This can be any random value. + +In the case of batched operations, `target`, `value` and `data` are specified as arrays, which must be of the same length. + +[[timelock-operation-lifecycle]] +==== Operation lifecycle + +Timelocked operations are identified by a unique id (their hash) and follow a specific lifecycle: + +`Unset` -> `Pending` -> `Pending` + `Ready` -> `Done` + +* By calling xref:api:governance.adoc#TimelockController-schedule-address-uint256-bytes-bytes32-bytes32-uint256-[`schedule`] (or xref:api:governance.adoc#TimelockController-scheduleBatch-address---uint256---bytes---bytes32-bytes32-uint256-[`scheduleBatch`]), a proposer moves the operation from the `Unset` to the `Pending` state. This starts a timer that must be longer than the minimum delay. The timer expires at a timestamp accessible through the xref:api:governance.adoc#TimelockController-getTimestamp-bytes32-[`getTimestamp`] method. +* Once the timer expires, the operation automatically gets the `Ready` state. At this point, it can be executed. +* By calling xref:api:governance.adoc#TimelockController-TimelockController-execute-address-uint256-bytes-bytes32-bytes32-[`execute`] (or xref:api:governance.adoc#TimelockController-executeBatch-address---uint256---bytes---bytes32-bytes32-[`executeBatch`]), an executor triggers the operation's underlying transactions and moves it to the `Done` state. If the operation has a predecessor, it has to be in the `Done` state for this transition to succeed. +* xref:api:governance.adoc#TimelockController-TimelockController-cancel-bytes32-[`cancel`] allows proposers to cancel any `Pending` operation. This resets the operation to the `Unset` state. It is thus possible for a proposer to re-schedule an operation that has been cancelled. In this case, the timer restarts when the operation is re-scheduled. + +Operations status can be queried using the functions: + +* xref:api:governance.adoc#TimelockController-isOperationPending-bytes32-[`isOperationPending(bytes32)`] +* xref:api:governance.adoc#TimelockController-isOperationReady-bytes32-[`isOperationReady(bytes32)`] +* xref:api:governance.adoc#TimelockController-isOperationDone-bytes32-[`isOperationDone(bytes32)`] + +[[timelock-roles]] +==== Roles + +[[timelock-admin]] +===== Admin + +The admins are in charge of managing proposers and executors. For the timelock to be self-governed, this role should only be given to the timelock itself. Upon deployment, both the timelock and the deployer have this role. After further configuration and testing, the deployer can renounce this role such that all further maintenance operations have to go through the timelock process. + +This role is identified by the *TIMELOCK_ADMIN_ROLE* value: `0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5` + +[[timelock-proposer]] +===== Proposer + +The proposers are in charge of scheduling (and cancelling) operations. This is a critical role, that should be given to governing entities. This could be an EOA, a multisig, or a DAO. + +WARNING: *Proposer fight:* Having multiple proposers, while providing redundancy in case one becomes unavailable, can be dangerous. As proposer have their say on all operations, they could cancel operations they disagree with, including operations to remove them for the proposers. + +This role is identified by the *PROPOSER_ROLE* value: `0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1` + +[[timelock-executor]] +===== Executor + +The executors are in charge of executing the operations scheduled by the proposers once the timelock expires. Logic dictates that multisig or DAO that are proposers should also be executors in order to guarantee operations that have been scheduled will eventually be executed. However, having additional executors can reduce the cost (the executing transaction does not require validation by the multisig or DAO that proposed it), while ensuring whoever is in charge of execution cannot trigger actions that have not been scheduled by the proposers. Alternatively, it is possible to allow _any_ address to execute a proposal once the timelock has expired by granting the executor role to the zero address. + +This role is identified by the *EXECUTOR_ROLE* value: `0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63` + +WARNING: A live contract without at least one proposer and one executor is locked. Make sure these roles are filled by reliable entities before the deployer renounces its administrative rights in favour of the timelock contract itself. See the {AccessControl} documentation to learn more about role management. diff --git a/certora/munged/governance/TimelockController.sol b/certora/munged/governance/TimelockController.sol new file mode 100644 index 000000000..b3b551dde --- /dev/null +++ b/certora/munged/governance/TimelockController.sol @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/TimelockController.sol) + +pragma solidity ^0.8.0; + +import "../access/AccessControl.sol"; + +/** + * @dev Contract module which acts as a timelocked controller. When set as the + * owner of an `Ownable` smart contract, it enforces a timelock on all + * `onlyOwner` maintenance operations. This gives time for users of the + * controlled contract to exit before a potentially dangerous maintenance + * operation is applied. + * + * By default, this contract is self administered, meaning administration tasks + * have to go through the timelock process. The proposer (resp executor) role + * is in charge of proposing (resp executing) operations. A common use case is + * to position this {TimelockController} as the owner of a smart contract, with + * a multisig or a DAO as the sole proposer. + * + * _Available since v3.3._ + */ +contract TimelockController is AccessControl { + bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); + bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); + bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); + uint256 internal constant _DONE_TIMESTAMP = uint256(1); + + mapping(bytes32 => uint256) private _timestamps; + uint256 private _minDelay; + + /** + * @dev Emitted when a call is scheduled as part of operation `id`. + */ + event CallScheduled( + bytes32 indexed id, + uint256 indexed index, + address target, + uint256 value, + bytes data, + bytes32 predecessor, + uint256 delay + ); + + /** + * @dev Emitted when a call is performed as part of operation `id`. + */ + event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data); + + /** + * @dev Emitted when operation `id` is cancelled. + */ + event Cancelled(bytes32 indexed id); + + /** + * @dev Emitted when the minimum delay for future operations is modified. + */ + event MinDelayChange(uint256 oldDuration, uint256 newDuration); + + /** + * @dev Initializes the contract with a given `minDelay`. + */ + constructor( + uint256 minDelay, + address[] memory proposers, + address[] memory executors + ) { + _setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE); + _setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE); + _setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE); + + // deployer + self administration + _setupRole(TIMELOCK_ADMIN_ROLE, _msgSender()); + _setupRole(TIMELOCK_ADMIN_ROLE, address(this)); + + // register proposers + for (uint256 i = 0; i < proposers.length; ++i) { + _setupRole(PROPOSER_ROLE, proposers[i]); + } + + // register executors + for (uint256 i = 0; i < executors.length; ++i) { + _setupRole(EXECUTOR_ROLE, executors[i]); + } + + _minDelay = minDelay; + emit MinDelayChange(0, minDelay); + } + + /** + * @dev Modifier to make a function callable only by a certain role. In + * addition to checking the sender's role, `address(0)` 's role is also + * considered. Granting a role to `address(0)` is equivalent to enabling + * this role for everyone. + */ + modifier onlyRoleOrOpenRole(bytes32 role) { + if (!hasRole(role, address(0))) { + _checkRole(role, _msgSender()); + } + _; + } + + /** + * @dev Contract might receive/hold ETH as part of the maintenance process. + */ + receive() external payable {} + + /** + * @dev Returns whether an id correspond to a registered operation. This + * includes both Pending, Ready and Done operations. + */ + function isOperation(bytes32 id) public view virtual returns (bool pending) { + return getTimestamp(id) > 0; + } + + /** + * @dev Returns whether an operation is pending or not. + */ + function isOperationPending(bytes32 id) public view virtual returns (bool pending) { + return getTimestamp(id) > _DONE_TIMESTAMP; + } + + /** + * @dev Returns whether an operation is ready or not. + */ + function isOperationReady(bytes32 id) public view virtual returns (bool ready) { + uint256 timestamp = getTimestamp(id); + return timestamp > _DONE_TIMESTAMP && timestamp <= block.timestamp; + } + + /** + * @dev Returns whether an operation is done or not. + */ + function isOperationDone(bytes32 id) public view virtual returns (bool done) { + return getTimestamp(id) == _DONE_TIMESTAMP; + } + + /** + * @dev Returns the timestamp at with an operation becomes ready (0 for + * unset operations, 1 for done operations). + */ + function getTimestamp(bytes32 id) public view virtual returns (uint256 timestamp) { + return _timestamps[id]; + } + + /** + * @dev Returns the minimum delay for an operation to become valid. + * + * This value can be changed by executing an operation that calls `updateDelay`. + */ + function getMinDelay() public view virtual returns (uint256 duration) { + return _minDelay; + } + + /** + * @dev Returns the identifier of an operation containing a single + * transaction. + */ + function hashOperation( + address target, + uint256 value, + bytes calldata data, + bytes32 predecessor, + bytes32 salt + ) public pure virtual returns (bytes32 hash) { + return keccak256(abi.encode(target, value, data, predecessor, salt)); + } + + /** + * @dev Returns the identifier of an operation containing a batch of + * transactions. + */ + function hashOperationBatch( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata datas, + bytes32 predecessor, + bytes32 salt + ) public pure virtual returns (bytes32 hash) { + return keccak256(abi.encode(targets, values, datas, predecessor, salt)); + } + + /** + * @dev Schedule an operation containing a single transaction. + * + * Emits a {CallScheduled} event. + * + * Requirements: + * + * - the caller must have the 'proposer' role. + */ + function schedule( + address target, + uint256 value, + bytes calldata data, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ) public virtual onlyRole(PROPOSER_ROLE) { + bytes32 id = hashOperation(target, value, data, predecessor, salt); + _schedule(id, delay); + emit CallScheduled(id, 0, target, value, data, predecessor, delay); + } + + /** + * @dev Schedule an operation containing a batch of transactions. + * + * Emits one {CallScheduled} event per transaction in the batch. + * + * Requirements: + * + * - the caller must have the 'proposer' role. + */ + function scheduleBatch( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata datas, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ) public virtual onlyRole(PROPOSER_ROLE) { + require(targets.length == values.length, "TimelockController: length mismatch"); + require(targets.length == datas.length, "TimelockController: length mismatch"); + + bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt); + _schedule(id, delay); + for (uint256 i = 0; i < targets.length; ++i) { + emit CallScheduled(id, i, targets[i], values[i], datas[i], predecessor, delay); + } + } + + /** + * @dev Schedule an operation that is to becomes valid after a given delay. + */ + function _schedule(bytes32 id, uint256 delay) private { + require(!isOperation(id), "TimelockController: operation already scheduled"); + require(delay >= getMinDelay(), "TimelockController: insufficient delay"); + _timestamps[id] = block.timestamp + delay; + } + + /** + * @dev Cancel an operation. + * + * Requirements: + * + * - the caller must have the 'proposer' role. + */ + function cancel(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) { + require(isOperationPending(id), "TimelockController: operation cannot be cancelled"); + delete _timestamps[id]; + + emit Cancelled(id); + } + + /** + * @dev Execute an (ready) operation containing a single transaction. + * + * Emits a {CallExecuted} event. + * + * Requirements: + * + * - the caller must have the 'executor' role. + */ + function execute( + address target, + uint256 value, + bytes calldata data, + bytes32 predecessor, + bytes32 salt + ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) { + bytes32 id = hashOperation(target, value, data, predecessor, salt); + _beforeCall(id, predecessor); + _call(id, 0, target, value, data); + _afterCall(id); + } + + /** + * @dev Execute an (ready) operation containing a batch of transactions. + * + * Emits one {CallExecuted} event per transaction in the batch. + * + * Requirements: + * + * - the caller must have the 'executor' role. + */ + function executeBatch( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata datas, + bytes32 predecessor, + bytes32 salt + ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) { + require(targets.length == values.length, "TimelockController: length mismatch"); + require(targets.length == datas.length, "TimelockController: length mismatch"); + + bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt); + _beforeCall(id, predecessor); + for (uint256 i = 0; i < targets.length; ++i) { + _call(id, i, targets[i], values[i], datas[i]); + } + _afterCall(id); + // ASSUME THAT THERE IS NO REENTRANCY IN WIZARDHARNESS1 + } + + /** + * @dev Checks before execution of an operation's calls. + */ + function _beforeCall(bytes32 id, bytes32 predecessor) private view { + require(isOperationReady(id), "TimelockController: operation is not ready"); + require(predecessor == bytes32(0) || isOperationDone(predecessor), "TimelockController: missing dependency"); + } + + /** + * @dev Checks after execution of an operation's calls. + */ + function _afterCall(bytes32 id) private { + require(isOperationReady(id), "TimelockController: operation is not ready"); + _timestamps[id] = _DONE_TIMESTAMP; + } + + /** + * @dev Execute an operation's call. + * + * Emits a {CallExecuted} event. + */ + function _call( + bytes32 id, + uint256 index, + address target, + uint256 value, + bytes calldata data + ) private { + (bool success, ) = target.call{value: value}(data); + require(success, "TimelockController: underlying transaction reverted"); + + emit CallExecuted(id, index, target, value, data); + } + + /** + * @dev Changes the minimum timelock duration for future operations. + * + * Emits a {MinDelayChange} event. + * + * Requirements: + * + * - the caller must be the timelock itself. This can only be achieved by scheduling and later executing + * an operation where the timelock is the target and the data is the ABI-encoded call to this function. + */ + function updateDelay(uint256 newDelay) external virtual { + require(msg.sender == address(this), "TimelockController: caller must be timelock"); + emit MinDelayChange(_minDelay, newDelay); + _minDelay = newDelay; + } +} diff --git a/certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol b/certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol new file mode 100644 index 000000000..20e507b61 --- /dev/null +++ b/certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/compatibility/GovernorCompatibilityBravo.sol) + +pragma solidity ^0.8.0; + +import "../../utils/Counters.sol"; +import "../../utils/math/SafeCast.sol"; +import "../extensions/IGovernorTimelock.sol"; +import "../Governor.sol"; +import "./IGovernorCompatibilityBravo.sol"; + +/** + * @dev Compatibility layer that implements GovernorBravo compatibility on to of {Governor}. + * + * This compatibility layer includes a voting system and requires a {IGovernorTimelock} compatible module to be added + * through inheritance. It does not include token bindings, not does it include any variable upgrade patterns. + * + * NOTE: When using this module, you may need to enable the Solidity optimizer to avoid hitting the contract size limit. + * + * _Available since v4.3._ + */ +abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorCompatibilityBravo, Governor { + using Counters for Counters.Counter; + using Timers for Timers.BlockNumber; + + enum VoteType { + Against, + For, + Abstain + } + + struct ProposalDetails { + address proposer; + address[] targets; + uint256[] values; + string[] signatures; + bytes[] calldatas; + uint256 forVotes; + uint256 againstVotes; + uint256 abstainVotes; + mapping(address => Receipt) receipts; + bytes32 descriptionHash; + } + + mapping(uint256 => ProposalDetails) private _proposalDetails; + + // solhint-disable-next-line func-name-mixedcase + function COUNTING_MODE() public pure virtual override returns (string memory) { + return "support=bravo&quorum=bravo"; + } + + // ============================================== Proposal lifecycle ============================================== + /** + * @dev See {IGovernor-propose}. + */ + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override(IGovernor, Governor) returns (uint256) { + _storeProposal(_msgSender(), targets, values, new string[](calldatas.length), calldatas, description); + return super.propose(targets, values, calldatas, description); + } + + /** + * @dev See {IGovernorCompatibilityBravo-propose}. + */ + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + _storeProposal(_msgSender(), targets, values, signatures, calldatas, description); + return propose(targets, values, _encodeCalldata(signatures, calldatas), description); + } + + /** + * @dev See {IGovernorCompatibilityBravo-queue}. + */ + function queue(uint256 proposalId) public virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + queue( + details.targets, + details.values, + _encodeCalldata(details.signatures, details.calldatas), + details.descriptionHash + ); + } + + /** + * @dev See {IGovernorCompatibilityBravo-execute}. + */ + function execute(uint256 proposalId) public payable virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + execute( + details.targets, + details.values, + _encodeCalldata(details.signatures, details.calldatas), + details.descriptionHash + ); + } + + function cancel(uint256 proposalId) public virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + + require( + _msgSender() == details.proposer || getVotes(details.proposer, block.number - 1) < proposalThreshold(), + "GovernorBravo: proposer above threshold" + ); + + _cancel( + details.targets, + details.values, + _encodeCalldata(details.signatures, details.calldatas), + details.descriptionHash + ); + } + + /** + * @dev Encodes calldatas with optional function signature. + */ + function _encodeCalldata(string[] memory signatures, bytes[] memory calldatas) + private + pure + returns (bytes[] memory) + { + bytes[] memory fullcalldatas = new bytes[](calldatas.length); + + for (uint256 i = 0; i < signatures.length; ++i) { + fullcalldatas[i] = bytes(signatures[i]).length == 0 + ? calldatas[i] + : abi.encodeWithSignature(signatures[i], calldatas[i]); + } + + return fullcalldatas; + } + + /** + * @dev Store proposal metadata for later lookup + */ + function _storeProposal( + address proposer, + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) private { + bytes32 descriptionHash = keccak256(bytes(description)); + uint256 proposalId = hashProposal(targets, values, _encodeCalldata(signatures, calldatas), descriptionHash); + + ProposalDetails storage details = _proposalDetails[proposalId]; + if (details.descriptionHash == bytes32(0)) { + details.proposer = proposer; + details.targets = targets; + details.values = values; + details.signatures = signatures; + details.calldatas = calldatas; + details.descriptionHash = descriptionHash; + } + } + + // ==================================================== Views ===================================================== + /** + * @dev See {IGovernorCompatibilityBravo-proposals}. + */ + function proposals(uint256 proposalId) + public + view + virtual + override + returns ( + uint256 id, + address proposer, + uint256 eta, + uint256 startBlock, + uint256 endBlock, + uint256 forVotes, + uint256 againstVotes, + uint256 abstainVotes, + bool canceled, + bool executed + ) + { + id = proposalId; + eta = proposalEta(proposalId); + startBlock = proposalSnapshot(proposalId); + endBlock = proposalDeadline(proposalId); + + ProposalDetails storage details = _proposalDetails[proposalId]; + proposer = details.proposer; + forVotes = details.forVotes; + againstVotes = details.againstVotes; + abstainVotes = details.abstainVotes; + + ProposalState status = state(proposalId); + canceled = status == ProposalState.Canceled; + executed = status == ProposalState.Executed; + } + + /** + * @dev See {IGovernorCompatibilityBravo-getActions}. + */ + function getActions(uint256 proposalId) + public + view + virtual + override + returns ( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas + ) + { + ProposalDetails storage details = _proposalDetails[proposalId]; + return (details.targets, details.values, details.signatures, details.calldatas); + } + + /** + * @dev See {IGovernorCompatibilityBravo-getReceipt}. + */ + function getReceipt(uint256 proposalId, address voter) public view virtual override returns (Receipt memory) { + return _proposalDetails[proposalId].receipts[voter]; + } + + /** + * @dev See {IGovernorCompatibilityBravo-quorumVotes}. + */ + function quorumVotes() public view virtual override returns (uint256) { + return quorum(block.number - 1); + } + + // ==================================================== Voting ==================================================== + /** + * @dev See {IGovernor-hasVoted}. + */ + function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) { + return _proposalDetails[proposalId].receipts[account].hasVoted; + } + + /** + * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. + */ + function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal + ProposalDetails storage details = _proposalDetails[proposalId]; + return quorum(proposalSnapshot(proposalId)) < details.forVotes; + } + + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be scritly over the againstVotes. + */ + function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal + ProposalDetails storage details = _proposalDetails[proposalId]; + return details.forVotes > details.againstVotes; + } + + /** + * @dev See {Governor-_countVote}. In this module, the support follows Governor Bravo. + */ + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + Receipt storage receipt = details.receipts[account]; + + require(!receipt.hasVoted, "GovernorCompatibilityBravo: vote already cast"); + receipt.hasVoted = true; + receipt.support = support; + receipt.votes = SafeCast.toUint96(weight); + + if (support == uint8(VoteType.Against)) { + details.againstVotes += weight; + } else if (support == uint8(VoteType.For)) { + details.forVotes += weight; + } else if (support == uint8(VoteType.Abstain)) { + details.abstainVotes += weight; + } else { + revert("GovernorCompatibilityBravo: invalid vote type"); + } + } +} diff --git a/certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol b/certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol new file mode 100644 index 000000000..ae2cb7c39 --- /dev/null +++ b/certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/compatibility/IGovernorCompatibilityBravo.sol) + +pragma solidity ^0.8.0; + +import "../IGovernor.sol"; + +/** + * @dev Interface extension that adds missing functions to the {Governor} core to provide `GovernorBravo` compatibility. + * + * _Available since v4.3._ + */ +abstract contract IGovernorCompatibilityBravo is IGovernor { + /** + * @dev Proposal structure from Compound Governor Bravo. Not actually used by the compatibility layer, as + * {{proposal}} returns a very different structure. + */ + struct Proposal { + uint256 id; + address proposer; + uint256 eta; + address[] targets; + uint256[] values; + string[] signatures; + bytes[] calldatas; + uint256 startBlock; + uint256 endBlock; + uint256 forVotes; + uint256 againstVotes; + uint256 abstainVotes; + bool canceled; + bool executed; + mapping(address => Receipt) receipts; + } + + /** + * @dev Receipt structure from Compound Governor Bravo + */ + struct Receipt { + bool hasVoted; + uint8 support; + uint96 votes; + } + + /** + * @dev Part of the Governor Bravo's interface. + */ + function quorumVotes() public view virtual returns (uint256); + + /** + * @dev Part of the Governor Bravo's interface: _"The official record of all proposals ever proposed"_. + */ + function proposals(uint256) + public + view + virtual + returns ( + uint256 id, + address proposer, + uint256 eta, + uint256 startBlock, + uint256 endBlock, + uint256 forVotes, + uint256 againstVotes, + uint256 abstainVotes, + bool canceled, + bool executed + ); + + /** + * @dev Part of the Governor Bravo's interface: _"Function used to propose a new proposal"_. + */ + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) public virtual returns (uint256); + + /** + * @dev Part of the Governor Bravo's interface: _"Queues a proposal of state succeeded"_. + */ + function queue(uint256 proposalId) public virtual; + + /** + * @dev Part of the Governor Bravo's interface: _"Executes a queued proposal if eta has passed"_. + */ + function execute(uint256 proposalId) public payable virtual; + + /** + * @dev Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold. + */ + function cancel(uint256 proposalId) public virtual; + + /** + * @dev Part of the Governor Bravo's interface: _"Gets actions of a proposal"_. + */ + function getActions(uint256 proposalId) + public + view + virtual + returns ( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas + ); + + /** + * @dev Part of the Governor Bravo's interface: _"Gets the receipt for a voter on a given proposal"_. + */ + function getReceipt(uint256 proposalId, address voter) public view virtual returns (Receipt memory); +} diff --git a/certora/munged/governance/extensions/GovernorCountingSimple.sol b/certora/munged/governance/extensions/GovernorCountingSimple.sol new file mode 100644 index 000000000..b8c72ed9e --- /dev/null +++ b/certora/munged/governance/extensions/GovernorCountingSimple.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorCountingSimple.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; + +/** + * @dev Extension of {Governor} for simple, 3 options, vote counting. + * + * _Available since v4.3._ + */ +abstract contract GovernorCountingSimple is Governor { + /** + * @dev Supported vote types. Matches Governor Bravo ordering. + */ + enum VoteType { + Against, + For, + Abstain + } + + struct ProposalVote { + uint256 againstVotes; + uint256 forVotes; + uint256 abstainVotes; + mapping(address => bool) hasVoted; + } + + mapping(uint256 => ProposalVote) private _proposalVotes; + + /** + * @dev See {IGovernor-COUNTING_MODE}. + */ + // solhint-disable-next-line func-name-mixedcase + function COUNTING_MODE() public pure virtual override returns (string memory) { + return "support=bravo&quorum=for,abstain"; + } + + /** + * @dev See {IGovernor-hasVoted}. + */ + function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) { + return _proposalVotes[proposalId].hasVoted[account]; + } + + /** + * @dev Accessor to the internal vote counts. + */ + function proposalVotes(uint256 proposalId) + public + view + virtual + returns ( + uint256 againstVotes, + uint256 forVotes, + uint256 abstainVotes + ) + { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + return (proposalvote.againstVotes, proposalvote.forVotes, proposalvote.abstainVotes); + } + + /** + * @dev See {Governor-_quorumReached}. + */ + function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + + return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes; + } + + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. + */ + function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + + return proposalvote.forVotes > proposalvote.againstVotes; + } + + /** + * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo). + */ + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight + ) internal virtual override { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + + require(!proposalvote.hasVoted[account], "GovernorVotingSimple: vote already cast"); + proposalvote.hasVoted[account] = true; + + if (support == uint8(VoteType.Against)) { + proposalvote.againstVotes += weight; + } else if (support == uint8(VoteType.For)) { + proposalvote.forVotes += weight; + } else if (support == uint8(VoteType.Abstain)) { + proposalvote.abstainVotes += weight; + } else { + revert("GovernorVotingSimple: invalid value for enum VoteType"); + } + } +} diff --git a/certora/munged/governance/extensions/GovernorProposalThreshold.sol b/certora/munged/governance/extensions/GovernorProposalThreshold.sol new file mode 100644 index 000000000..d9623f200 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorProposalThreshold.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorProposalThreshold.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; + +/** + * @dev Extension of {Governor} for proposal restriction to token holders with a minimum balance. + * + * _Available since v4.3._ + * _Deprecated since v4.4._ + */ +abstract contract GovernorProposalThreshold is Governor { + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + return super.propose(targets, values, calldatas, description); + } +} diff --git a/certora/munged/governance/extensions/GovernorSettings.sol b/certora/munged/governance/extensions/GovernorSettings.sol new file mode 100644 index 000000000..9b68f3cf6 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorSettings.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorSettings.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; + +/** + * @dev Extension of {Governor} for settings updatable through governance. + * + * _Available since v4.4._ + */ +abstract contract GovernorSettings is Governor { + uint256 private _votingDelay; + uint256 private _votingPeriod; + uint256 private _proposalThreshold; + + event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay); + event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod); + event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold); + + /** + * @dev Initialize the governance parameters. + */ + constructor( + uint256 initialVotingDelay, + uint256 initialVotingPeriod, + uint256 initialProposalThreshold + ) { + _setVotingDelay(initialVotingDelay); + _setVotingPeriod(initialVotingPeriod); + _setProposalThreshold(initialProposalThreshold); + } + + /** + * @dev See {IGovernor-votingDelay}. + */ + function votingDelay() public view virtual override returns (uint256) { + return _votingDelay; + } + + /** + * @dev See {IGovernor-votingPeriod}. + */ + function votingPeriod() public view virtual override returns (uint256) { + return _votingPeriod; + } + + /** + * @dev See {Governor-proposalThreshold}. + */ + function proposalThreshold() public view virtual override returns (uint256) { + return _proposalThreshold; + } + + /** + * @dev Update the voting delay. This operation can only be performed through a governance proposal. + * + * Emits a {VotingDelaySet} event. + */ + function setVotingDelay(uint256 newVotingDelay) public onlyGovernance { + _setVotingDelay(newVotingDelay); + } + + /** + * @dev Update the voting period. This operation can only be performed through a governance proposal. + * + * Emits a {VotingPeriodSet} event. + */ + function setVotingPeriod(uint256 newVotingPeriod) public onlyGovernance { + _setVotingPeriod(newVotingPeriod); + } + + /** + * @dev Update the proposal threshold. This operation can only be performed through a governance proposal. + * + * Emits a {ProposalThresholdSet} event. + */ + function setProposalThreshold(uint256 newProposalThreshold) public onlyGovernance { + _setProposalThreshold(newProposalThreshold); + } + + /** + * @dev Internal setter for the voting delay. + * + * Emits a {VotingDelaySet} event. + */ + function _setVotingDelay(uint256 newVotingDelay) internal virtual { + emit VotingDelaySet(_votingDelay, newVotingDelay); + _votingDelay = newVotingDelay; + } + + /** + * @dev Internal setter for the voting period. + * + * Emits a {VotingPeriodSet} event. + */ + function _setVotingPeriod(uint256 newVotingPeriod) internal virtual { + // voting period must be at least one block long + require(newVotingPeriod > 0, "GovernorSettings: voting period too low"); + emit VotingPeriodSet(_votingPeriod, newVotingPeriod); + _votingPeriod = newVotingPeriod; + } + + /** + * @dev Internal setter for the proposal threshold. + * + * Emits a {ProposalThresholdSet} event. + */ + function _setProposalThreshold(uint256 newProposalThreshold) internal virtual { + emit ProposalThresholdSet(_proposalThreshold, newProposalThreshold); + _proposalThreshold = newProposalThreshold; + } +} diff --git a/certora/munged/governance/extensions/GovernorTimelockCompound.sol b/certora/munged/governance/extensions/GovernorTimelockCompound.sol new file mode 100644 index 000000000..f9685b6e0 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorTimelockCompound.sol @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorTimelockCompound.sol) + +pragma solidity ^0.8.0; + +import "./IGovernorTimelock.sol"; +import "../Governor.sol"; +import "../../utils/math/SafeCast.sol"; + +/** + * https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol[Compound's timelock] interface + */ +interface ICompoundTimelock { + receive() external payable; + + // solhint-disable-next-line func-name-mixedcase + function GRACE_PERIOD() external view returns (uint256); + + // solhint-disable-next-line func-name-mixedcase + function MINIMUM_DELAY() external view returns (uint256); + + // solhint-disable-next-line func-name-mixedcase + function MAXIMUM_DELAY() external view returns (uint256); + + function admin() external view returns (address); + + function pendingAdmin() external view returns (address); + + function delay() external view returns (uint256); + + function queuedTransactions(bytes32) external view returns (bool); + + function setDelay(uint256) external; + + function acceptAdmin() external; + + function setPendingAdmin(address) external; + + function queueTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) external returns (bytes32); + + function cancelTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) external; + + function executeTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) external payable returns (bytes memory); +} + +/** + * @dev Extension of {Governor} that binds the execution process to a Compound Timelock. This adds a delay, enforced by + * the external timelock to all successful proposal (in addition to the voting duration). The {Governor} needs to be + * the admin of the timelock for any operation to be performed. A public, unrestricted, + * {GovernorTimelockCompound-__acceptAdmin} is available to accept ownership of the timelock. + * + * Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus, + * the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be + * inaccessible. + * + * _Available since v4.3._ + */ +abstract contract GovernorTimelockCompound is IGovernorTimelock, Governor { + using SafeCast for uint256; + using Timers for Timers.Timestamp; + + struct ProposalTimelock { + Timers.Timestamp timer; + } + + ICompoundTimelock private _timelock; + + mapping(uint256 => ProposalTimelock) private _proposalTimelocks; + + /** + * @dev Emitted when the timelock controller used for proposal execution is modified. + */ + event TimelockChange(address oldTimelock, address newTimelock); + + /** + * @dev Set the timelock. + */ + constructor(ICompoundTimelock timelockAddress) { + _updateTimelock(timelockAddress); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, Governor) returns (bool) { + return interfaceId == type(IGovernorTimelock).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Overriden version of the {Governor-state} function with added support for the `Queued` and `Expired` status. + */ + function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) { + ProposalState status = super.state(proposalId); + + if (status != ProposalState.Succeeded) { + return status; + } + + uint256 eta = proposalEta(proposalId); + if (eta == 0) { + return status; + } else if (block.timestamp >= eta + _timelock.GRACE_PERIOD()) { + return ProposalState.Expired; + } else { + return ProposalState.Queued; + } + } + + /** + * @dev Public accessor to check the address of the timelock + */ + function timelock() public view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Public accessor to check the eta of a queued proposal + */ + function proposalEta(uint256 proposalId) public view virtual override returns (uint256) { + return _proposalTimelocks[proposalId].timer.getDeadline(); + } + + /** + * @dev Function to queue a proposal to the timelock. + */ + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public virtual override returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + + require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful"); + + uint256 eta = block.timestamp + _timelock.delay(); + _proposalTimelocks[proposalId].timer.setDeadline(eta.toUint64()); + for (uint256 i = 0; i < targets.length; ++i) { + require( + !_timelock.queuedTransactions(keccak256(abi.encode(targets[i], values[i], "", calldatas[i], eta))), + "GovernorTimelockCompound: identical proposal action already queued" + ); + _timelock.queueTransaction(targets[i], values[i], "", calldatas[i], eta); + } + + emit ProposalQueued(proposalId, eta); + + return proposalId; + } + + /** + * @dev Overriden execute function that run the already queued proposal through the timelock. + */ + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 /*descriptionHash*/ + ) internal virtual override { + uint256 eta = proposalEta(proposalId); + require(eta > 0, "GovernorTimelockCompound: proposal not yet queued"); + Address.sendValue(payable(_timelock), msg.value); + for (uint256 i = 0; i < targets.length; ++i) { + _timelock.executeTransaction(targets[i], values[i], "", calldatas[i], eta); + } + } + + /** + * @dev Overriden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already + * been queued. + */ + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override returns (uint256) { + uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash); + + uint256 eta = proposalEta(proposalId); + if (eta > 0) { + for (uint256 i = 0; i < targets.length; ++i) { + _timelock.cancelTransaction(targets[i], values[i], "", calldatas[i], eta); + } + _proposalTimelocks[proposalId].timer.reset(); + } + + return proposalId; + } + + /** + * @dev Address through which the governor executes action. In this case, the timelock. + */ + function _executor() internal view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Accept admin right over the timelock. + */ + // solhint-disable-next-line private-vars-leading-underscore + function __acceptAdmin() public { + _timelock.acceptAdmin(); + } + + /** + * @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates + * must be proposed, scheduled and executed using the {Governor} workflow. + * + * For security reason, the timelock must be handed over to another admin before setting up a new one. The two + * operations (hand over the timelock) and do the update can be batched in a single proposal. + * + * Note that if the timelock admin has been handed over in a previous operation, we refuse updates made through the + * timelock if admin of the timelock has already been accepted and the operation is executed outside the scope of + * governance. + */ + function updateTimelock(ICompoundTimelock newTimelock) external virtual onlyGovernance { + _updateTimelock(newTimelock); + } + + function _updateTimelock(ICompoundTimelock newTimelock) private { + emit TimelockChange(address(_timelock), address(newTimelock)); + _timelock = newTimelock; + } +} diff --git a/certora/munged/governance/extensions/GovernorTimelockControl.sol b/certora/munged/governance/extensions/GovernorTimelockControl.sol new file mode 100644 index 000000000..892ec3a55 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorTimelockControl.sol @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorTimelockControl.sol) + +pragma solidity ^0.8.0; + +import "./IGovernorTimelock.sol"; +import "../Governor.sol"; +import "../TimelockController.sol"; + +/** + * @dev Extension of {Governor} that binds the execution process to an instance of {TimelockController}. This adds a + * delay, enforced by the {TimelockController} to all successful proposal (in addition to the voting duration). The + * {Governor} needs the proposer (an ideally the executor) roles for the {Governor} to work properly. + * + * Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus, + * the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be + * inaccessible. + * + * _Available since v4.3._ + */ +abstract contract GovernorTimelockControl is IGovernorTimelock, Governor { + TimelockController private _timelock; + mapping(uint256 => bytes32) private _timelockIds; + + /** + * @dev Emitted when the timelock controller used for proposal execution is modified. + */ + event TimelockChange(address oldTimelock, address newTimelock); + + /** + * @dev Set the timelock. + */ + constructor(TimelockController timelockAddress) { + _updateTimelock(timelockAddress); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, Governor) returns (bool) { + return interfaceId == type(IGovernorTimelock).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Overriden version of the {Governor-state} function with added support for the `Queued` status. + */ + function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) { + ProposalState status = super.state(proposalId); + + if (status != ProposalState.Succeeded) { + return status; + } + + // core tracks execution, so we just have to check if successful proposal have been queued. + bytes32 queueid = _timelockIds[proposalId]; + if (queueid == bytes32(0)) { + return status; + } else if (_timelock.isOperationDone(queueid)) { + return ProposalState.Executed; + } else { + return ProposalState.Queued; + } + } + + /** + * @dev Public accessor to check the address of the timelock + */ + function timelock() public view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Public accessor to check the eta of a queued proposal + */ + function proposalEta(uint256 proposalId) public view virtual override returns (uint256) { + uint256 eta = _timelock.getTimestamp(_timelockIds[proposalId]); + return eta == 1 ? 0 : eta; // _DONE_TIMESTAMP (1) should be replaced with a 0 value + } + + /** + * @dev Function to queue a proposal to the timelock. + */ + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public virtual override returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + + require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful"); + + uint256 delay = _timelock.getMinDelay(); + _timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash); + _timelock.scheduleBatch(targets, values, calldatas, 0, descriptionHash, delay); + + emit ProposalQueued(proposalId, block.timestamp + delay); + + return proposalId; + } + + /** + * @dev Overriden execute function that run the already queued proposal through the timelock. + */ + function _execute( + uint256, /* proposalId */ + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override { + _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); + } + + /** + * @dev Overriden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already + * been queued. + */ + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override returns (uint256) { + uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash); + + if (_timelockIds[proposalId] != 0) { + _timelock.cancel(_timelockIds[proposalId]); + delete _timelockIds[proposalId]; + } + + return proposalId; + } + + /** + * @dev Address through which the governor executes action. In this case, the timelock. + */ + function _executor() internal view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates + * must be proposed, scheduled and executed using the {Governor} workflow. + */ + function updateTimelock(TimelockController newTimelock) external virtual onlyGovernance { + _updateTimelock(newTimelock); + } + + function _updateTimelock(TimelockController newTimelock) private { + emit TimelockChange(address(_timelock), address(newTimelock)); + _timelock = newTimelock; + } +} diff --git a/certora/munged/governance/extensions/GovernorVotes.sol b/certora/munged/governance/extensions/GovernorVotes.sol new file mode 100644 index 000000000..a1172e614 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorVotes.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorVotes.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; +import "../../token/ERC20/extensions/ERC20Votes.sol"; +import "../../utils/math/Math.sol"; + +/** + * @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token. + * + * _Available since v4.3._ + */ +abstract contract GovernorVotes is Governor { + ERC20Votes public immutable token; + + constructor(ERC20Votes tokenAddress) { + token = tokenAddress; + } + + /** + * Read the voting weight from the token's built in snapshot mechanism (see {IGovernor-getVotes}). + */ + function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + return token.getPastVotes(account, blockNumber); + } +} diff --git a/certora/munged/governance/extensions/GovernorVotesComp.sol b/certora/munged/governance/extensions/GovernorVotesComp.sol new file mode 100644 index 000000000..bb6b09a01 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorVotesComp.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorVotesComp.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; +import "../../token/ERC20/extensions/ERC20VotesComp.sol"; + +/** + * @dev Extension of {Governor} for voting weight extraction from a Comp token. + * + * _Available since v4.3._ + */ +abstract contract GovernorVotesComp is Governor { + ERC20VotesComp public immutable token; + + constructor(ERC20VotesComp token_) { + token = token_; + } + + /** + * Read the voting weight from the token's built in snapshot mechanism (see {IGovernor-getVotes}). + */ + function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + return token.getPriorVotes(account, blockNumber); + } +} diff --git a/certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol b/certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol new file mode 100644 index 000000000..5bac4e597 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorVotesQuorumFraction.sol) + +pragma solidity ^0.8.0; + +import "./GovernorVotes.sol"; + +/** + * @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token and a quorum expressed as a + * fraction of the total supply. + * + * _Available since v4.3._ + */ +abstract contract GovernorVotesQuorumFraction is GovernorVotes { + uint256 private _quorumNumerator; + + event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator); + + constructor(uint256 quorumNumeratorValue) { + _updateQuorumNumerator(quorumNumeratorValue); + } + + function quorumNumerator() public view virtual returns (uint256) { + return _quorumNumerator; + } + + function quorumDenominator() public view virtual returns (uint256) { + return 100; + } + + function quorum(uint256 blockNumber) public view virtual override returns (uint256) { + return (token.getPastTotalSupply(blockNumber) * quorumNumerator()) / quorumDenominator(); + } + + function updateQuorumNumerator(uint256 newQuorumNumerator) external virtual onlyGovernance { + _updateQuorumNumerator(newQuorumNumerator); + } + + function _updateQuorumNumerator(uint256 newQuorumNumerator) internal virtual { + require( + newQuorumNumerator <= quorumDenominator(), + "GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator" + ); + + uint256 oldQuorumNumerator = _quorumNumerator; + _quorumNumerator = newQuorumNumerator; + + emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator); + } +} diff --git a/certora/munged/governance/extensions/IGovernorTimelock.sol b/certora/munged/governance/extensions/IGovernorTimelock.sol new file mode 100644 index 000000000..910135a37 --- /dev/null +++ b/certora/munged/governance/extensions/IGovernorTimelock.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (governance/extensions/IGovernorTimelock.sol) + +pragma solidity ^0.8.0; + +import "../IGovernor.sol"; + +/** + * @dev Extension of the {IGovernor} for timelock supporting modules. + * + * _Available since v4.3._ + */ +abstract contract IGovernorTimelock is IGovernor { + event ProposalQueued(uint256 proposalId, uint256 eta); + + function timelock() public view virtual returns (address); + + function proposalEta(uint256 proposalId) public view virtual returns (uint256); + + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public virtual returns (uint256 proposalId); +} diff --git a/certora/munged/interfaces/IERC1155.sol b/certora/munged/interfaces/IERC1155.sol new file mode 100644 index 000000000..995aa876c --- /dev/null +++ b/certora/munged/interfaces/IERC1155.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1155.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC1155/IERC1155.sol"; diff --git a/certora/munged/interfaces/IERC1155MetadataURI.sol b/certora/munged/interfaces/IERC1155MetadataURI.sol new file mode 100644 index 000000000..6f7af0440 --- /dev/null +++ b/certora/munged/interfaces/IERC1155MetadataURI.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1155MetadataURI.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC1155/extensions/IERC1155MetadataURI.sol"; diff --git a/certora/munged/interfaces/IERC1155Receiver.sol b/certora/munged/interfaces/IERC1155Receiver.sol new file mode 100644 index 000000000..cd947ddf1 --- /dev/null +++ b/certora/munged/interfaces/IERC1155Receiver.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1155Receiver.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC1155/IERC1155Receiver.sol"; diff --git a/certora/munged/interfaces/IERC1271.sol b/certora/munged/interfaces/IERC1271.sol new file mode 100644 index 000000000..ee89e252b --- /dev/null +++ b/certora/munged/interfaces/IERC1271.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1271.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC1271 standard signature validation method for + * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. + * + * _Available since v4.1._ + */ +interface IERC1271 { + /** + * @dev Should return whether the signature provided is valid for the provided data + * @param hash Hash of the data to be signed + * @param signature Signature byte array associated with _data + */ + function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); +} diff --git a/certora/munged/interfaces/IERC1363.sol b/certora/munged/interfaces/IERC1363.sol new file mode 100644 index 000000000..6d36befda --- /dev/null +++ b/certora/munged/interfaces/IERC1363.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1363.sol) + +pragma solidity ^0.8.0; + +import "./IERC20.sol"; +import "./IERC165.sol"; + +interface IERC1363 is IERC165, IERC20 { + /* + * Note: the ERC-165 identifier for this interface is 0x4bbee2df. + * 0x4bbee2df === + * bytes4(keccak256('transferAndCall(address,uint256)')) ^ + * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ + * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ + * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) + */ + + /* + * Note: the ERC-165 identifier for this interface is 0xfb9ec8ce. + * 0xfb9ec8ce === + * bytes4(keccak256('approveAndCall(address,uint256)')) ^ + * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) + */ + + /** + * @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @return true unless throwing + */ + function transferAndCall(address to, uint256 value) external returns (bool); + + /** + * @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @param data bytes Additional data with no specified format, sent in call to `to` + * @return true unless throwing + */ + function transferAndCall( + address to, + uint256 value, + bytes memory data + ) external returns (bool); + + /** + * @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @return true unless throwing + */ + function transferFromAndCall( + address from, + address to, + uint256 value + ) external returns (bool); + + /** + * @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @param data bytes Additional data with no specified format, sent in call to `to` + * @return true unless throwing + */ + function transferFromAndCall( + address from, + address to, + uint256 value, + bytes memory data + ) external returns (bool); + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender + * and then call `onApprovalReceived` on spender. + * @param spender address The address which will spend the funds + * @param value uint256 The amount of tokens to be spent + */ + function approveAndCall(address spender, uint256 value) external returns (bool); + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender + * and then call `onApprovalReceived` on spender. + * @param spender address The address which will spend the funds + * @param value uint256 The amount of tokens to be spent + * @param data bytes Additional data with no specified format, sent in call to `spender` + */ + function approveAndCall( + address spender, + uint256 value, + bytes memory data + ) external returns (bool); +} diff --git a/certora/munged/interfaces/IERC1363Receiver.sol b/certora/munged/interfaces/IERC1363Receiver.sol new file mode 100644 index 000000000..ae6e10ce9 --- /dev/null +++ b/certora/munged/interfaces/IERC1363Receiver.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1363Receiver.sol) + +pragma solidity ^0.8.0; + +interface IERC1363Receiver { + /* + * Note: the ERC-165 identifier for this interface is 0x88a7ca5c. + * 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)")) + */ + + /** + * @notice Handle the receipt of ERC1363 tokens + * @dev Any ERC1363 smart contract calls this function on the recipient + * after a `transfer` or a `transferFrom`. This function MAY throw to revert and reject the + * transfer. Return of other than the magic value MUST result in the + * transaction being reverted. + * Note: the token contract address is always the message sender. + * @param operator address The address which called `transferAndCall` or `transferFromAndCall` function + * @param from address The address which are token transferred from + * @param value uint256 The amount of tokens transferred + * @param data bytes Additional data with no specified format + * @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))` + * unless throwing + */ + function onTransferReceived( + address operator, + address from, + uint256 value, + bytes memory data + ) external returns (bytes4); +} diff --git a/certora/munged/interfaces/IERC1363Spender.sol b/certora/munged/interfaces/IERC1363Spender.sol new file mode 100644 index 000000000..782b20fce --- /dev/null +++ b/certora/munged/interfaces/IERC1363Spender.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1363Spender.sol) + +pragma solidity ^0.8.0; + +interface IERC1363Spender { + /* + * Note: the ERC-165 identifier for this interface is 0x7b04a2d0. + * 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)")) + */ + + /** + * @notice Handle the approval of ERC1363 tokens + * @dev Any ERC1363 smart contract calls this function on the recipient + * after an `approve`. This function MAY throw to revert and reject the + * approval. Return of other than the magic value MUST result in the + * transaction being reverted. + * Note: the token contract address is always the message sender. + * @param owner address The address which called `approveAndCall` function + * @param value uint256 The amount of tokens to be spent + * @param data bytes Additional data with no specified format + * @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))` + * unless throwing + */ + function onApprovalReceived( + address owner, + uint256 value, + bytes memory data + ) external returns (bytes4); +} diff --git a/certora/munged/interfaces/IERC165.sol b/certora/munged/interfaces/IERC165.sol new file mode 100644 index 000000000..e1d54e698 --- /dev/null +++ b/certora/munged/interfaces/IERC165.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC165.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC165.sol"; diff --git a/certora/munged/interfaces/IERC1820Implementer.sol b/certora/munged/interfaces/IERC1820Implementer.sol new file mode 100644 index 000000000..7ce0a79d7 --- /dev/null +++ b/certora/munged/interfaces/IERC1820Implementer.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1820Implementer.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC1820Implementer.sol"; diff --git a/certora/munged/interfaces/IERC1820Registry.sol b/certora/munged/interfaces/IERC1820Registry.sol new file mode 100644 index 000000000..aea318470 --- /dev/null +++ b/certora/munged/interfaces/IERC1820Registry.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1820Registry.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC1820Registry.sol"; diff --git a/certora/munged/interfaces/IERC20.sol b/certora/munged/interfaces/IERC20.sol new file mode 100644 index 000000000..ee6091660 --- /dev/null +++ b/certora/munged/interfaces/IERC20.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC20.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/IERC20.sol"; diff --git a/certora/munged/interfaces/IERC20Metadata.sol b/certora/munged/interfaces/IERC20Metadata.sol new file mode 100644 index 000000000..4752b50a3 --- /dev/null +++ b/certora/munged/interfaces/IERC20Metadata.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC20Metadata.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/IERC20Metadata.sol"; diff --git a/certora/munged/interfaces/IERC2981.sol b/certora/munged/interfaces/IERC2981.sol new file mode 100644 index 000000000..3ef94b0e7 --- /dev/null +++ b/certora/munged/interfaces/IERC2981.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC2981.sol) + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +/** + * @dev Interface for the NFT Royalty Standard + */ +interface IERC2981 is IERC165 { + /** + * @dev Called with the sale price to determine how much royalty is owed and to whom. + * @param tokenId - the NFT asset queried for royalty information + * @param salePrice - the sale price of the NFT asset specified by `tokenId` + * @return receiver - address of who should be sent the royalty payment + * @return royaltyAmount - the royalty payment amount for `salePrice` + */ + function royaltyInfo(uint256 tokenId, uint256 salePrice) + external + view + returns (address receiver, uint256 royaltyAmount); +} diff --git a/certora/munged/interfaces/IERC3156.sol b/certora/munged/interfaces/IERC3156.sol new file mode 100644 index 000000000..f24e30142 --- /dev/null +++ b/certora/munged/interfaces/IERC3156.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC3156.sol) + +pragma solidity ^0.8.0; + +import "./IERC3156FlashBorrower.sol"; +import "./IERC3156FlashLender.sol"; diff --git a/certora/munged/interfaces/IERC3156FlashBorrower.sol b/certora/munged/interfaces/IERC3156FlashBorrower.sol new file mode 100644 index 000000000..e956c2a6b --- /dev/null +++ b/certora/munged/interfaces/IERC3156FlashBorrower.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC3156FlashBorrower.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC3156 FlashBorrower, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * _Available since v4.1._ + */ +interface IERC3156FlashBorrower { + /** + * @dev Receive a flash loan. + * @param initiator The initiator of the loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param fee The additional amount of tokens to repay. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" + */ + function onFlashLoan( + address initiator, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) external returns (bytes32); +} diff --git a/certora/munged/interfaces/IERC3156FlashLender.sol b/certora/munged/interfaces/IERC3156FlashLender.sol new file mode 100644 index 000000000..954f79bfc --- /dev/null +++ b/certora/munged/interfaces/IERC3156FlashLender.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC3156FlashLender.sol) + +pragma solidity ^0.8.0; + +import "./IERC3156FlashBorrower.sol"; + +/** + * @dev Interface of the ERC3156 FlashLender, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * _Available since v4.1._ + */ +interface IERC3156FlashLender { + /** + * @dev The amount of currency available to be lended. + * @param token The loan currency. + * @return The amount of `token` that can be borrowed. + */ + function maxFlashLoan(address token) external view returns (uint256); + + /** + * @dev The fee to be charged for a given loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @return The amount of `token` to be charged for the loan, on top of the returned principal. + */ + function flashFee(address token, uint256 amount) external view returns (uint256); + + /** + * @dev Initiate a flash loan. + * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + */ + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) external returns (bool); +} diff --git a/certora/munged/interfaces/IERC721.sol b/certora/munged/interfaces/IERC721.sol new file mode 100644 index 000000000..59a2e2f11 --- /dev/null +++ b/certora/munged/interfaces/IERC721.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC721.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/IERC721.sol"; diff --git a/certora/munged/interfaces/IERC721Enumerable.sol b/certora/munged/interfaces/IERC721Enumerable.sol new file mode 100644 index 000000000..e3b17e7d0 --- /dev/null +++ b/certora/munged/interfaces/IERC721Enumerable.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/IERC721Enumerable.sol"; diff --git a/certora/munged/interfaces/IERC721Metadata.sol b/certora/munged/interfaces/IERC721Metadata.sol new file mode 100644 index 000000000..1a7f1f8e6 --- /dev/null +++ b/certora/munged/interfaces/IERC721Metadata.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC721Metadata.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/IERC721Metadata.sol"; diff --git a/certora/munged/interfaces/IERC721Receiver.sol b/certora/munged/interfaces/IERC721Receiver.sol new file mode 100644 index 000000000..0e65c5e6c --- /dev/null +++ b/certora/munged/interfaces/IERC721Receiver.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC721Receiver.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/IERC721Receiver.sol"; diff --git a/certora/munged/interfaces/IERC777.sol b/certora/munged/interfaces/IERC777.sol new file mode 100644 index 000000000..4d808007e --- /dev/null +++ b/certora/munged/interfaces/IERC777.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC777.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777.sol"; diff --git a/certora/munged/interfaces/IERC777Recipient.sol b/certora/munged/interfaces/IERC777Recipient.sol new file mode 100644 index 000000000..36b58e51b --- /dev/null +++ b/certora/munged/interfaces/IERC777Recipient.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC777Recipient.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777Recipient.sol"; diff --git a/certora/munged/interfaces/IERC777Sender.sol b/certora/munged/interfaces/IERC777Sender.sol new file mode 100644 index 000000000..f9f564853 --- /dev/null +++ b/certora/munged/interfaces/IERC777Sender.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/IERC777Sender.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777Sender.sol"; diff --git a/certora/munged/interfaces/README.adoc b/certora/munged/interfaces/README.adoc new file mode 100644 index 000000000..31dd27c85 --- /dev/null +++ b/certora/munged/interfaces/README.adoc @@ -0,0 +1,50 @@ += Interfaces + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/interfaces + +== List of standardized interfaces +These interfaces are available as `.sol` files, and also as compiler `.json` ABI files (through the npm package). These +are usefull to interract with third party contracts that implement them. + +- {IERC20} +- {IERC20Metadata} +- {IERC165} +- {IERC721} +- {IERC721Receiver} +- {IERC721Enumerable} +- {IERC721Metadata} +- {IERC777} +- {IERC777Recipient} +- {IERC777Sender} +- {IERC1155} +- {IERC1155Receiver} +- {IERC1155MetadataURI} +- {IERC1271} +- {IERC1363} +- {IERC1820Implementer} +- {IERC1820Registry} +- {IERC2612} +- {IERC2981} +- {IERC3156FlashLender} +- {IERC3156FlashBorrower} + +== Detailed ABI + +{{IERC1271}} + +{{IERC1363}} + +{{IERC1363Receiver}} + +{{IERC1820Implementer}} + +{{IERC1820Registry}} + +{{IERC2612}} + +{{IERC2981}} + +{{IERC3156FlashLender}} + +{{IERC3156FlashBorrower}} diff --git a/certora/munged/interfaces/draft-IERC2612.sol b/certora/munged/interfaces/draft-IERC2612.sol new file mode 100644 index 000000000..160a5ef61 --- /dev/null +++ b/certora/munged/interfaces/draft-IERC2612.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (interfaces/draft-IERC2612.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/draft-IERC20Permit.sol"; + +interface IERC2612 is IERC20Permit {} diff --git a/certora/munged/metatx/ERC2771Context.sol b/certora/munged/metatx/ERC2771Context.sol new file mode 100644 index 000000000..e5c0674a7 --- /dev/null +++ b/certora/munged/metatx/ERC2771Context.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (metatx/ERC2771Context.sol) + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +/** + * @dev Context variant with ERC2771 support. + */ +abstract contract ERC2771Context is Context { + address private _trustedForwarder; + + constructor(address trustedForwarder) { + _trustedForwarder = trustedForwarder; + } + + function isTrustedForwarder(address forwarder) public view virtual returns (bool) { + return forwarder == _trustedForwarder; + } + + function _msgSender() internal view virtual override returns (address sender) { + if (isTrustedForwarder(msg.sender)) { + // The assembly code is more direct than the Solidity version using `abi.decode`. + assembly { + sender := shr(96, calldataload(sub(calldatasize(), 20))) + } + } else { + return super._msgSender(); + } + } + + function _msgData() internal view virtual override returns (bytes calldata) { + if (isTrustedForwarder(msg.sender)) { + return msg.data[:msg.data.length - 20]; + } else { + return super._msgData(); + } + } +} diff --git a/certora/munged/metatx/MinimalForwarder.sol b/certora/munged/metatx/MinimalForwarder.sol new file mode 100644 index 000000000..b3326f3f4 --- /dev/null +++ b/certora/munged/metatx/MinimalForwarder.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (metatx/MinimalForwarder.sol) + +pragma solidity ^0.8.0; + +import "../utils/cryptography/ECDSA.sol"; +import "../utils/cryptography/draft-EIP712.sol"; + +/** + * @dev Simple minimal forwarder to be used together with an ERC2771 compatible contract. See {ERC2771Context}. + */ +contract MinimalForwarder is EIP712 { + using ECDSA for bytes32; + + struct ForwardRequest { + address from; + address to; + uint256 value; + uint256 gas; + uint256 nonce; + bytes data; + } + + bytes32 private constant _TYPEHASH = + keccak256("ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)"); + + mapping(address => uint256) private _nonces; + + constructor() EIP712("MinimalForwarder", "0.0.1") {} + + function getNonce(address from) public view returns (uint256) { + return _nonces[from]; + } + + function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) { + address signer = _hashTypedDataV4( + keccak256(abi.encode(_TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data))) + ).recover(signature); + return _nonces[req.from] == req.nonce && signer == req.from; + } + + function execute(ForwardRequest calldata req, bytes calldata signature) + public + payable + returns (bool, bytes memory) + { + require(verify(req, signature), "MinimalForwarder: signature does not match request"); + _nonces[req.from] = req.nonce + 1; + + (bool success, bytes memory returndata) = req.to.call{gas: req.gas, value: req.value}( + abi.encodePacked(req.data, req.from) + ); + // Validate that the relayer has sent enough gas for the call. + // See https://ronan.eth.link/blog/ethereum-gas-dangers/ + assert(gasleft() > req.gas / 63); + + return (success, returndata); + } +} diff --git a/certora/munged/metatx/README.adoc b/certora/munged/metatx/README.adoc new file mode 100644 index 000000000..eccdeaf97 --- /dev/null +++ b/certora/munged/metatx/README.adoc @@ -0,0 +1,12 @@ += Meta Transactions + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/metatx + +== Core + +{{ERC2771Context}} + +== Utils + +{{MinimalForwarder}} diff --git a/certora/munged/mocks/AccessControlEnumerableMock.sol b/certora/munged/mocks/AccessControlEnumerableMock.sol new file mode 100644 index 000000000..7b15e3602 --- /dev/null +++ b/certora/munged/mocks/AccessControlEnumerableMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/AccessControlEnumerable.sol"; + +contract AccessControlEnumerableMock is AccessControlEnumerable { + constructor() { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public { + _setRoleAdmin(roleId, adminRoleId); + } + + function senderProtected(bytes32 roleId) public onlyRole(roleId) {} +} diff --git a/certora/munged/mocks/AccessControlMock.sol b/certora/munged/mocks/AccessControlMock.sol new file mode 100644 index 000000000..86f51477e --- /dev/null +++ b/certora/munged/mocks/AccessControlMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/AccessControl.sol"; + +contract AccessControlMock is AccessControl { + constructor() { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public { + _setRoleAdmin(roleId, adminRoleId); + } + + function senderProtected(bytes32 roleId) public onlyRole(roleId) {} +} diff --git a/certora/munged/mocks/AddressImpl.sol b/certora/munged/mocks/AddressImpl.sol new file mode 100644 index 000000000..702093c73 --- /dev/null +++ b/certora/munged/mocks/AddressImpl.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Address.sol"; + +contract AddressImpl { + string public sharedAnswer; + + event CallReturnValue(string data); + + function isContract(address account) external view returns (bool) { + return Address.isContract(account); + } + + function sendValue(address payable receiver, uint256 amount) external { + Address.sendValue(receiver, amount); + } + + function functionCall(address target, bytes calldata data) external { + bytes memory returnData = Address.functionCall(target, data); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + function functionCallWithValue( + address target, + bytes calldata data, + uint256 value + ) external payable { + bytes memory returnData = Address.functionCallWithValue(target, data, value); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + function functionStaticCall(address target, bytes calldata data) external { + bytes memory returnData = Address.functionStaticCall(target, data); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + function functionDelegateCall(address target, bytes calldata data) external { + bytes memory returnData = Address.functionDelegateCall(target, data); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + // sendValue's tests require the contract to hold Ether + receive() external payable {} +} diff --git a/certora/munged/mocks/ArraysImpl.sol b/certora/munged/mocks/ArraysImpl.sol new file mode 100644 index 000000000..f720524b8 --- /dev/null +++ b/certora/munged/mocks/ArraysImpl.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Arrays.sol"; + +contract ArraysImpl { + using Arrays for uint256[]; + + uint256[] private _array; + + constructor(uint256[] memory array) { + _array = array; + } + + function findUpperBound(uint256 element) external view returns (uint256) { + return _array.findUpperBound(element); + } +} diff --git a/certora/munged/mocks/BadBeacon.sol b/certora/munged/mocks/BadBeacon.sol new file mode 100644 index 000000000..bedcfed84 --- /dev/null +++ b/certora/munged/mocks/BadBeacon.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract BadBeaconNoImpl {} + +contract BadBeaconNotContract { + function implementation() external pure returns (address) { + return address(0x1); + } +} diff --git a/certora/munged/mocks/BitmapMock.sol b/certora/munged/mocks/BitmapMock.sol new file mode 100644 index 000000000..ccf8486f5 --- /dev/null +++ b/certora/munged/mocks/BitmapMock.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/structs/BitMaps.sol"; + +contract BitMapMock { + using BitMaps for BitMaps.BitMap; + + BitMaps.BitMap private _bitmap; + + function get(uint256 index) public view returns (bool) { + return _bitmap.get(index); + } + + function setTo(uint256 index, bool value) public { + _bitmap.setTo(index, value); + } + + function set(uint256 index) public { + _bitmap.set(index); + } + + function unset(uint256 index) public { + _bitmap.unset(index); + } +} diff --git a/certora/munged/mocks/CallReceiverMock.sol b/certora/munged/mocks/CallReceiverMock.sol new file mode 100644 index 000000000..11d21b405 --- /dev/null +++ b/certora/munged/mocks/CallReceiverMock.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract CallReceiverMock { + string public sharedAnswer; + + event MockFunctionCalled(); + + uint256[] private _array; + + function mockFunction() public payable returns (string memory) { + emit MockFunctionCalled(); + + return "0x1234"; + } + + function mockFunctionNonPayable() public returns (string memory) { + emit MockFunctionCalled(); + + return "0x1234"; + } + + function mockStaticFunction() public pure returns (string memory) { + return "0x1234"; + } + + function mockFunctionRevertsNoReason() public payable { + revert(); + } + + function mockFunctionRevertsReason() public payable { + revert("CallReceiverMock: reverting"); + } + + function mockFunctionThrows() public payable { + assert(false); + } + + function mockFunctionOutOfGas() public payable { + for (uint256 i = 0; ; ++i) { + _array.push(i); + } + } + + function mockFunctionWritesStorage() public returns (string memory) { + sharedAnswer = "42"; + return "0x1234"; + } +} diff --git a/certora/munged/mocks/ClashingImplementation.sol b/certora/munged/mocks/ClashingImplementation.sol new file mode 100644 index 000000000..80aca0c29 --- /dev/null +++ b/certora/munged/mocks/ClashingImplementation.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @dev Implementation contract with an admin() function made to clash with + * @dev TransparentUpgradeableProxy's to test correct functioning of the + * @dev Transparent Proxy feature. + */ +contract ClashingImplementation { + function admin() external pure returns (address) { + return 0x0000000000000000000000000000000011111142; + } + + function delegatedFunction() external pure returns (bool) { + return true; + } +} diff --git a/certora/munged/mocks/ClonesMock.sol b/certora/munged/mocks/ClonesMock.sol new file mode 100644 index 000000000..3719b0a78 --- /dev/null +++ b/certora/munged/mocks/ClonesMock.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/Clones.sol"; +import "../utils/Address.sol"; + +contract ClonesMock { + using Address for address; + using Clones for address; + + event NewInstance(address instance); + + function clone(address implementation, bytes calldata initdata) public payable { + _initAndEmit(implementation.clone(), initdata); + } + + function cloneDeterministic( + address implementation, + bytes32 salt, + bytes calldata initdata + ) public payable { + _initAndEmit(implementation.cloneDeterministic(salt), initdata); + } + + function predictDeterministicAddress(address implementation, bytes32 salt) public view returns (address predicted) { + return implementation.predictDeterministicAddress(salt); + } + + function _initAndEmit(address instance, bytes memory initdata) private { + if (initdata.length > 0) { + instance.functionCallWithValue(initdata, msg.value); + } + emit NewInstance(instance); + } +} diff --git a/certora/munged/mocks/ConditionalEscrowMock.sol b/certora/munged/mocks/ConditionalEscrowMock.sol new file mode 100644 index 000000000..ececf0521 --- /dev/null +++ b/certora/munged/mocks/ConditionalEscrowMock.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/escrow/ConditionalEscrow.sol"; + +// mock class using ConditionalEscrow +contract ConditionalEscrowMock is ConditionalEscrow { + mapping(address => bool) private _allowed; + + function setAllowed(address payee, bool allowed) public { + _allowed[payee] = allowed; + } + + function withdrawalAllowed(address payee) public view override returns (bool) { + return _allowed[payee]; + } +} diff --git a/certora/munged/mocks/ContextMock.sol b/certora/munged/mocks/ContextMock.sol new file mode 100644 index 000000000..f17af38a4 --- /dev/null +++ b/certora/munged/mocks/ContextMock.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +contract ContextMock is Context { + event Sender(address sender); + + function msgSender() public { + emit Sender(_msgSender()); + } + + event Data(bytes data, uint256 integerValue, string stringValue); + + function msgData(uint256 integerValue, string memory stringValue) public { + emit Data(_msgData(), integerValue, stringValue); + } +} + +contract ContextMockCaller { + function callSender(ContextMock context) public { + context.msgSender(); + } + + function callData( + ContextMock context, + uint256 integerValue, + string memory stringValue + ) public { + context.msgData(integerValue, stringValue); + } +} diff --git a/certora/munged/mocks/CountersImpl.sol b/certora/munged/mocks/CountersImpl.sol new file mode 100644 index 000000000..651b50baf --- /dev/null +++ b/certora/munged/mocks/CountersImpl.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Counters.sol"; + +contract CountersImpl { + using Counters for Counters.Counter; + + Counters.Counter private _counter; + + function current() public view returns (uint256) { + return _counter.current(); + } + + function increment() public { + _counter.increment(); + } + + function decrement() public { + _counter.decrement(); + } + + function reset() public { + _counter.reset(); + } +} diff --git a/certora/munged/mocks/Create2Impl.sol b/certora/munged/mocks/Create2Impl.sol new file mode 100644 index 000000000..070ad3671 --- /dev/null +++ b/certora/munged/mocks/Create2Impl.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Create2.sol"; +import "../utils/introspection/ERC1820Implementer.sol"; + +contract Create2Impl { + function deploy( + uint256 value, + bytes32 salt, + bytes memory code + ) public { + Create2.deploy(value, salt, code); + } + + function deployERC1820Implementer(uint256 value, bytes32 salt) public { + Create2.deploy(value, salt, type(ERC1820Implementer).creationCode); + } + + function computeAddress(bytes32 salt, bytes32 codeHash) public view returns (address) { + return Create2.computeAddress(salt, codeHash); + } + + function computeAddressWithDeployer( + bytes32 salt, + bytes32 codeHash, + address deployer + ) public pure returns (address) { + return Create2.computeAddress(salt, codeHash, deployer); + } + + receive() external payable {} +} diff --git a/certora/munged/mocks/DummyImplementation.sol b/certora/munged/mocks/DummyImplementation.sol new file mode 100644 index 000000000..d8651340d --- /dev/null +++ b/certora/munged/mocks/DummyImplementation.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +abstract contract Impl { + function version() public pure virtual returns (string memory); +} + +contract DummyImplementation { + uint256 public value; + string public text; + uint256[] public values; + + function initializeNonPayable() public { + value = 10; + } + + function initializePayable() public payable { + value = 100; + } + + function initializeNonPayableWithValue(uint256 _value) public { + value = _value; + } + + function initializePayableWithValue(uint256 _value) public payable { + value = _value; + } + + function initialize( + uint256 _value, + string memory _text, + uint256[] memory _values + ) public { + value = _value; + text = _text; + values = _values; + } + + function get() public pure returns (bool) { + return true; + } + + function version() public pure virtual returns (string memory) { + return "V1"; + } + + function reverts() public pure { + require(false, "DummyImplementation reverted"); + } +} + +contract DummyImplementationV2 is DummyImplementation { + function migrate(uint256 newVal) public payable { + value = newVal; + } + + function version() public pure override returns (string memory) { + return "V2"; + } +} diff --git a/certora/munged/mocks/ECDSAMock.sol b/certora/munged/mocks/ECDSAMock.sol new file mode 100644 index 000000000..97bd46669 --- /dev/null +++ b/certora/munged/mocks/ECDSAMock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/ECDSA.sol"; + +contract ECDSAMock { + using ECDSA for bytes32; + using ECDSA for bytes; + + function recover(bytes32 hash, bytes memory signature) public pure returns (address) { + return hash.recover(signature); + } + + // solhint-disable-next-line func-name-mixedcase + function recover_v_r_s( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) public pure returns (address) { + return hash.recover(v, r, s); + } + + // solhint-disable-next-line func-name-mixedcase + function recover_r_vs( + bytes32 hash, + bytes32 r, + bytes32 vs + ) public pure returns (address) { + return hash.recover(r, vs); + } + + function toEthSignedMessageHash(bytes32 hash) public pure returns (bytes32) { + return hash.toEthSignedMessageHash(); + } + + function toEthSignedMessageHash(bytes memory s) public pure returns (bytes32) { + return s.toEthSignedMessageHash(); + } +} diff --git a/certora/munged/mocks/EIP712External.sol b/certora/munged/mocks/EIP712External.sol new file mode 100644 index 000000000..6f2446900 --- /dev/null +++ b/certora/munged/mocks/EIP712External.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/draft-EIP712.sol"; +import "../utils/cryptography/ECDSA.sol"; + +contract EIP712External is EIP712 { + constructor(string memory name, string memory version) EIP712(name, version) {} + + function domainSeparator() external view returns (bytes32) { + return _domainSeparatorV4(); + } + + function verify( + bytes memory signature, + address signer, + address mailTo, + string memory mailContents + ) external view { + bytes32 digest = _hashTypedDataV4( + keccak256(abi.encode(keccak256("Mail(address to,string contents)"), mailTo, keccak256(bytes(mailContents)))) + ); + address recoveredSigner = ECDSA.recover(digest, signature); + require(recoveredSigner == signer); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/certora/munged/mocks/ERC1155BurnableMock.sol b/certora/munged/mocks/ERC1155BurnableMock.sol new file mode 100644 index 000000000..62138f28d --- /dev/null +++ b/certora/munged/mocks/ERC1155BurnableMock.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC1155/extensions/ERC1155Burnable.sol"; + +contract ERC1155BurnableMock is ERC1155Burnable { + constructor(string memory uri) ERC1155(uri) {} + + function mint( + address to, + uint256 id, + uint256 value, + bytes memory data + ) public { + _mint(to, id, value, data); + } +} diff --git a/certora/munged/mocks/ERC1155Mock.sol b/certora/munged/mocks/ERC1155Mock.sol new file mode 100644 index 000000000..0518ac26c --- /dev/null +++ b/certora/munged/mocks/ERC1155Mock.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC1155/ERC1155.sol"; + +/** + * @title ERC1155Mock + * This mock just publicizes internal functions for testing purposes + */ +contract ERC1155Mock is ERC1155 { + constructor(string memory uri) ERC1155(uri) {} + + function setURI(string memory newuri) public { + _setURI(newuri); + } + + function mint( + address to, + uint256 id, + uint256 value, + bytes memory data + ) public { + _mint(to, id, value, data); + } + + function mintBatch( + address to, + uint256[] memory ids, + uint256[] memory values, + bytes memory data + ) public { + _mintBatch(to, ids, values, data); + } + + function burn( + address owner, + uint256 id, + uint256 value + ) public { + _burn(owner, id, value); + } + + function burnBatch( + address owner, + uint256[] memory ids, + uint256[] memory values + ) public { + _burnBatch(owner, ids, values); + } +} diff --git a/certora/munged/mocks/ERC1155PausableMock.sol b/certora/munged/mocks/ERC1155PausableMock.sol new file mode 100644 index 000000000..b1a4a8e1e --- /dev/null +++ b/certora/munged/mocks/ERC1155PausableMock.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ERC1155Mock.sol"; +import "../token/ERC1155/extensions/ERC1155Pausable.sol"; + +contract ERC1155PausableMock is ERC1155Mock, ERC1155Pausable { + constructor(string memory uri) ERC1155Mock(uri) {} + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } + + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override(ERC1155, ERC1155Pausable) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + } +} diff --git a/certora/munged/mocks/ERC1155ReceiverMock.sol b/certora/munged/mocks/ERC1155ReceiverMock.sol new file mode 100644 index 000000000..6443a56c7 --- /dev/null +++ b/certora/munged/mocks/ERC1155ReceiverMock.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC1155/IERC1155Receiver.sol"; +import "../utils/introspection/ERC165.sol"; + +contract ERC1155ReceiverMock is ERC165, IERC1155Receiver { + bytes4 private _recRetval; + bool private _recReverts; + bytes4 private _batRetval; + bool private _batReverts; + + event Received(address operator, address from, uint256 id, uint256 value, bytes data, uint256 gas); + event BatchReceived(address operator, address from, uint256[] ids, uint256[] values, bytes data, uint256 gas); + + constructor( + bytes4 recRetval, + bool recReverts, + bytes4 batRetval, + bool batReverts + ) { + _recRetval = recRetval; + _recReverts = recReverts; + _batRetval = batRetval; + _batReverts = batReverts; + } + + function onERC1155Received( + address operator, + address from, + uint256 id, + uint256 value, + bytes calldata data + ) external override returns (bytes4) { + require(!_recReverts, "ERC1155ReceiverMock: reverting on receive"); + emit Received(operator, from, id, value, data, gasleft()); + return _recRetval; + } + + function onERC1155BatchReceived( + address operator, + address from, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external override returns (bytes4) { + require(!_batReverts, "ERC1155ReceiverMock: reverting on batch receive"); + emit BatchReceived(operator, from, ids, values, data, gasleft()); + return _batRetval; + } +} diff --git a/certora/munged/mocks/ERC1155SupplyMock.sol b/certora/munged/mocks/ERC1155SupplyMock.sol new file mode 100644 index 000000000..44b208007 --- /dev/null +++ b/certora/munged/mocks/ERC1155SupplyMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ERC1155Mock.sol"; +import "../token/ERC1155/extensions/ERC1155Supply.sol"; + +contract ERC1155SupplyMock is ERC1155Mock, ERC1155Supply { + constructor(string memory uri) ERC1155Mock(uri) {} + + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override(ERC1155, ERC1155Supply) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + } +} diff --git a/certora/munged/mocks/ERC1271WalletMock.sol b/certora/munged/mocks/ERC1271WalletMock.sol new file mode 100644 index 000000000..c92acdba6 --- /dev/null +++ b/certora/munged/mocks/ERC1271WalletMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/Ownable.sol"; +import "../interfaces/IERC1271.sol"; +import "../utils/cryptography/ECDSA.sol"; + +contract ERC1271WalletMock is Ownable, IERC1271 { + constructor(address originalOwner) { + transferOwnership(originalOwner); + } + + function isValidSignature(bytes32 hash, bytes memory signature) public view override returns (bytes4 magicValue) { + return ECDSA.recover(hash, signature) == owner() ? this.isValidSignature.selector : bytes4(0); + } +} diff --git a/certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol b/certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol new file mode 100644 index 000000000..7a5e5bc67 --- /dev/null +++ b/certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * https://eips.ethereum.org/EIPS/eip-214#specification + * From the specification: + * > Any attempts to make state-changing operations inside an execution instance with STATIC set to true will instead + * throw an exception. + * > These operations include [...], LOG0, LOG1, LOG2, [...] + * + * therefore, because this contract is staticcall'd we need to not emit events (which is how solidity-coverage works) + * solidity-coverage ignores the /mocks folder, so we duplicate its implementation here to avoid instrumenting it + */ +contract SupportsInterfaceWithLookupMock is IERC165 { + /* + * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 + */ + bytes4 public constant INTERFACE_ID_ERC165 = 0x01ffc9a7; + + /** + * @dev A mapping of interface id to whether or not it's supported. + */ + mapping(bytes4 => bool) private _supportedInterfaces; + + /** + * @dev A contract implementing SupportsInterfaceWithLookup + * implement ERC165 itself. + */ + constructor() { + _registerInterface(INTERFACE_ID_ERC165); + } + + /** + * @dev Implement supportsInterface(bytes4) using a lookup table. + */ + function supportsInterface(bytes4 interfaceId) public view override returns (bool) { + return _supportedInterfaces[interfaceId]; + } + + /** + * @dev Private method for registering an interface. + */ + function _registerInterface(bytes4 interfaceId) internal { + require(interfaceId != 0xffffffff, "ERC165InterfacesSupported: invalid interface id"); + _supportedInterfaces[interfaceId] = true; + } +} + +contract ERC165InterfacesSupported is SupportsInterfaceWithLookupMock { + constructor(bytes4[] memory interfaceIds) { + for (uint256 i = 0; i < interfaceIds.length; i++) { + _registerInterface(interfaceIds[i]); + } + } +} diff --git a/certora/munged/mocks/ERC165/ERC165MissingData.sol b/certora/munged/mocks/ERC165/ERC165MissingData.sol new file mode 100644 index 000000000..59cd51ae6 --- /dev/null +++ b/certora/munged/mocks/ERC165/ERC165MissingData.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract ERC165MissingData { + function supportsInterface(bytes4 interfaceId) public view {} // missing return +} diff --git a/certora/munged/mocks/ERC165/ERC165NotSupported.sol b/certora/munged/mocks/ERC165/ERC165NotSupported.sol new file mode 100644 index 000000000..486c7f0a4 --- /dev/null +++ b/certora/munged/mocks/ERC165/ERC165NotSupported.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract ERC165NotSupported {} diff --git a/certora/munged/mocks/ERC165CheckerMock.sol b/certora/munged/mocks/ERC165CheckerMock.sol new file mode 100644 index 000000000..bda5cfc78 --- /dev/null +++ b/certora/munged/mocks/ERC165CheckerMock.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165Checker.sol"; + +contract ERC165CheckerMock { + using ERC165Checker for address; + + function supportsERC165(address account) public view returns (bool) { + return account.supportsERC165(); + } + + function supportsInterface(address account, bytes4 interfaceId) public view returns (bool) { + return account.supportsInterface(interfaceId); + } + + function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool) { + return account.supportsAllInterfaces(interfaceIds); + } + + function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool[] memory) { + return account.getSupportedInterfaces(interfaceIds); + } +} diff --git a/certora/munged/mocks/ERC165Mock.sol b/certora/munged/mocks/ERC165Mock.sol new file mode 100644 index 000000000..c123d0ab2 --- /dev/null +++ b/certora/munged/mocks/ERC165Mock.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165.sol"; + +contract ERC165Mock is ERC165 {} diff --git a/certora/munged/mocks/ERC165StorageMock.sol b/certora/munged/mocks/ERC165StorageMock.sol new file mode 100644 index 000000000..4b0bae908 --- /dev/null +++ b/certora/munged/mocks/ERC165StorageMock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165Storage.sol"; + +contract ERC165StorageMock is ERC165Storage { + function registerInterface(bytes4 interfaceId) public { + _registerInterface(interfaceId); + } +} diff --git a/certora/munged/mocks/ERC1820ImplementerMock.sol b/certora/munged/mocks/ERC1820ImplementerMock.sol new file mode 100644 index 000000000..a6012d7ff --- /dev/null +++ b/certora/munged/mocks/ERC1820ImplementerMock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC1820Implementer.sol"; + +contract ERC1820ImplementerMock is ERC1820Implementer { + function registerInterfaceForAddress(bytes32 interfaceHash, address account) public { + _registerInterfaceForAddress(interfaceHash, account); + } +} diff --git a/certora/munged/mocks/ERC20BurnableMock.sol b/certora/munged/mocks/ERC20BurnableMock.sol new file mode 100644 index 000000000..0ed6c0c98 --- /dev/null +++ b/certora/munged/mocks/ERC20BurnableMock.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Burnable.sol"; + +contract ERC20BurnableMock is ERC20Burnable { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } +} diff --git a/certora/munged/mocks/ERC20CappedMock.sol b/certora/munged/mocks/ERC20CappedMock.sol new file mode 100644 index 000000000..edb36f205 --- /dev/null +++ b/certora/munged/mocks/ERC20CappedMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Capped.sol"; + +contract ERC20CappedMock is ERC20Capped { + constructor( + string memory name, + string memory symbol, + uint256 cap + ) ERC20(name, symbol) ERC20Capped(cap) {} + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } +} diff --git a/certora/munged/mocks/ERC20DecimalsMock.sol b/certora/munged/mocks/ERC20DecimalsMock.sol new file mode 100644 index 000000000..924c3af31 --- /dev/null +++ b/certora/munged/mocks/ERC20DecimalsMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/ERC20.sol"; + +contract ERC20DecimalsMock is ERC20 { + uint8 private immutable _decimals; + + constructor( + string memory name_, + string memory symbol_, + uint8 decimals_ + ) ERC20(name_, symbol_) { + _decimals = decimals_; + } + + function decimals() public view virtual override returns (uint8) { + return _decimals; + } +} diff --git a/certora/munged/mocks/ERC20FlashMintMock.sol b/certora/munged/mocks/ERC20FlashMintMock.sol new file mode 100644 index 000000000..0bb7871fc --- /dev/null +++ b/certora/munged/mocks/ERC20FlashMintMock.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20FlashMint.sol"; + +contract ERC20FlashMintMock is ERC20FlashMint { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } +} diff --git a/certora/munged/mocks/ERC20Mock.sol b/certora/munged/mocks/ERC20Mock.sol new file mode 100644 index 000000000..fd7f991ba --- /dev/null +++ b/certora/munged/mocks/ERC20Mock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/ERC20.sol"; + +// mock class using ERC20 +contract ERC20Mock is ERC20 { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) payable ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } + + function transferInternal( + address from, + address to, + uint256 value + ) public { + _transfer(from, to, value); + } + + function approveInternal( + address owner, + address spender, + uint256 value + ) public { + _approve(owner, spender, value); + } +} diff --git a/certora/munged/mocks/ERC20PausableMock.sol b/certora/munged/mocks/ERC20PausableMock.sol new file mode 100644 index 000000000..19160ba6c --- /dev/null +++ b/certora/munged/mocks/ERC20PausableMock.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Pausable.sol"; + +// mock class using ERC20Pausable +contract ERC20PausableMock is ERC20Pausable { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } + + function mint(address to, uint256 amount) public { + _mint(to, amount); + } + + function burn(address from, uint256 amount) public { + _burn(from, amount); + } +} diff --git a/certora/munged/mocks/ERC20PermitMock.sol b/certora/munged/mocks/ERC20PermitMock.sol new file mode 100644 index 000000000..20302bfa0 --- /dev/null +++ b/certora/munged/mocks/ERC20PermitMock.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/draft-ERC20Permit.sol"; + +contract ERC20PermitMock is ERC20Permit { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) payable ERC20(name, symbol) ERC20Permit(name) { + _mint(initialAccount, initialBalance); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/certora/munged/mocks/ERC20SnapshotMock.sol b/certora/munged/mocks/ERC20SnapshotMock.sol new file mode 100644 index 000000000..cb3048322 --- /dev/null +++ b/certora/munged/mocks/ERC20SnapshotMock.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Snapshot.sol"; + +contract ERC20SnapshotMock is ERC20Snapshot { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } + + function snapshot() public { + _snapshot(); + } + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } +} diff --git a/certora/munged/mocks/ERC20VotesCompMock.sol b/certora/munged/mocks/ERC20VotesCompMock.sol new file mode 100644 index 000000000..171071fd5 --- /dev/null +++ b/certora/munged/mocks/ERC20VotesCompMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20VotesComp.sol"; + +contract ERC20VotesCompMock is ERC20VotesComp { + constructor(string memory name, string memory symbol) ERC20(name, symbol) ERC20Permit(name) {} + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/certora/munged/mocks/ERC20VotesMock.sol b/certora/munged/mocks/ERC20VotesMock.sol new file mode 100644 index 000000000..0975e8b9f --- /dev/null +++ b/certora/munged/mocks/ERC20VotesMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Votes.sol"; + +contract ERC20VotesMock is ERC20Votes { + constructor(string memory name, string memory symbol) ERC20(name, symbol) ERC20Permit(name) {} + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/certora/munged/mocks/ERC20WrapperMock.sol b/certora/munged/mocks/ERC20WrapperMock.sol new file mode 100644 index 000000000..cf34a7a52 --- /dev/null +++ b/certora/munged/mocks/ERC20WrapperMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Wrapper.sol"; + +contract ERC20WrapperMock is ERC20Wrapper { + constructor( + IERC20 _underlyingToken, + string memory name, + string memory symbol + ) ERC20(name, symbol) ERC20Wrapper(_underlyingToken) {} + + function recover(address account) public returns (uint256) { + return _recover(account); + } +} diff --git a/certora/munged/mocks/ERC2771ContextMock.sol b/certora/munged/mocks/ERC2771ContextMock.sol new file mode 100644 index 000000000..7bc1c4538 --- /dev/null +++ b/certora/munged/mocks/ERC2771ContextMock.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ContextMock.sol"; +import "../metatx/ERC2771Context.sol"; + +// By inheriting from ERC2771Context, Context's internal functions are overridden automatically +contract ERC2771ContextMock is ContextMock, ERC2771Context { + constructor(address trustedForwarder) ERC2771Context(trustedForwarder) {} + + function _msgSender() internal view virtual override(Context, ERC2771Context) returns (address) { + return ERC2771Context._msgSender(); + } + + function _msgData() internal view virtual override(Context, ERC2771Context) returns (bytes calldata) { + return ERC2771Context._msgData(); + } +} diff --git a/certora/munged/mocks/ERC3156FlashBorrowerMock.sol b/certora/munged/mocks/ERC3156FlashBorrowerMock.sol new file mode 100644 index 000000000..288a278fb --- /dev/null +++ b/certora/munged/mocks/ERC3156FlashBorrowerMock.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/IERC20.sol"; +import "../interfaces/IERC3156.sol"; +import "../utils/Address.sol"; + +/** + * @dev WARNING: this IERC3156FlashBorrower mock implementation is for testing purposes ONLY. + * Writing a secure flash lock borrower is not an easy task, and should be done with the utmost care. + * This is not an example of how it should be done, and no pattern present in this mock should be considered secure. + * Following best practices, always have your contract properly audited before using them to manipulate important funds on + * live networks. + */ +contract ERC3156FlashBorrowerMock is IERC3156FlashBorrower { + bytes32 internal constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); + + bool immutable _enableApprove; + bool immutable _enableReturn; + + event BalanceOf(address token, address account, uint256 value); + event TotalSupply(address token, uint256 value); + + constructor(bool enableReturn, bool enableApprove) { + _enableApprove = enableApprove; + _enableReturn = enableReturn; + } + + function onFlashLoan( + address, /*initiator*/ + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) public override returns (bytes32) { + require(msg.sender == token); + + emit BalanceOf(token, address(this), IERC20(token).balanceOf(address(this))); + emit TotalSupply(token, IERC20(token).totalSupply()); + + if (data.length > 0) { + // WARNING: This code is for testing purposes only! Do not use. + Address.functionCall(token, data); + } + + if (_enableApprove) { + IERC20(token).approve(token, amount + fee); + } + + return _enableReturn ? _RETURN_VALUE : bytes32(0); + } +} diff --git a/certora/munged/mocks/ERC721BurnableMock.sol b/certora/munged/mocks/ERC721BurnableMock.sol new file mode 100644 index 000000000..b30dbf53d --- /dev/null +++ b/certora/munged/mocks/ERC721BurnableMock.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Burnable.sol"; + +contract ERC721BurnableMock is ERC721Burnable { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) public { + _safeMint(to, tokenId, _data); + } +} diff --git a/certora/munged/mocks/ERC721EnumerableMock.sol b/certora/munged/mocks/ERC721EnumerableMock.sol new file mode 100644 index 000000000..73aee9d04 --- /dev/null +++ b/certora/munged/mocks/ERC721EnumerableMock.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Enumerable.sol"; + +/** + * @title ERC721Mock + * This mock just provides a public safeMint, mint, and burn functions for testing purposes + */ +contract ERC721EnumerableMock is ERC721Enumerable { + string private _baseTokenURI; + + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function _baseURI() internal view virtual override returns (string memory) { + return _baseTokenURI; + } + + function setBaseURI(string calldata newBaseTokenURI) public { + _baseTokenURI = newBaseTokenURI; + } + + function baseURI() public view returns (string memory) { + return _baseURI(); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/certora/munged/mocks/ERC721Mock.sol b/certora/munged/mocks/ERC721Mock.sol new file mode 100644 index 000000000..74a092334 --- /dev/null +++ b/certora/munged/mocks/ERC721Mock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/ERC721.sol"; + +/** + * @title ERC721Mock + * This mock just provides a public safeMint, mint, and burn functions for testing purposes + */ +contract ERC721Mock is ERC721 { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function baseURI() public view returns (string memory) { + return _baseURI(); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/certora/munged/mocks/ERC721PausableMock.sol b/certora/munged/mocks/ERC721PausableMock.sol new file mode 100644 index 000000000..8d8e818fb --- /dev/null +++ b/certora/munged/mocks/ERC721PausableMock.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Pausable.sol"; + +/** + * @title ERC721PausableMock + * This mock just provides a public mint, burn and exists functions for testing purposes + */ +contract ERC721PausableMock is ERC721Pausable { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/certora/munged/mocks/ERC721ReceiverMock.sol b/certora/munged/mocks/ERC721ReceiverMock.sol new file mode 100644 index 000000000..a4923bfd5 --- /dev/null +++ b/certora/munged/mocks/ERC721ReceiverMock.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/IERC721Receiver.sol"; + +contract ERC721ReceiverMock is IERC721Receiver { + enum Error { + None, + RevertWithMessage, + RevertWithoutMessage, + Panic + } + + bytes4 private immutable _retval; + Error private immutable _error; + + event Received(address operator, address from, uint256 tokenId, bytes data, uint256 gas); + + constructor(bytes4 retval, Error error) { + _retval = retval; + _error = error; + } + + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes memory data + ) public override returns (bytes4) { + if (_error == Error.RevertWithMessage) { + revert("ERC721ReceiverMock: reverting"); + } else if (_error == Error.RevertWithoutMessage) { + revert(); + } else if (_error == Error.Panic) { + uint256 a = uint256(0) / uint256(0); + a; + } + emit Received(operator, from, tokenId, data, gasleft()); + return _retval; + } +} diff --git a/certora/munged/mocks/ERC721URIStorageMock.sol b/certora/munged/mocks/ERC721URIStorageMock.sol new file mode 100644 index 000000000..9c3480f71 --- /dev/null +++ b/certora/munged/mocks/ERC721URIStorageMock.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721URIStorage.sol"; + +/** + * @title ERC721Mock + * This mock just provides a public safeMint, mint, and burn functions for testing purposes + */ +contract ERC721URIStorageMock is ERC721URIStorage { + string private _baseTokenURI; + + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function _baseURI() internal view virtual override returns (string memory) { + return _baseTokenURI; + } + + function setBaseURI(string calldata newBaseTokenURI) public { + _baseTokenURI = newBaseTokenURI; + } + + function baseURI() public view returns (string memory) { + return _baseURI(); + } + + function setTokenURI(uint256 tokenId, string memory _tokenURI) public { + _setTokenURI(tokenId, _tokenURI); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/certora/munged/mocks/ERC777Mock.sol b/certora/munged/mocks/ERC777Mock.sol new file mode 100644 index 000000000..f8a3b6784 --- /dev/null +++ b/certora/munged/mocks/ERC777Mock.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; +import "../token/ERC777/ERC777.sol"; + +contract ERC777Mock is Context, ERC777 { + event BeforeTokenTransfer(); + + constructor( + address initialHolder, + uint256 initialBalance, + string memory name, + string memory symbol, + address[] memory defaultOperators + ) ERC777(name, symbol, defaultOperators) { + _mint(initialHolder, initialBalance, "", ""); + } + + function mintInternal( + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData + ) public { + _mint(to, amount, userData, operatorData); + } + + function mintInternalExtended( + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) public { + _mint(to, amount, userData, operatorData, requireReceptionAck); + } + + function approveInternal( + address holder, + address spender, + uint256 value + ) public { + _approve(holder, spender, value); + } + + function _beforeTokenTransfer( + address, + address, + address, + uint256 + ) internal override { + emit BeforeTokenTransfer(); + } +} diff --git a/certora/munged/mocks/ERC777SenderRecipientMock.sol b/certora/munged/mocks/ERC777SenderRecipientMock.sol new file mode 100644 index 000000000..169912f69 --- /dev/null +++ b/certora/munged/mocks/ERC777SenderRecipientMock.sol @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777.sol"; +import "../token/ERC777/IERC777Sender.sol"; +import "../token/ERC777/IERC777Recipient.sol"; +import "../utils/Context.sol"; +import "../utils/introspection/IERC1820Registry.sol"; +import "../utils/introspection/ERC1820Implementer.sol"; + +contract ERC777SenderRecipientMock is Context, IERC777Sender, IERC777Recipient, ERC1820Implementer { + event TokensToSendCalled( + address operator, + address from, + address to, + uint256 amount, + bytes data, + bytes operatorData, + address token, + uint256 fromBalance, + uint256 toBalance + ); + + event TokensReceivedCalled( + address operator, + address from, + address to, + uint256 amount, + bytes data, + bytes operatorData, + address token, + uint256 fromBalance, + uint256 toBalance + ); + + // Emitted in ERC777Mock. Here for easier decoding + event BeforeTokenTransfer(); + + bool private _shouldRevertSend; + bool private _shouldRevertReceive; + + IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + + bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); + bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + + function tokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external override { + if (_shouldRevertSend) { + revert(); + } + + IERC777 token = IERC777(_msgSender()); + + uint256 fromBalance = token.balanceOf(from); + // when called due to burn, to will be the zero address, which will have a balance of 0 + uint256 toBalance = token.balanceOf(to); + + emit TokensToSendCalled( + operator, + from, + to, + amount, + userData, + operatorData, + address(token), + fromBalance, + toBalance + ); + } + + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external override { + if (_shouldRevertReceive) { + revert(); + } + + IERC777 token = IERC777(_msgSender()); + + uint256 fromBalance = token.balanceOf(from); + // when called due to burn, to will be the zero address, which will have a balance of 0 + uint256 toBalance = token.balanceOf(to); + + emit TokensReceivedCalled( + operator, + from, + to, + amount, + userData, + operatorData, + address(token), + fromBalance, + toBalance + ); + } + + function senderFor(address account) public { + _registerInterfaceForAddress(_TOKENS_SENDER_INTERFACE_HASH, account); + + address self = address(this); + if (account == self) { + registerSender(self); + } + } + + function registerSender(address sender) public { + _erc1820.setInterfaceImplementer(address(this), _TOKENS_SENDER_INTERFACE_HASH, sender); + } + + function recipientFor(address account) public { + _registerInterfaceForAddress(_TOKENS_RECIPIENT_INTERFACE_HASH, account); + + address self = address(this); + if (account == self) { + registerRecipient(self); + } + } + + function registerRecipient(address recipient) public { + _erc1820.setInterfaceImplementer(address(this), _TOKENS_RECIPIENT_INTERFACE_HASH, recipient); + } + + function setShouldRevertSend(bool shouldRevert) public { + _shouldRevertSend = shouldRevert; + } + + function setShouldRevertReceive(bool shouldRevert) public { + _shouldRevertReceive = shouldRevert; + } + + function send( + IERC777 token, + address to, + uint256 amount, + bytes memory data + ) public { + // This is 777's send function, not the Solidity send function + token.send(to, amount, data); // solhint-disable-line check-send-result + } + + function burn( + IERC777 token, + uint256 amount, + bytes memory data + ) public { + token.burn(amount, data); + } +} diff --git a/certora/munged/mocks/EnumerableMapMock.sol b/certora/munged/mocks/EnumerableMapMock.sol new file mode 100644 index 000000000..510647b58 --- /dev/null +++ b/certora/munged/mocks/EnumerableMapMock.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/structs/EnumerableMap.sol"; + +contract EnumerableMapMock { + using EnumerableMap for EnumerableMap.UintToAddressMap; + + event OperationResult(bool result); + + EnumerableMap.UintToAddressMap private _map; + + function contains(uint256 key) public view returns (bool) { + return _map.contains(key); + } + + function set(uint256 key, address value) public { + bool result = _map.set(key, value); + emit OperationResult(result); + } + + function remove(uint256 key) public { + bool result = _map.remove(key); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _map.length(); + } + + function at(uint256 index) public view returns (uint256 key, address value) { + return _map.at(index); + } + + function tryGet(uint256 key) public view returns (bool, address) { + return _map.tryGet(key); + } + + function get(uint256 key) public view returns (address) { + return _map.get(key); + } + + function getWithMessage(uint256 key, string calldata errorMessage) public view returns (address) { + return _map.get(key, errorMessage); + } +} diff --git a/certora/munged/mocks/EnumerableSetMock.sol b/certora/munged/mocks/EnumerableSetMock.sol new file mode 100644 index 000000000..922ce46d2 --- /dev/null +++ b/certora/munged/mocks/EnumerableSetMock.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/structs/EnumerableSet.sol"; + +// Bytes32Set +contract EnumerableBytes32SetMock { + using EnumerableSet for EnumerableSet.Bytes32Set; + + event OperationResult(bool result); + + EnumerableSet.Bytes32Set private _set; + + function contains(bytes32 value) public view returns (bool) { + return _set.contains(value); + } + + function add(bytes32 value) public { + bool result = _set.add(value); + emit OperationResult(result); + } + + function remove(bytes32 value) public { + bool result = _set.remove(value); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _set.length(); + } + + function at(uint256 index) public view returns (bytes32) { + return _set.at(index); + } + + function values() public view returns (bytes32[] memory) { + return _set.values(); + } +} + +// AddressSet +contract EnumerableAddressSetMock { + using EnumerableSet for EnumerableSet.AddressSet; + + event OperationResult(bool result); + + EnumerableSet.AddressSet private _set; + + function contains(address value) public view returns (bool) { + return _set.contains(value); + } + + function add(address value) public { + bool result = _set.add(value); + emit OperationResult(result); + } + + function remove(address value) public { + bool result = _set.remove(value); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _set.length(); + } + + function at(uint256 index) public view returns (address) { + return _set.at(index); + } + + function values() public view returns (address[] memory) { + return _set.values(); + } +} + +// UintSet +contract EnumerableUintSetMock { + using EnumerableSet for EnumerableSet.UintSet; + + event OperationResult(bool result); + + EnumerableSet.UintSet private _set; + + function contains(uint256 value) public view returns (bool) { + return _set.contains(value); + } + + function add(uint256 value) public { + bool result = _set.add(value); + emit OperationResult(result); + } + + function remove(uint256 value) public { + bool result = _set.remove(value); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _set.length(); + } + + function at(uint256 index) public view returns (uint256) { + return _set.at(index); + } + + function values() public view returns (uint256[] memory) { + return _set.values(); + } +} diff --git a/certora/munged/mocks/EtherReceiverMock.sol b/certora/munged/mocks/EtherReceiverMock.sol new file mode 100644 index 000000000..a11e646fb --- /dev/null +++ b/certora/munged/mocks/EtherReceiverMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract EtherReceiverMock { + bool private _acceptEther; + + function setAcceptEther(bool acceptEther) public { + _acceptEther = acceptEther; + } + + receive() external payable { + if (!_acceptEther) { + revert(); + } + } +} diff --git a/certora/munged/mocks/GovernorCompMock.sol b/certora/munged/mocks/GovernorCompMock.sol new file mode 100644 index 000000000..9dcbc536d --- /dev/null +++ b/certora/munged/mocks/GovernorCompMock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesComp.sol"; + +contract GovernorCompMock is GovernorVotesComp, GovernorCountingSimple { + constructor(string memory name_, ERC20VotesComp token_) Governor(name_) GovernorVotesComp(token_) {} + + function quorum(uint256) public pure override returns (uint256) { + return 0; + } + + function votingDelay() public pure override returns (uint256) { + return 4; + } + + function votingPeriod() public pure override returns (uint256) { + return 16; + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } + + function getVotes(address account, uint256 blockNumber) + public + view + virtual + override(IGovernor, GovernorVotesComp) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } +} diff --git a/certora/munged/mocks/GovernorCompatibilityBravoMock.sol b/certora/munged/mocks/GovernorCompatibilityBravoMock.sol new file mode 100644 index 000000000..60afbb918 --- /dev/null +++ b/certora/munged/mocks/GovernorCompatibilityBravoMock.sol @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/compatibility/GovernorCompatibilityBravo.sol"; +import "../governance/extensions/GovernorTimelockCompound.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorVotesComp.sol"; + +contract GovernorCompatibilityBravoMock is + GovernorCompatibilityBravo, + GovernorSettings, + GovernorTimelockCompound, + GovernorVotesComp +{ + constructor( + string memory name_, + ERC20VotesComp token_, + uint256 votingDelay_, + uint256 votingPeriod_, + uint256 proposalThreshold_, + ICompoundTimelock timelock_ + ) + Governor(name_) + GovernorTimelockCompound(timelock_) + GovernorSettings(votingDelay_, votingPeriod_, proposalThreshold_) + GovernorVotesComp(token_) + {} + + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(IERC165, Governor, GovernorTimelockCompound) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function quorum(uint256) public pure override returns (uint256) { + return 0; + } + + function state(uint256 proposalId) + public + view + virtual + override(IGovernor, Governor, GovernorTimelockCompound) + returns (ProposalState) + { + return super.state(proposalId); + } + + function proposalEta(uint256 proposalId) + public + view + virtual + override(IGovernorTimelock, GovernorTimelockCompound) + returns (uint256) + { + return super.proposalEta(proposalId); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override(IGovernor, Governor, GovernorCompatibilityBravo) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public virtual override(IGovernorTimelock, GovernorTimelockCompound) returns (uint256) { + return super.queue(targets, values, calldatas, salt); + } + + function execute( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public payable virtual override(IGovernor, Governor) returns (uint256) { + return super.execute(targets, values, calldatas, salt); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override(Governor, GovernorTimelockCompound) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + /** + * @notice WARNING: this is for mock purposes only. Ability to the _cancel function should be restricted for live + * deployments. + */ + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) internal virtual override(Governor, GovernorTimelockCompound) returns (uint256 proposalId) { + return super._cancel(targets, values, calldatas, salt); + } + + function getVotes(address account, uint256 blockNumber) + public + view + virtual + override(IGovernor, GovernorVotesComp) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function _executor() internal view virtual override(Governor, GovernorTimelockCompound) returns (address) { + return super._executor(); + } +} diff --git a/certora/munged/mocks/GovernorMock.sol b/certora/munged/mocks/GovernorMock.sol new file mode 100644 index 000000000..cc96dcd27 --- /dev/null +++ b/certora/munged/mocks/GovernorMock.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorProposalThreshold.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesQuorumFraction.sol"; + +contract GovernorMock is + GovernorProposalThreshold, + GovernorSettings, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + string memory name_, + ERC20Votes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + uint256 quorumNumerator_ + ) + Governor(name_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorVotesQuorumFraction(quorumNumerator_) + {} + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } + + function getVotes(address account, uint256 blockNumber) + public + view + virtual + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override(Governor, GovernorProposalThreshold) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } +} diff --git a/certora/munged/mocks/GovernorTimelockCompoundMock.sol b/certora/munged/mocks/GovernorTimelockCompoundMock.sol new file mode 100644 index 000000000..848f4b409 --- /dev/null +++ b/certora/munged/mocks/GovernorTimelockCompoundMock.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorTimelockCompound.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesQuorumFraction.sol"; + +contract GovernorTimelockCompoundMock is + GovernorSettings, + GovernorTimelockCompound, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + string memory name_, + ERC20Votes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + ICompoundTimelock timelock_, + uint256 quorumNumerator_ + ) + Governor(name_) + GovernorTimelockCompound(timelock_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorVotesQuorumFraction(quorumNumerator_) + {} + + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(Governor, GovernorTimelockCompound) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } + + /** + * Overriding nightmare + */ + function state(uint256 proposalId) + public + view + virtual + override(Governor, GovernorTimelockCompound) + returns (ProposalState) + { + return super.state(proposalId); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override(Governor, GovernorTimelockCompound) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) internal virtual override(Governor, GovernorTimelockCompound) returns (uint256 proposalId) { + return super._cancel(targets, values, calldatas, salt); + } + + function getVotes(address account, uint256 blockNumber) + public + view + virtual + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function _executor() internal view virtual override(Governor, GovernorTimelockCompound) returns (address) { + return super._executor(); + } +} diff --git a/certora/munged/mocks/GovernorTimelockControlMock.sol b/certora/munged/mocks/GovernorTimelockControlMock.sol new file mode 100644 index 000000000..4d9e97fd5 --- /dev/null +++ b/certora/munged/mocks/GovernorTimelockControlMock.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorTimelockControl.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesQuorumFraction.sol"; + +contract GovernorTimelockControlMock is + GovernorSettings, + GovernorTimelockControl, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + string memory name_, + ERC20Votes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + TimelockController timelock_, + uint256 quorumNumerator_ + ) + Governor(name_) + GovernorTimelockControl(timelock_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorVotesQuorumFraction(quorumNumerator_) + {} + + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(Governor, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, descriptionHash); + } + + /** + * Overriding nightmare + */ + function state(uint256 proposalId) + public + view + virtual + override(Governor, GovernorTimelockControl) + returns (ProposalState) + { + return super.state(proposalId); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override(Governor, GovernorTimelockControl) returns (uint256 proposalId) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function getVotes(address account, uint256 blockNumber) + public + view + virtual + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function _executor() internal view virtual override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } +} diff --git a/certora/munged/mocks/InitializableMock.sol b/certora/munged/mocks/InitializableMock.sol new file mode 100644 index 000000000..0d3e77dfa --- /dev/null +++ b/certora/munged/mocks/InitializableMock.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +/** + * @title InitializableMock + * @dev This contract is a mock to test initializable functionality + */ +contract InitializableMock is Initializable { + bool public initializerRan; + uint256 public x; + + function initialize() public initializer { + initializerRan = true; + } + + function initializeNested() public initializer { + initialize(); + } + + function initializeWithX(uint256 _x) public payable initializer { + x = _x; + } + + function nonInitializable(uint256 _x) public payable { + x = _x; + } + + function fail() public pure { + require(false, "InitializableMock forced failure"); + } +} diff --git a/certora/munged/mocks/MathMock.sol b/certora/munged/mocks/MathMock.sol new file mode 100644 index 000000000..c651b6bb1 --- /dev/null +++ b/certora/munged/mocks/MathMock.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/Math.sol"; + +contract MathMock { + function max(uint256 a, uint256 b) public pure returns (uint256) { + return Math.max(a, b); + } + + function min(uint256 a, uint256 b) public pure returns (uint256) { + return Math.min(a, b); + } + + function average(uint256 a, uint256 b) public pure returns (uint256) { + return Math.average(a, b); + } + + function ceilDiv(uint256 a, uint256 b) public pure returns (uint256) { + return Math.ceilDiv(a, b); + } +} diff --git a/certora/munged/mocks/MerkleProofWrapper.sol b/certora/munged/mocks/MerkleProofWrapper.sol new file mode 100644 index 000000000..1e188df36 --- /dev/null +++ b/certora/munged/mocks/MerkleProofWrapper.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/MerkleProof.sol"; + +contract MerkleProofWrapper { + function verify( + bytes32[] memory proof, + bytes32 root, + bytes32 leaf + ) public pure returns (bool) { + return MerkleProof.verify(proof, root, leaf); + } + + function processProof(bytes32[] memory proof, bytes32 leaf) public pure returns (bytes32) { + return MerkleProof.processProof(proof, leaf); + } +} diff --git a/certora/munged/mocks/MulticallTest.sol b/certora/munged/mocks/MulticallTest.sol new file mode 100644 index 000000000..f1a3a9cfe --- /dev/null +++ b/certora/munged/mocks/MulticallTest.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./MulticallTokenMock.sol"; + +contract MulticallTest { + function testReturnValues( + MulticallTokenMock multicallToken, + address[] calldata recipients, + uint256[] calldata amounts + ) external { + bytes[] memory calls = new bytes[](recipients.length); + for (uint256 i = 0; i < recipients.length; i++) { + calls[i] = abi.encodeWithSignature("transfer(address,uint256)", recipients[i], amounts[i]); + } + + bytes[] memory results = multicallToken.multicall(calls); + for (uint256 i = 0; i < results.length; i++) { + require(abi.decode(results[i], (bool))); + } + } +} diff --git a/certora/munged/mocks/MulticallTokenMock.sol b/certora/munged/mocks/MulticallTokenMock.sol new file mode 100644 index 000000000..de379681b --- /dev/null +++ b/certora/munged/mocks/MulticallTokenMock.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Multicall.sol"; +import "./ERC20Mock.sol"; + +contract MulticallTokenMock is ERC20Mock, Multicall { + constructor(uint256 initialBalance) ERC20Mock("MulticallToken", "BCT", msg.sender, initialBalance) {} +} diff --git a/certora/munged/mocks/MultipleInheritanceInitializableMocks.sol b/certora/munged/mocks/MultipleInheritanceInitializableMocks.sol new file mode 100644 index 000000000..1a008e8d8 --- /dev/null +++ b/certora/munged/mocks/MultipleInheritanceInitializableMocks.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +// Sample contracts showing upgradeability with multiple inheritance. +// Child contract inherits from Father and Mother contracts, and Father extends from Gramps. +// +// Human +// / \ +// | Gramps +// | | +// Mother Father +// | | +// -- Child -- + +/** + * Sample base intializable contract that is a human + */ +contract SampleHuman is Initializable { + bool public isHuman; + + function initialize() public initializer { + isHuman = true; + } +} + +/** + * Sample base intializable contract that defines a field mother + */ +contract SampleMother is Initializable, SampleHuman { + uint256 public mother; + + function initialize(uint256 value) public virtual initializer { + SampleHuman.initialize(); + mother = value; + } +} + +/** + * Sample base intializable contract that defines a field gramps + */ +contract SampleGramps is Initializable, SampleHuman { + string public gramps; + + function initialize(string memory value) public virtual initializer { + SampleHuman.initialize(); + gramps = value; + } +} + +/** + * Sample base intializable contract that defines a field father and extends from gramps + */ +contract SampleFather is Initializable, SampleGramps { + uint256 public father; + + function initialize(string memory _gramps, uint256 _father) public initializer { + SampleGramps.initialize(_gramps); + father = _father; + } +} + +/** + * Child extends from mother, father (gramps) + */ +contract SampleChild is Initializable, SampleMother, SampleFather { + uint256 public child; + + function initialize( + uint256 _mother, + string memory _gramps, + uint256 _father, + uint256 _child + ) public initializer { + SampleMother.initialize(_mother); + SampleFather.initialize(_gramps, _father); + child = _child; + } +} diff --git a/certora/munged/mocks/OwnableMock.sol b/certora/munged/mocks/OwnableMock.sol new file mode 100644 index 000000000..d60f1c40d --- /dev/null +++ b/certora/munged/mocks/OwnableMock.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/Ownable.sol"; + +contract OwnableMock is Ownable {} diff --git a/certora/munged/mocks/PausableMock.sol b/certora/munged/mocks/PausableMock.sol new file mode 100644 index 000000000..98bcfd593 --- /dev/null +++ b/certora/munged/mocks/PausableMock.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../security/Pausable.sol"; + +contract PausableMock is Pausable { + bool public drasticMeasureTaken; + uint256 public count; + + constructor() { + drasticMeasureTaken = false; + count = 0; + } + + function normalProcess() external whenNotPaused { + count++; + } + + function drasticMeasure() external whenPaused { + drasticMeasureTaken = true; + } + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } +} diff --git a/certora/munged/mocks/PullPaymentMock.sol b/certora/munged/mocks/PullPaymentMock.sol new file mode 100644 index 000000000..8a708e30c --- /dev/null +++ b/certora/munged/mocks/PullPaymentMock.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../security/PullPayment.sol"; + +// mock class using PullPayment +contract PullPaymentMock is PullPayment { + constructor() payable {} + + // test helper function to call asyncTransfer + function callTransfer(address dest, uint256 amount) public { + _asyncTransfer(dest, amount); + } +} diff --git a/certora/munged/mocks/ReentrancyAttack.sol b/certora/munged/mocks/ReentrancyAttack.sol new file mode 100644 index 000000000..4de181205 --- /dev/null +++ b/certora/munged/mocks/ReentrancyAttack.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +contract ReentrancyAttack is Context { + function callSender(bytes4 data) public { + (bool success, ) = _msgSender().call(abi.encodeWithSelector(data)); + require(success, "ReentrancyAttack: failed call"); + } +} diff --git a/certora/munged/mocks/ReentrancyMock.sol b/certora/munged/mocks/ReentrancyMock.sol new file mode 100644 index 000000000..43425dd6e --- /dev/null +++ b/certora/munged/mocks/ReentrancyMock.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../security/ReentrancyGuard.sol"; +import "./ReentrancyAttack.sol"; + +contract ReentrancyMock is ReentrancyGuard { + uint256 public counter; + + constructor() { + counter = 0; + } + + function callback() external nonReentrant { + _count(); + } + + function countLocalRecursive(uint256 n) public nonReentrant { + if (n > 0) { + _count(); + countLocalRecursive(n - 1); + } + } + + function countThisRecursive(uint256 n) public nonReentrant { + if (n > 0) { + _count(); + (bool success, ) = address(this).call(abi.encodeWithSignature("countThisRecursive(uint256)", n - 1)); + require(success, "ReentrancyMock: failed call"); + } + } + + function countAndCall(ReentrancyAttack attacker) public nonReentrant { + _count(); + bytes4 func = bytes4(keccak256("callback()")); + attacker.callSender(func); + } + + function _count() private { + counter += 1; + } +} diff --git a/certora/munged/mocks/RegressionImplementation.sol b/certora/munged/mocks/RegressionImplementation.sol new file mode 100644 index 000000000..be6b501c1 --- /dev/null +++ b/certora/munged/mocks/RegressionImplementation.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +contract Implementation1 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } +} + +contract Implementation2 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue() public view returns (uint256) { + return _value; + } +} + +contract Implementation3 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue(uint256 _number) public view returns (uint256) { + return _value + _number; + } +} + +contract Implementation4 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue() public view returns (uint256) { + return _value; + } + + fallback() external { + _value = 1; + } +} diff --git a/certora/munged/mocks/SafeCastMock.sol b/certora/munged/mocks/SafeCastMock.sol new file mode 100644 index 000000000..d1f1aaaba --- /dev/null +++ b/certora/munged/mocks/SafeCastMock.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SafeCast.sol"; + +contract SafeCastMock { + using SafeCast for uint256; + using SafeCast for int256; + + function toUint256(int256 a) public pure returns (uint256) { + return a.toUint256(); + } + + function toUint224(uint256 a) public pure returns (uint224) { + return a.toUint224(); + } + + function toUint128(uint256 a) public pure returns (uint128) { + return a.toUint128(); + } + + function toUint96(uint256 a) public pure returns (uint96) { + return a.toUint96(); + } + + function toUint64(uint256 a) public pure returns (uint64) { + return a.toUint64(); + } + + function toUint32(uint256 a) public pure returns (uint32) { + return a.toUint32(); + } + + function toUint16(uint256 a) public pure returns (uint16) { + return a.toUint16(); + } + + function toUint8(uint256 a) public pure returns (uint8) { + return a.toUint8(); + } + + function toInt256(uint256 a) public pure returns (int256) { + return a.toInt256(); + } + + function toInt128(int256 a) public pure returns (int128) { + return a.toInt128(); + } + + function toInt64(int256 a) public pure returns (int64) { + return a.toInt64(); + } + + function toInt32(int256 a) public pure returns (int32) { + return a.toInt32(); + } + + function toInt16(int256 a) public pure returns (int16) { + return a.toInt16(); + } + + function toInt8(int256 a) public pure returns (int8) { + return a.toInt8(); + } +} diff --git a/certora/munged/mocks/SafeERC20Helper.sol b/certora/munged/mocks/SafeERC20Helper.sol new file mode 100644 index 000000000..9e3442b35 --- /dev/null +++ b/certora/munged/mocks/SafeERC20Helper.sol @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; +import "../token/ERC20/IERC20.sol"; +import "../token/ERC20/utils/SafeERC20.sol"; + +contract ERC20ReturnFalseMock is Context { + uint256 private _allowance; + + // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, + // we write to a dummy state variable. + uint256 private _dummy; + + function transfer(address, uint256) public returns (bool) { + _dummy = 0; + return false; + } + + function transferFrom( + address, + address, + uint256 + ) public returns (bool) { + _dummy = 0; + return false; + } + + function approve(address, uint256) public returns (bool) { + _dummy = 0; + return false; + } + + function allowance(address, address) public view returns (uint256) { + require(_dummy == 0); // Duummy read from a state variable so that the function is view + return 0; + } +} + +contract ERC20ReturnTrueMock is Context { + mapping(address => uint256) private _allowances; + + // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, + // we write to a dummy state variable. + uint256 private _dummy; + + function transfer(address, uint256) public returns (bool) { + _dummy = 0; + return true; + } + + function transferFrom( + address, + address, + uint256 + ) public returns (bool) { + _dummy = 0; + return true; + } + + function approve(address, uint256) public returns (bool) { + _dummy = 0; + return true; + } + + function setAllowance(uint256 allowance_) public { + _allowances[_msgSender()] = allowance_; + } + + function allowance(address owner, address) public view returns (uint256) { + return _allowances[owner]; + } +} + +contract ERC20NoReturnMock is Context { + mapping(address => uint256) private _allowances; + + // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, + // we write to a dummy state variable. + uint256 private _dummy; + + function transfer(address, uint256) public { + _dummy = 0; + } + + function transferFrom( + address, + address, + uint256 + ) public { + _dummy = 0; + } + + function approve(address, uint256) public { + _dummy = 0; + } + + function setAllowance(uint256 allowance_) public { + _allowances[_msgSender()] = allowance_; + } + + function allowance(address owner, address) public view returns (uint256) { + return _allowances[owner]; + } +} + +contract SafeERC20Wrapper is Context { + using SafeERC20 for IERC20; + + IERC20 private _token; + + constructor(IERC20 token) { + _token = token; + } + + function transfer() public { + _token.safeTransfer(address(0), 0); + } + + function transferFrom() public { + _token.safeTransferFrom(address(0), address(0), 0); + } + + function approve(uint256 amount) public { + _token.safeApprove(address(0), amount); + } + + function increaseAllowance(uint256 amount) public { + _token.safeIncreaseAllowance(address(0), amount); + } + + function decreaseAllowance(uint256 amount) public { + _token.safeDecreaseAllowance(address(0), amount); + } + + function setAllowance(uint256 allowance_) public { + ERC20ReturnTrueMock(address(_token)).setAllowance(allowance_); + } + + function allowance() public view returns (uint256) { + return _token.allowance(address(0), address(0)); + } +} diff --git a/certora/munged/mocks/SafeMathMock.sol b/certora/munged/mocks/SafeMathMock.sol new file mode 100644 index 000000000..3d1f4727e --- /dev/null +++ b/certora/munged/mocks/SafeMathMock.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SafeMath.sol"; + +contract SafeMathMock { + function tryAdd(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryAdd(a, b); + } + + function trySub(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.trySub(a, b); + } + + function tryMul(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryMul(a, b); + } + + function tryDiv(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryDiv(a, b); + } + + function tryMod(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryMod(a, b); + } + + // using the do* naming convention to avoid warnings due to clashing opcode names + + function doAdd(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.add(a, b); + } + + function doSub(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.sub(a, b); + } + + function doMul(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.mul(a, b); + } + + function doDiv(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.div(a, b); + } + + function doMod(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.mod(a, b); + } + + function subWithMessage( + uint256 a, + uint256 b, + string memory errorMessage + ) public pure returns (uint256) { + return SafeMath.sub(a, b, errorMessage); + } + + function divWithMessage( + uint256 a, + uint256 b, + string memory errorMessage + ) public pure returns (uint256) { + return SafeMath.div(a, b, errorMessage); + } + + function modWithMessage( + uint256 a, + uint256 b, + string memory errorMessage + ) public pure returns (uint256) { + return SafeMath.mod(a, b, errorMessage); + } + + function addMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.add(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function subMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.sub(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function mulMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.mul(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function divMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.div(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function modMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.mod(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } +} diff --git a/certora/munged/mocks/SignatureCheckerMock.sol b/certora/munged/mocks/SignatureCheckerMock.sol new file mode 100644 index 000000000..3b399c1ae --- /dev/null +++ b/certora/munged/mocks/SignatureCheckerMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/SignatureChecker.sol"; + +contract SignatureCheckerMock { + using SignatureChecker for address; + + function isValidSignatureNow( + address signer, + bytes32 hash, + bytes memory signature + ) public view returns (bool) { + return signer.isValidSignatureNow(hash, signature); + } +} diff --git a/certora/munged/mocks/SignedSafeMathMock.sol b/certora/munged/mocks/SignedSafeMathMock.sol new file mode 100644 index 000000000..8d1021798 --- /dev/null +++ b/certora/munged/mocks/SignedSafeMathMock.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SignedSafeMath.sol"; + +contract SignedSafeMathMock { + function mul(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.mul(a, b); + } + + function div(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.div(a, b); + } + + function sub(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.sub(a, b); + } + + function add(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.add(a, b); + } +} diff --git a/certora/munged/mocks/SingleInheritanceInitializableMocks.sol b/certora/munged/mocks/SingleInheritanceInitializableMocks.sol new file mode 100644 index 000000000..6c82dd20c --- /dev/null +++ b/certora/munged/mocks/SingleInheritanceInitializableMocks.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +/** + * @title MigratableMockV1 + * @dev This contract is a mock to test initializable functionality through migrations + */ +contract MigratableMockV1 is Initializable { + uint256 public x; + + function initialize(uint256 value) public payable initializer { + x = value; + } +} + +/** + * @title MigratableMockV2 + * @dev This contract is a mock to test migratable functionality with params + */ +contract MigratableMockV2 is MigratableMockV1 { + bool internal _migratedV2; + uint256 public y; + + function migrate(uint256 value, uint256 anotherValue) public payable { + require(!_migratedV2); + x = value; + y = anotherValue; + _migratedV2 = true; + } +} + +/** + * @title MigratableMockV3 + * @dev This contract is a mock to test migratable functionality without params + */ +contract MigratableMockV3 is MigratableMockV2 { + bool internal _migratedV3; + + function migrate() public payable { + require(!_migratedV3); + uint256 oldX = x; + x = y; + y = oldX; + _migratedV3 = true; + } +} diff --git a/certora/munged/mocks/StorageSlotMock.sol b/certora/munged/mocks/StorageSlotMock.sol new file mode 100644 index 000000000..5d099fca8 --- /dev/null +++ b/certora/munged/mocks/StorageSlotMock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/StorageSlot.sol"; + +contract StorageSlotMock { + using StorageSlot for bytes32; + + function setBoolean(bytes32 slot, bool value) public { + slot.getBooleanSlot().value = value; + } + + function setAddress(bytes32 slot, address value) public { + slot.getAddressSlot().value = value; + } + + function setBytes32(bytes32 slot, bytes32 value) public { + slot.getBytes32Slot().value = value; + } + + function setUint256(bytes32 slot, uint256 value) public { + slot.getUint256Slot().value = value; + } + + function getBoolean(bytes32 slot) public view returns (bool) { + return slot.getBooleanSlot().value; + } + + function getAddress(bytes32 slot) public view returns (address) { + return slot.getAddressSlot().value; + } + + function getBytes32(bytes32 slot) public view returns (bytes32) { + return slot.getBytes32Slot().value; + } + + function getUint256(bytes32 slot) public view returns (uint256) { + return slot.getUint256Slot().value; + } +} diff --git a/certora/munged/mocks/StringsMock.sol b/certora/munged/mocks/StringsMock.sol new file mode 100644 index 000000000..f257734e7 --- /dev/null +++ b/certora/munged/mocks/StringsMock.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Strings.sol"; + +contract StringsMock { + function fromUint256(uint256 value) public pure returns (string memory) { + return Strings.toString(value); + } + + function fromUint256Hex(uint256 value) public pure returns (string memory) { + return Strings.toHexString(value); + } + + function fromUint256HexFixed(uint256 value, uint256 length) public pure returns (string memory) { + return Strings.toHexString(value, length); + } +} diff --git a/certora/munged/mocks/TimersBlockNumberImpl.sol b/certora/munged/mocks/TimersBlockNumberImpl.sol new file mode 100644 index 000000000..84633e6f8 --- /dev/null +++ b/certora/munged/mocks/TimersBlockNumberImpl.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Timers.sol"; + +contract TimersBlockNumberImpl { + using Timers for Timers.BlockNumber; + + Timers.BlockNumber private _timer; + + function getDeadline() public view returns (uint64) { + return _timer.getDeadline(); + } + + function setDeadline(uint64 timestamp) public { + _timer.setDeadline(timestamp); + } + + function reset() public { + _timer.reset(); + } + + function isUnset() public view returns (bool) { + return _timer.isUnset(); + } + + function isStarted() public view returns (bool) { + return _timer.isStarted(); + } + + function isPending() public view returns (bool) { + return _timer.isPending(); + } + + function isExpired() public view returns (bool) { + return _timer.isExpired(); + } +} diff --git a/certora/munged/mocks/TimersTimestampImpl.sol b/certora/munged/mocks/TimersTimestampImpl.sol new file mode 100644 index 000000000..07f9a1b3f --- /dev/null +++ b/certora/munged/mocks/TimersTimestampImpl.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Timers.sol"; + +contract TimersTimestampImpl { + using Timers for Timers.Timestamp; + + Timers.Timestamp private _timer; + + function getDeadline() public view returns (uint64) { + return _timer.getDeadline(); + } + + function setDeadline(uint64 timestamp) public { + _timer.setDeadline(timestamp); + } + + function reset() public { + _timer.reset(); + } + + function isUnset() public view returns (bool) { + return _timer.isUnset(); + } + + function isStarted() public view returns (bool) { + return _timer.isStarted(); + } + + function isPending() public view returns (bool) { + return _timer.isPending(); + } + + function isExpired() public view returns (bool) { + return _timer.isExpired(); + } +} diff --git a/certora/munged/mocks/UUPS/TestInProd.sol b/certora/munged/mocks/UUPS/TestInProd.sol new file mode 100644 index 000000000..bbb610300 --- /dev/null +++ b/certora/munged/mocks/UUPS/TestInProd.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../CountersImpl.sol"; +import "../../proxy/utils/UUPSUpgradeable.sol"; + +contract UUPSUpgradeableMock is CountersImpl, UUPSUpgradeable { + // Not having any checks in this function is dangerous! Do not do this outside tests! + function _authorizeUpgrade(address) internal virtual override {} +} + +contract UUPSUpgradeableUnsafeMock is UUPSUpgradeableMock { + function upgradeTo(address newImplementation) external virtual override { + ERC1967Upgrade._upgradeToAndCall(newImplementation, bytes(""), false); + } + + function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual override { + ERC1967Upgrade._upgradeToAndCall(newImplementation, data, false); + } +} + +contract UUPSUpgradeableBrokenMock is UUPSUpgradeableMock { + function upgradeTo(address) external virtual override { + // pass + } + + function upgradeToAndCall(address, bytes memory) external payable virtual override { + // pass + } +} diff --git a/certora/munged/mocks/compound/CompTimelock.sol b/certora/munged/mocks/compound/CompTimelock.sol new file mode 100644 index 000000000..49ffa4b77 --- /dev/null +++ b/certora/munged/mocks/compound/CompTimelock.sol @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: BSD-3-Clause +// solhint-disable private-vars-leading-underscore +/** + * Copyright 2020 Compound Labs, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the + * following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +pragma solidity ^0.8.0; + +contract CompTimelock { + event NewAdmin(address indexed newAdmin); + event NewPendingAdmin(address indexed newPendingAdmin); + event NewDelay(uint256 indexed newDelay); + event CancelTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + event ExecuteTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + event QueueTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + + uint256 public constant GRACE_PERIOD = 14 days; + uint256 public constant MINIMUM_DELAY = 2 days; + uint256 public constant MAXIMUM_DELAY = 30 days; + + address public admin; + address public pendingAdmin; + uint256 public delay; + + mapping(bytes32 => bool) public queuedTransactions; + + constructor(address admin_, uint256 delay_) { + require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); + require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); + + admin = admin_; + delay = delay_; + } + + receive() external payable {} + + function setDelay(uint256 delay_) public { + require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); + require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); + require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); + delay = delay_; + + emit NewDelay(delay); + } + + function acceptAdmin() public { + require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); + admin = msg.sender; + pendingAdmin = address(0); + + emit NewAdmin(admin); + } + + function setPendingAdmin(address pendingAdmin_) public { + require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); + pendingAdmin = pendingAdmin_; + + emit NewPendingAdmin(pendingAdmin); + } + + function queueTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) public returns (bytes32) { + require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); + require( + eta >= getBlockTimestamp() + delay, + "Timelock::queueTransaction: Estimated execution block must satisfy delay." + ); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + queuedTransactions[txHash] = true; + + emit QueueTransaction(txHash, target, value, signature, data, eta); + return txHash; + } + + function cancelTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) public { + require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + queuedTransactions[txHash] = false; + + emit CancelTransaction(txHash, target, value, signature, data, eta); + } + + function executeTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) public payable returns (bytes memory) { + require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); + require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); + require(getBlockTimestamp() <= eta + GRACE_PERIOD, "Timelock::executeTransaction: Transaction is stale."); + + queuedTransactions[txHash] = false; + + bytes memory callData; + + if (bytes(signature).length == 0) { + callData = data; + } else { + callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); + } + + // solium-disable-next-line security/no-call-value + (bool success, bytes memory returnData) = target.call{value: value}(callData); + require(success, "Timelock::executeTransaction: Transaction execution reverted."); + + emit ExecuteTransaction(txHash, target, value, signature, data, eta); + + return returnData; + } + + function getBlockTimestamp() internal view returns (uint256) { + // solium-disable-next-line security/no-block-members + return block.timestamp; + } +} diff --git a/certora/munged/mocks/wizard/MyGovernor1.sol b/certora/munged/mocks/wizard/MyGovernor1.sol new file mode 100644 index 000000000..72b486aa7 --- /dev/null +++ b/certora/munged/mocks/wizard/MyGovernor1.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../governance/Governor.sol"; +import "../../governance/extensions/GovernorCountingSimple.sol"; +import "../../governance/extensions/GovernorVotes.sol"; +import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../governance/extensions/GovernorTimelockControl.sol"; + +contract MyGovernor1 is + Governor, + GovernorTimelockControl, + GovernorVotes, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor(ERC20Votes _token, TimelockController _timelock) + Governor("MyGovernor") + GovernorVotes(_token) + GovernorVotesQuorumFraction(4) + GovernorTimelockControl(_timelock) + {} + + function votingDelay() public pure override returns (uint256) { + return 1; // 1 block + } + + function votingPeriod() public pure override returns (uint256) { + return 45818; // 1 week + } + + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function getVotes(address account, uint256 blockNumber) + public + view + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/munged/mocks/wizard/MyGovernor2.sol b/certora/munged/mocks/wizard/MyGovernor2.sol new file mode 100644 index 000000000..3f25b91bf --- /dev/null +++ b/certora/munged/mocks/wizard/MyGovernor2.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../governance/Governor.sol"; +import "../../governance/extensions/GovernorProposalThreshold.sol"; +import "../../governance/extensions/GovernorCountingSimple.sol"; +import "../../governance/extensions/GovernorVotes.sol"; +import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../governance/extensions/GovernorTimelockControl.sol"; + +contract MyGovernor2 is + Governor, + GovernorTimelockControl, + GovernorProposalThreshold, + GovernorVotes, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor(ERC20Votes _token, TimelockController _timelock) + Governor("MyGovernor") + GovernorVotes(_token) + GovernorVotesQuorumFraction(4) + GovernorTimelockControl(_timelock) + {} + + function votingDelay() public pure override returns (uint256) { + return 1; // 1 block + } + + function votingPeriod() public pure override returns (uint256) { + return 45818; // 1 week + } + + function proposalThreshold() public pure override returns (uint256) { + return 1000e18; + } + + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function getVotes(address account, uint256 blockNumber) + public + view + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, GovernorProposalThreshold, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/munged/mocks/wizard/MyGovernor3.sol b/certora/munged/mocks/wizard/MyGovernor3.sol new file mode 100644 index 000000000..c2465751a --- /dev/null +++ b/certora/munged/mocks/wizard/MyGovernor3.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../governance/Governor.sol"; +import "../../governance/compatibility/GovernorCompatibilityBravo.sol"; +import "../../governance/extensions/GovernorVotes.sol"; +import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../governance/extensions/GovernorTimelockControl.sol"; + +contract MyGovernor is + Governor, + GovernorTimelockControl, + GovernorCompatibilityBravo, + GovernorVotes, + GovernorVotesQuorumFraction +{ + constructor(ERC20Votes _token, TimelockController _timelock) + Governor("MyGovernor") + GovernorVotes(_token) + GovernorVotesQuorumFraction(4) + GovernorTimelockControl(_timelock) + {} + + function votingDelay() public pure override returns (uint256) { + return 1; // 1 block + } + + function votingPeriod() public pure override returns (uint256) { + return 45818; // 1 week + } + + function proposalThreshold() public pure override returns (uint256) { + return 1000e18; + } + + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function getVotes(address account, uint256 blockNumber) + public + view + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function state(uint256 proposalId) + public + view + override(Governor, IGovernor, GovernorTimelockControl) + returns (ProposalState) + { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, IERC165, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/munged/package.json b/certora/munged/package.json new file mode 100644 index 000000000..c8705f0e5 --- /dev/null +++ b/certora/munged/package.json @@ -0,0 +1,32 @@ +{ + "name": "@openzeppelin/contracts", + "description": "Secure Smart Contract library for Solidity", + "version": "4.3.2", + "files": [ + "**/*.sol", + "/build/contracts/*.json", + "!/mocks/**/*" + ], + "scripts": { + "prepare": "bash ../scripts/prepare-contracts-package.sh", + "prepare-docs": "cd ..; npm run prepare-docs" + }, + "repository": { + "type": "git", + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts.git" + }, + "keywords": [ + "solidity", + "ethereum", + "smart", + "contracts", + "security", + "zeppelin" + ], + "author": "OpenZeppelin Community ", + "license": "MIT", + "bugs": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts/issues" + }, + "homepage": "https://openzeppelin.com/contracts/" +} diff --git a/certora/munged/proxy/Clones.sol b/certora/munged/proxy/Clones.sol new file mode 100644 index 000000000..bd661b10b --- /dev/null +++ b/certora/munged/proxy/Clones.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/Clones.sol) + +pragma solidity ^0.8.0; + +/** + * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for + * deploying minimal proxy contracts, also known as "clones". + * + * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies + * > a minimal bytecode implementation that delegates all calls to a known, fixed address. + * + * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` + * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the + * deterministic method. + * + * _Available since v3.4._ + */ +library Clones { + /** + * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. + * + * This function uses the create opcode, which should never revert. + */ + function clone(address implementation) internal returns (address instance) { + assembly { + let ptr := mload(0x40) + mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) + mstore(add(ptr, 0x14), shl(0x60, implementation)) + mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) + instance := create(0, ptr, 0x37) + } + require(instance != address(0), "ERC1167: create failed"); + } + + /** + * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. + * + * This function uses the create2 opcode and a `salt` to deterministically deploy + * the clone. Using the same `implementation` and `salt` multiple time will revert, since + * the clones cannot be deployed twice at the same address. + */ + function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { + assembly { + let ptr := mload(0x40) + mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) + mstore(add(ptr, 0x14), shl(0x60, implementation)) + mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) + instance := create2(0, ptr, 0x37, salt) + } + require(instance != address(0), "ERC1167: create2 failed"); + } + + /** + * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. + */ + function predictDeterministicAddress( + address implementation, + bytes32 salt, + address deployer + ) internal pure returns (address predicted) { + assembly { + let ptr := mload(0x40) + mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) + mstore(add(ptr, 0x14), shl(0x60, implementation)) + mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000) + mstore(add(ptr, 0x38), shl(0x60, deployer)) + mstore(add(ptr, 0x4c), salt) + mstore(add(ptr, 0x6c), keccak256(ptr, 0x37)) + predicted := keccak256(add(ptr, 0x37), 0x55) + } + } + + /** + * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. + */ + function predictDeterministicAddress(address implementation, bytes32 salt) + internal + view + returns (address predicted) + { + return predictDeterministicAddress(implementation, salt, address(this)); + } +} diff --git a/certora/munged/proxy/ERC1967/ERC1967Proxy.sol b/certora/munged/proxy/ERC1967/ERC1967Proxy.sol new file mode 100644 index 000000000..dbc655ecb --- /dev/null +++ b/certora/munged/proxy/ERC1967/ERC1967Proxy.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/ERC1967/ERC1967Proxy.sol) + +pragma solidity ^0.8.0; + +import "../Proxy.sol"; +import "./ERC1967Upgrade.sol"; + +/** + * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an + * implementation address that can be changed. This address is stored in storage in the location specified by + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the + * implementation behind the proxy. + */ +contract ERC1967Proxy is Proxy, ERC1967Upgrade { + /** + * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. + * + * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded + * function call, and allows initializating the storage of the proxy like a Solidity constructor. + */ + constructor(address _logic, bytes memory _data) payable { + assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); + _upgradeToAndCall(_logic, _data, false); + } + + /** + * @dev Returns the current implementation address. + */ + function _implementation() internal view virtual override returns (address impl) { + return ERC1967Upgrade._getImplementation(); + } +} diff --git a/certora/munged/proxy/ERC1967/ERC1967Upgrade.sol b/certora/munged/proxy/ERC1967/ERC1967Upgrade.sol new file mode 100644 index 000000000..053b5c124 --- /dev/null +++ b/certora/munged/proxy/ERC1967/ERC1967Upgrade.sol @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/ERC1967/ERC1967Upgrade.sol) + +pragma solidity ^0.8.2; + +import "../beacon/IBeacon.sol"; +import "../../utils/Address.sol"; +import "../../utils/StorageSlot.sol"; + +/** + * @dev This abstract contract provides getters and event emitting update functions for + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. + * + * _Available since v4.1._ + * + * @custom:oz-upgrades-unsafe-allow delegatecall + */ +abstract contract ERC1967Upgrade { + // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 + bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @dev Emitted when the implementation is upgraded. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Returns the current implementation address. + */ + function _getImplementation() internal view returns (address) { + return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 implementation slot. + */ + function _setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + } + + /** + * @dev Perform implementation upgrade + * + * Emits an {Upgraded} event. + */ + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Perform implementation upgrade with additional setup call. + * + * Emits an {Upgraded} event. + */ + function _upgradeToAndCall( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + _upgradeTo(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + } + + /** + * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. + * + * Emits an {Upgraded} event. + */ + function _upgradeToAndCallSecure( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + address oldImplementation = _getImplementation(); + + // Initial upgrade and setup call + _setImplementation(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + + // Perform rollback test if not already in progress + StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT); + if (!rollbackTesting.value) { + // Trigger rollback using upgradeTo from the new implementation + rollbackTesting.value = true; + Address.functionDelegateCall( + newImplementation, + abi.encodeWithSignature("upgradeTo(address)", oldImplementation) + ); + rollbackTesting.value = false; + // Check rollback was effective + require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades"); + // Finally reset to the new implementation and log the upgrade + _upgradeTo(newImplementation); + } + } + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + /** + * @dev Emitted when the admin account has changed. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Returns the current admin. + */ + function _getAdmin() internal view returns (address) { + return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 admin slot. + */ + function _setAdmin(address newAdmin) private { + require(newAdmin != address(0), "ERC1967: new admin is the zero address"); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } + + /** + * @dev Changes the admin of the proxy. + * + * Emits an {AdminChanged} event. + */ + function _changeAdmin(address newAdmin) internal { + emit AdminChanged(_getAdmin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. + * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. + */ + bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; + + /** + * @dev Emitted when the beacon is upgraded. + */ + event BeaconUpgraded(address indexed beacon); + + /** + * @dev Returns the current beacon. + */ + function _getBeacon() internal view returns (address) { + return StorageSlot.getAddressSlot(_BEACON_SLOT).value; + } + + /** + * @dev Stores a new beacon in the EIP1967 beacon slot. + */ + function _setBeacon(address newBeacon) private { + require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); + require( + Address.isContract(IBeacon(newBeacon).implementation()), + "ERC1967: beacon implementation is not a contract" + ); + StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; + } + + /** + * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does + * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). + * + * Emits a {BeaconUpgraded} event. + */ + function _upgradeBeaconToAndCall( + address newBeacon, + bytes memory data, + bool forceCall + ) internal { + _setBeacon(newBeacon); + emit BeaconUpgraded(newBeacon); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); + } + } +} diff --git a/certora/munged/proxy/Proxy.sol b/certora/munged/proxy/Proxy.sol new file mode 100644 index 000000000..84d9fde04 --- /dev/null +++ b/certora/munged/proxy/Proxy.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/Proxy.sol) + +pragma solidity ^0.8.0; + +/** + * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM + * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to + * be specified by overriding the virtual {_implementation} function. + * + * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a + * different contract through the {_delegate} function. + * + * The success and return data of the delegated call will be returned back to the caller of the proxy. + */ +abstract contract Proxy { + /** + * @dev Delegates the current call to `implementation`. + * + * This function does not return to its internall call site, it will return directly to the external caller. + */ + function _delegate(address implementation) internal virtual { + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize()) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + + /** + * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function + * and {_fallback} should delegate. + */ + function _implementation() internal view virtual returns (address); + + /** + * @dev Delegates the current call to the address returned by `_implementation()`. + * + * This function does not return to its internall call site, it will return directly to the external caller. + */ + function _fallback() internal virtual { + _beforeFallback(); + _delegate(_implementation()); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other + * function in the contract matches the call data. + */ + fallback() external payable virtual { + _fallback(); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data + * is empty. + */ + receive() external payable virtual { + _fallback(); + } + + /** + * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` + * call, or as part of the Solidity `fallback` or `receive` functions. + * + * If overriden should call `super._beforeFallback()`. + */ + function _beforeFallback() internal virtual {} +} diff --git a/certora/munged/proxy/README.adoc b/certora/munged/proxy/README.adoc new file mode 100644 index 000000000..ae278b083 --- /dev/null +++ b/certora/munged/proxy/README.adoc @@ -0,0 +1,83 @@ += Proxies + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/proxy + +This is a low-level set of contracts implementing different proxy patterns with and without upgradeability. For an in-depth overview of this pattern check out the xref:upgrades-plugins::proxies.adoc[Proxy Upgrade Pattern] page. + +Most of the proxies below are built on an abstract base contract. + +- {Proxy}: Abstract contract implementing the core delegation functionality. + +In order to avoid clashes with the storage variables of the implementation contract behind a proxy, we use https://eips.ethereum.org/EIPS/eip-1967[EIP1967] storage slots. + +- {ERC1967Upgrade}: Internal functions to get and set the storage slots defined in EIP1967. +- {ERC1967Proxy}: A proxy using EIP1967 storage slots. Not upgradeable by default. + +There are two alternative ways to add upgradeability to an ERC1967 proxy. Their differences are explained below in <>. + +- {TransparentUpgradeableProxy}: A proxy with a built in admin and upgrade interface. +- {UUPSUpgradeable}: An upgradeability mechanism to be included in the implementation for an ERC1967 proxy. + +CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Truffle and Hardhat. + +A different family of proxies are beacon proxies. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. + +- {BeaconProxy}: A proxy that retreives its implementation from a beacon contract. +- {UpgradeableBeacon}: A beacon contract that can be upgraded. + +In this pattern, the proxy contract doesn't hold the implementation address in storage like an ERC1967 proxy, instead the address is stored in a separate beacon contract. The `upgrade` operations that are sent to the beacon instead of to the proxy contract, and all proxies that follow that beacon are automatically upgraded. + +Outside the realm of upgradeability, proxies can also be useful to make cheap contract clones, such as those created by an on-chain factory contract that creates many instances of the same contract. These instances are designed to be both cheap to deploy, and cheap to call. + +- {Clones}: A library that can deploy cheap minimal non-upgradeable proxies. + +[[transparent-vs-uups]] +== Transparent vs UUPS Proxies + +The original proxies included in OpenZeppelin followed the https://blog.openzeppelin.com/the-transparent-proxy-pattern/[Transparent Proxy Pattern]. While this pattern is still provided, our recommendation is now shifting towards UUPS proxies, which are both lightweight and versatile. The name UUPS comes from https://eips.ethereum.org/EIPS/eip-1822[EIP1822], which first documented the pattern. + +While both of these share the same interface for upgrades, in UUPS proxies the upgrade is handled by the implementation, and can eventually be removed. Transparent proxies, on the other hand, include the upgrade and admin logic in the proxy itself. This means {TransparentUpgradeableProxy} is more expensive to deploy than what is possible with UUPS proxies. + +UUPS proxies are implemented using an {ERC1967Proxy}. Note that this proxy is not by itself upgradeable. It is the role of the implementation to include, alongside the contract's logic, all the code necessary to update the implementation's address that is stored at a specific slot in the proxy's storage space. This is where the {UUPSUpgradeable} contract comes in. Inheriting from it (and overriding the {xref-UUPSUpgradeable-_authorizeUpgrade-address-}[`_authorizeUpgrade`] function with the relevant access control mechanism) will turn your contract into a UUPS compliant implementation. + +Note that since both proxies use the same storage slot for the implementation address, using a UUPS compliant implementation with a {TransparentUpgradeableProxy} might allow non-admins to perform upgrade operations. + +By default, the upgrade functionality included in {UUPSUpgradeable} contains a security mechanism that will prevent any upgrades to a non UUPS compliant implementation. This prevents upgrades to an implementation contract that wouldn't contain the necessary upgrade mechanism, as it would lock the upgradeability of the proxy forever. This security mechanism can be bypassed by either of: + +- Adding a flag mechanism in the implementation that will disable the upgrade function when triggered. +- Upgrading to an implementation that features an upgrade mechanism without the additional security check, and then upgrading again to another implementation without the upgrade mechanism. + +== Core + +{{Proxy}} + +== ERC1967 + +{{ERC1967Proxy}} + +{{ERC1967Upgrade}} + +== Transparent Proxy + +{{TransparentUpgradeableProxy}} + +{{ProxyAdmin}} + +== Beacon + +{{BeaconProxy}} + +{{IBeacon}} + +{{UpgradeableBeacon}} + +== Minimal Clones + +{{Clones}} + +== Utils + +{{Initializable}} + +{{UUPSUpgradeable}} diff --git a/certora/munged/proxy/beacon/BeaconProxy.sol b/certora/munged/proxy/beacon/BeaconProxy.sol new file mode 100644 index 000000000..ff8970509 --- /dev/null +++ b/certora/munged/proxy/beacon/BeaconProxy.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/beacon/BeaconProxy.sol) + +pragma solidity ^0.8.0; + +import "./IBeacon.sol"; +import "../Proxy.sol"; +import "../ERC1967/ERC1967Upgrade.sol"; + +/** + * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}. + * + * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't + * conflict with the storage layout of the implementation behind the proxy. + * + * _Available since v3.4._ + */ +contract BeaconProxy is Proxy, ERC1967Upgrade { + /** + * @dev Initializes the proxy with `beacon`. + * + * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This + * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity + * constructor. + * + * Requirements: + * + * - `beacon` must be a contract with the interface {IBeacon}. + */ + constructor(address beacon, bytes memory data) payable { + assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1)); + _upgradeBeaconToAndCall(beacon, data, false); + } + + /** + * @dev Returns the current beacon address. + */ + function _beacon() internal view virtual returns (address) { + return _getBeacon(); + } + + /** + * @dev Returns the current implementation address of the associated beacon. + */ + function _implementation() internal view virtual override returns (address) { + return IBeacon(_getBeacon()).implementation(); + } + + /** + * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}. + * + * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. + * + * Requirements: + * + * - `beacon` must be a contract. + * - The implementation returned by `beacon` must be a contract. + */ + function _setBeacon(address beacon, bytes memory data) internal virtual { + _upgradeBeaconToAndCall(beacon, data, false); + } +} diff --git a/certora/munged/proxy/beacon/IBeacon.sol b/certora/munged/proxy/beacon/IBeacon.sol new file mode 100644 index 000000000..efa33d11c --- /dev/null +++ b/certora/munged/proxy/beacon/IBeacon.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/beacon/IBeacon.sol) + +pragma solidity ^0.8.0; + +/** + * @dev This is the interface that {BeaconProxy} expects of its beacon. + */ +interface IBeacon { + /** + * @dev Must return an address that can be used as a delegate call target. + * + * {BeaconProxy} will check that this address is a contract. + */ + function implementation() external view returns (address); +} diff --git a/certora/munged/proxy/beacon/UpgradeableBeacon.sol b/certora/munged/proxy/beacon/UpgradeableBeacon.sol new file mode 100644 index 000000000..4cbb5f49c --- /dev/null +++ b/certora/munged/proxy/beacon/UpgradeableBeacon.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/beacon/UpgradeableBeacon.sol) + +pragma solidity ^0.8.0; + +import "./IBeacon.sol"; +import "../../access/Ownable.sol"; +import "../../utils/Address.sol"; + +/** + * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their + * implementation contract, which is where they will delegate all function calls. + * + * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon. + */ +contract UpgradeableBeacon is IBeacon, Ownable { + address private _implementation; + + /** + * @dev Emitted when the implementation returned by the beacon is changed. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the + * beacon. + */ + constructor(address implementation_) { + _setImplementation(implementation_); + } + + /** + * @dev Returns the current implementation address. + */ + function implementation() public view virtual override returns (address) { + return _implementation; + } + + /** + * @dev Upgrades the beacon to a new implementation. + * + * Emits an {Upgraded} event. + * + * Requirements: + * + * - msg.sender must be the owner of the contract. + * - `newImplementation` must be a contract. + */ + function upgradeTo(address newImplementation) public virtual onlyOwner { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Sets the implementation contract address for this beacon + * + * Requirements: + * + * - `newImplementation` must be a contract. + */ + function _setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract"); + _implementation = newImplementation; + } +} diff --git a/certora/munged/proxy/transparent/ProxyAdmin.sol b/certora/munged/proxy/transparent/ProxyAdmin.sol new file mode 100644 index 000000000..18a6ef301 --- /dev/null +++ b/certora/munged/proxy/transparent/ProxyAdmin.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/transparent/ProxyAdmin.sol) + +pragma solidity ^0.8.0; + +import "./TransparentUpgradeableProxy.sol"; +import "../../access/Ownable.sol"; + +/** + * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an + * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}. + */ +contract ProxyAdmin is Ownable { + /** + * @dev Returns the current implementation of `proxy`. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) { + // We need to manually run the static call since the getter cannot be flagged as view + // bytes4(keccak256("implementation()")) == 0x5c60da1b + (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b"); + require(success); + return abi.decode(returndata, (address)); + } + + /** + * @dev Returns the current admin of `proxy`. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) { + // We need to manually run the static call since the getter cannot be flagged as view + // bytes4(keccak256("admin()")) == 0xf851a440 + (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440"); + require(success); + return abi.decode(returndata, (address)); + } + + /** + * @dev Changes the admin of `proxy` to `newAdmin`. + * + * Requirements: + * + * - This contract must be the current admin of `proxy`. + */ + function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner { + proxy.changeAdmin(newAdmin); + } + + /** + * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner { + proxy.upgradeTo(implementation); + } + + /** + * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See + * {TransparentUpgradeableProxy-upgradeToAndCall}. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function upgradeAndCall( + TransparentUpgradeableProxy proxy, + address implementation, + bytes memory data + ) public payable virtual onlyOwner { + proxy.upgradeToAndCall{value: msg.value}(implementation, data); + } +} diff --git a/certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol b/certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol new file mode 100644 index 000000000..7d1b7021d --- /dev/null +++ b/certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/transparent/TransparentUpgradeableProxy.sol) + +pragma solidity ^0.8.0; + +import "../ERC1967/ERC1967Proxy.sol"; + +/** + * @dev This contract implements a proxy that is upgradeable by an admin. + * + * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector + * clashing], which can potentially be used in an attack, this contract uses the + * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two + * things that go hand in hand: + * + * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if + * that call matches one of the admin functions exposed by the proxy itself. + * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the + * implementation. If the admin tries to call a function on the implementation it will fail with an error that says + * "admin cannot fallback to proxy target". + * + * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing + * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due + * to sudden errors when trying to call a function from the proxy implementation. + * + * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, + * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy. + */ +contract TransparentUpgradeableProxy is ERC1967Proxy { + /** + * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and + * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}. + */ + constructor( + address _logic, + address admin_, + bytes memory _data + ) payable ERC1967Proxy(_logic, _data) { + assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); + _changeAdmin(admin_); + } + + /** + * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. + */ + modifier ifAdmin() { + if (msg.sender == _getAdmin()) { + _; + } else { + _fallback(); + } + } + + /** + * @dev Returns the current admin. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the + * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` + */ + function admin() external ifAdmin returns (address admin_) { + admin_ = _getAdmin(); + } + + /** + * @dev Returns the current implementation. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the + * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` + */ + function implementation() external ifAdmin returns (address implementation_) { + implementation_ = _implementation(); + } + + /** + * @dev Changes the admin of the proxy. + * + * Emits an {AdminChanged} event. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}. + */ + function changeAdmin(address newAdmin) external virtual ifAdmin { + _changeAdmin(newAdmin); + } + + /** + * @dev Upgrade the implementation of the proxy. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}. + */ + function upgradeTo(address newImplementation) external ifAdmin { + _upgradeToAndCall(newImplementation, bytes(""), false); + } + + /** + * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified + * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the + * proxied contract. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}. + */ + function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin { + _upgradeToAndCall(newImplementation, data, true); + } + + /** + * @dev Returns the current admin. + */ + function _admin() internal view virtual returns (address) { + return _getAdmin(); + } + + /** + * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}. + */ + function _beforeFallback() internal virtual override { + require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target"); + super._beforeFallback(); + } +} diff --git a/certora/munged/proxy/utils/Initializable.sol b/certora/munged/proxy/utils/Initializable.sol new file mode 100644 index 000000000..12c77dca7 --- /dev/null +++ b/certora/munged/proxy/utils/Initializable.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/utils/Initializable.sol) + +pragma solidity ^0.8.0; + +/** + * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed + * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + * + * [CAUTION] + * ==== + * Avoid leaving a contract uninitialized. + * + * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation + * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the + * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed: + * + * [.hljs-theme-light.nopadding] + * ``` + * /// @custom:oz-upgrades-unsafe-allow constructor + * constructor() initializer {} + * ``` + * ==== + */ +abstract contract Initializable { + /** + * @dev Indicates that the contract has been initialized. + */ + bool private _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private _initializing; + + /** + * @dev Modifier to protect an initializer function from being invoked twice. + */ + modifier initializer() { + require(_initializing || !_initialized, "Initializable: contract is already initialized"); + + bool isTopLevelCall = !_initializing; + if (isTopLevelCall) { + _initializing = true; + _initialized = true; + } + + _; + + if (isTopLevelCall) { + _initializing = false; + } + } +} diff --git a/certora/munged/proxy/utils/UUPSUpgradeable.sol b/certora/munged/proxy/utils/UUPSUpgradeable.sol new file mode 100644 index 000000000..9969100d2 --- /dev/null +++ b/certora/munged/proxy/utils/UUPSUpgradeable.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (proxy/utils/UUPSUpgradeable.sol) + +pragma solidity ^0.8.0; + +import "../ERC1967/ERC1967Upgrade.sol"; + +/** + * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an + * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy. + * + * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is + * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing + * `UUPSUpgradeable` with a custom implementation of upgrades. + * + * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism. + * + * _Available since v4.1._ + */ +abstract contract UUPSUpgradeable is ERC1967Upgrade { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment + address private immutable __self = address(this); + + /** + * @dev Check that the execution is being performed through a delegatecall call and that the execution context is + * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case + * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a + * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to + * fail. + */ + modifier onlyProxy() { + require(address(this) != __self, "Function must be called through delegatecall"); + require(_getImplementation() == __self, "Function must be called through active proxy"); + _; + } + + /** + * @dev Upgrade the implementation of the proxy to `newImplementation`. + * + * Calls {_authorizeUpgrade}. + * + * Emits an {Upgraded} event. + */ + function upgradeTo(address newImplementation) external virtual onlyProxy { + _authorizeUpgrade(newImplementation); + _upgradeToAndCallSecure(newImplementation, new bytes(0), false); + } + + /** + * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call + * encoded in `data`. + * + * Calls {_authorizeUpgrade}. + * + * Emits an {Upgraded} event. + */ + function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy { + _authorizeUpgrade(newImplementation); + _upgradeToAndCallSecure(newImplementation, data, true); + } + + /** + * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by + * {upgradeTo} and {upgradeToAndCall}. + * + * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}. + * + * ```solidity + * function _authorizeUpgrade(address) internal override onlyOwner {} + * ``` + */ + function _authorizeUpgrade(address newImplementation) internal virtual; +} diff --git a/certora/munged/security/Pausable.sol b/certora/munged/security/Pausable.sol new file mode 100644 index 000000000..bca14e968 --- /dev/null +++ b/certora/munged/security/Pausable.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (security/Pausable.sol) + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +/** + * @dev Contract module which allows children to implement an emergency stop + * mechanism that can be triggered by an authorized account. + * + * This module is used through inheritance. It will make available the + * modifiers `whenNotPaused` and `whenPaused`, which can be applied to + * the functions of your contract. Note that they will not be pausable by + * simply including this module, only once the modifiers are put in place. + */ +abstract contract Pausable is Context { + /** + * @dev Emitted when the pause is triggered by `account`. + */ + event Paused(address account); + + /** + * @dev Emitted when the pause is lifted by `account`. + */ + event Unpaused(address account); + + bool private _paused; + + /** + * @dev Initializes the contract in unpaused state. + */ + constructor() { + _paused = false; + } + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + require(!paused(), "Pausable: paused"); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + require(paused(), "Pausable: not paused"); + _; + } + + /** + * @dev Triggers stopped state. + * + * Requirements: + * + * - The contract must not be paused. + */ + function _pause() internal virtual whenNotPaused { + _paused = true; + emit Paused(_msgSender()); + } + + /** + * @dev Returns to normal state. + * + * Requirements: + * + * - The contract must be paused. + */ + function _unpause() internal virtual whenPaused { + _paused = false; + emit Unpaused(_msgSender()); + } +} diff --git a/certora/munged/security/PullPayment.sol b/certora/munged/security/PullPayment.sol new file mode 100644 index 000000000..df03ec08a --- /dev/null +++ b/certora/munged/security/PullPayment.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (security/PullPayment.sol) + +pragma solidity ^0.8.0; + +import "../utils/escrow/Escrow.sol"; + +/** + * @dev Simple implementation of a + * https://consensys.github.io/smart-contract-best-practices/recommendations/#favor-pull-over-push-for-external-calls[pull-payment] + * strategy, where the paying contract doesn't interact directly with the + * receiver account, which must withdraw its payments itself. + * + * Pull-payments are often considered the best practice when it comes to sending + * Ether, security-wise. It prevents recipients from blocking execution, and + * eliminates reentrancy concerns. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + * + * To use, derive from the `PullPayment` contract, and use {_asyncTransfer} + * instead of Solidity's `transfer` function. Payees can query their due + * payments with {payments}, and retrieve them with {withdrawPayments}. + */ +abstract contract PullPayment { + Escrow private immutable _escrow; + + constructor() { + _escrow = new Escrow(); + } + + /** + * @dev Withdraw accumulated payments, forwarding all gas to the recipient. + * + * Note that _any_ account can call this function, not just the `payee`. + * This means that contracts unaware of the `PullPayment` protocol can still + * receive funds this way, by having a separate account call + * {withdrawPayments}. + * + * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. + * Make sure you trust the recipient, or are either following the + * checks-effects-interactions pattern or using {ReentrancyGuard}. + * + * @param payee Whose payments will be withdrawn. + */ + function withdrawPayments(address payable payee) public virtual { + _escrow.withdraw(payee); + } + + /** + * @dev Returns the payments owed to an address. + * @param dest The creditor's address. + */ + function payments(address dest) public view returns (uint256) { + return _escrow.depositsOf(dest); + } + + /** + * @dev Called by the payer to store the sent amount as credit to be pulled. + * Funds sent in this way are stored in an intermediate {Escrow} contract, so + * there is no danger of them being spent before withdrawal. + * + * @param dest The destination address of the funds. + * @param amount The amount to transfer. + */ + function _asyncTransfer(address dest, uint256 amount) internal virtual { + _escrow.deposit{value: amount}(dest); + } +} diff --git a/certora/munged/security/README.adoc b/certora/munged/security/README.adoc new file mode 100644 index 000000000..66f398fec --- /dev/null +++ b/certora/munged/security/README.adoc @@ -0,0 +1,20 @@ += Security + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/security + +These contracts aim to cover common security practices. + +* {PullPayment}: A pattern that can be used to avoid reentrancy attacks. +* {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions. +* {Pausable}: A common emergency response mechanism that can pause functionality while a remediation is pending. + +TIP: For an overview on reentrancy and the possible mechanisms to prevent it, read our article https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + +== Contracts + +{{PullPayment}} + +{{ReentrancyGuard}} + +{{Pausable}} diff --git a/certora/munged/security/ReentrancyGuard.sol b/certora/munged/security/ReentrancyGuard.sol new file mode 100644 index 000000000..aea68d3db --- /dev/null +++ b/certora/munged/security/ReentrancyGuard.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (security/ReentrancyGuard.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Contract module that helps prevent reentrant calls to a function. + * + * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier + * available, which can be applied to functions to make sure there are no nested + * (reentrant) calls to them. + * + * Note that because there is a single `nonReentrant` guard, functions marked as + * `nonReentrant` may not call one another. This can be worked around by making + * those functions `private`, and then adding `external` `nonReentrant` entry + * points to them. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + */ +abstract contract ReentrancyGuard { + // Booleans are more expensive than uint256 or any type that takes up a full + // word because each write operation emits an extra SLOAD to first read the + // slot's contents, replace the bits taken up by the boolean, and then write + // back. This is the compiler's defense against contract upgrades and + // pointer aliasing, and it cannot be disabled. + + // The values being non-zero value makes deployment a bit more expensive, + // but in exchange the refund on every call to nonReentrant will be lower in + // amount. Since refunds are capped to a percentage of the total + // transaction's gas, it is best to keep them low in cases like this one, to + // increase the likelihood of the full refund coming into effect. + uint256 private constant _NOT_ENTERED = 1; + uint256 private constant _ENTERED = 2; + + uint256 private _status; + + constructor() { + _status = _NOT_ENTERED; + } + + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is not supported. It is possible to prevent this from happening + * by making the `nonReentrant` function external, and making it call a + * `private` function that does the actual work. + */ + modifier nonReentrant() { + // On the first call to nonReentrant, _notEntered will be true + require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _status = _ENTERED; + + _; + + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _status = _NOT_ENTERED; + } +} diff --git a/certora/munged/token/ERC1155/ERC1155.sol b/certora/munged/token/ERC1155/ERC1155.sol new file mode 100644 index 000000000..38e101e64 --- /dev/null +++ b/certora/munged/token/ERC1155/ERC1155.sol @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC1155/ERC1155.sol) + +pragma solidity ^0.8.0; + +import "./IERC1155.sol"; +import "./IERC1155Receiver.sol"; +import "./extensions/IERC1155MetadataURI.sol"; +import "../../utils/Address.sol"; +import "../../utils/Context.sol"; +import "../../utils/introspection/ERC165.sol"; + +/** + * @dev Implementation of the basic standard multi-token. + * See https://eips.ethereum.org/EIPS/eip-1155 + * Originally based on code by Enjin: https://github.com/enjin/erc-1155 + * + * _Available since v3.1._ + */ +contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { + using Address for address; + + // Mapping from token ID to account balances + mapping(uint256 => mapping(address => uint256)) private _balances; + + // Mapping from account to operator approvals + mapping(address => mapping(address => bool)) private _operatorApprovals; + + // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json + string private _uri; + + /** + * @dev See {_setURI}. + */ + constructor(string memory uri_) { + _setURI(uri_); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IERC1155).interfaceId || + interfaceId == type(IERC1155MetadataURI).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC1155MetadataURI-uri}. + * + * This implementation returns the same URI for *all* token types. It relies + * on the token type ID substitution mechanism + * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. + * + * Clients calling this function must replace the `\{id\}` substring with the + * actual token type ID. + */ + function uri(uint256) public view virtual override returns (string memory) { + return _uri; + } + + /** + * @dev See {IERC1155-balanceOf}. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { + require(account != address(0), "ERC1155: balance query for the zero address"); + return _balances[id][account]; + } + + /** + * @dev See {IERC1155-balanceOfBatch}. + * + * Requirements: + * + * - `accounts` and `ids` must have the same length. + */ + function balanceOfBatch(address[] memory accounts, uint256[] memory ids) + public + view + virtual + override + returns (uint256[] memory) + { + require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch"); + + uint256[] memory batchBalances = new uint256[](accounts.length); + + for (uint256 i = 0; i < accounts.length; ++i) { + batchBalances[i] = balanceOf(accounts[i], ids[i]); + } + + return batchBalances; + } + + /** + * @dev See {IERC1155-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) public virtual override { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /** + * @dev See {IERC1155-isApprovedForAll}. + */ + function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { + return _operatorApprovals[account][operator]; + } + + /** + * @dev See {IERC1155-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual override { + require( + from == _msgSender() || isApprovedForAll(from, _msgSender()), + "ERC1155: caller is not owner nor approved" + ); + _safeTransferFrom(from, to, id, amount, data); + } + + /** + * @dev See {IERC1155-safeBatchTransferFrom}. + */ + function safeBatchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual override { + require( + from == _msgSender() || isApprovedForAll(from, _msgSender()), + "ERC1155: transfer caller is not owner nor approved" + ); + _safeBatchTransferFrom(from, to, ids, amounts, data); + } + + /** + * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `from` must have a balance of tokens of type `id` of at least `amount`. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function _safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) internal virtual { + require(to != address(0), "ERC1155: transfer to the zero address"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data); + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + _balances[id][to] += amount; + + emit TransferSingle(operator, from, to, id, amount); + + _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + */ + function _safeBatchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual { + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + require(to != address(0), "ERC1155: transfer to the zero address"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, from, to, ids, amounts, data); + + for (uint256 i = 0; i < ids.length; ++i) { + uint256 id = ids[i]; + uint256 amount = amounts[i]; + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + _balances[id][to] += amount; + } + + emit TransferBatch(operator, from, to, ids, amounts); + + _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); + } + + /** + * @dev Sets a new URI for all token types, by relying on the token type ID + * substitution mechanism + * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. + * + * By this mechanism, any occurrence of the `\{id\}` substring in either the + * URI or any of the amounts in the JSON file at said URI will be replaced by + * clients with the token type ID. + * + * For example, the `https://token-cdn-domain/\{id\}.json` URI would be + * interpreted by clients as + * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` + * for token type ID 0x4cce0. + * + * See {uri}. + * + * Because these URIs cannot be meaningfully represented by the {URI} event, + * this function emits no events. + */ + function _setURI(string memory newuri) internal virtual { + _uri = newuri; + } + + /** + * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function _mint( + address to, + uint256 id, + uint256 amount, + bytes memory data + ) internal virtual { + require(to != address(0), "ERC1155: mint to the zero address"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, address(0), to, _asSingletonArray(id), _asSingletonArray(amount), data); + + _balances[id][to] += amount; + emit TransferSingle(operator, address(0), to, id, amount); + + _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}. + * + * Requirements: + * + * - `ids` and `amounts` must have the same length. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + */ + function _mintBatch( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual { + require(to != address(0), "ERC1155: mint to the zero address"); + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); + + for (uint256 i = 0; i < ids.length; i++) { + _balances[ids[i]][to] += amounts[i]; + } + + emit TransferBatch(operator, address(0), to, ids, amounts); + + _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data); + } + + /** + * @dev Destroys `amount` tokens of token type `id` from `from` + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `from` must have at least `amount` tokens of token type `id`. + */ + function _burn( + address from, + uint256 id, + uint256 amount + ) internal virtual { + require(from != address(0), "ERC1155: burn from the zero address"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, from, address(0), _asSingletonArray(id), _asSingletonArray(amount), ""); + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + + emit TransferSingle(operator, from, address(0), id, amount); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}. + * + * Requirements: + * + * - `ids` and `amounts` must have the same length. + */ + function _burnBatch( + address from, + uint256[] memory ids, + uint256[] memory amounts + ) internal virtual { + require(from != address(0), "ERC1155: burn from the zero address"); + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); + + for (uint256 i = 0; i < ids.length; i++) { + uint256 id = ids[i]; + uint256 amount = amounts[i]; + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + } + + emit TransferBatch(operator, from, address(0), ids, amounts); + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Emits a {ApprovalForAll} event. + */ + function _setApprovalForAll( + address owner, + address operator, + bool approved + ) internal virtual { + require(owner != operator, "ERC1155: setting approval status for self"); + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev Hook that is called before any token transfer. This includes minting + * and burning, as well as batched variants. + * + * The same hook is called on both single and batched variants. For single + * transfers, the length of the `id` and `amount` arrays will be 1. + * + * Calling conditions (for each `id` and `amount` pair): + * + * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * of token type `id` will be transferred to `to`. + * - When `from` is zero, `amount` tokens of token type `id` will be minted + * for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` + * will be burned. + * - `from` and `to` are never both zero. + * - `ids` and `amounts` have the same, non-zero length. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual {} + + function _doSafeTransferAcceptanceCheck( + address operator, + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) private { + if (to.isContract()) { + try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { + if (response != IERC1155Receiver.onERC1155Received.selector) { + revert("ERC1155: ERC1155Receiver rejected tokens"); + } + } catch Error(string memory reason) { + revert(reason); + } catch { + revert("ERC1155: transfer to non ERC1155Receiver implementer"); + } + } + } + + function _doSafeBatchTransferAcceptanceCheck( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) private { + if (to.isContract()) { + try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( + bytes4 response + ) { + if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { + revert("ERC1155: ERC1155Receiver rejected tokens"); + } + } catch Error(string memory reason) { + revert(reason); + } catch { + revert("ERC1155: transfer to non ERC1155Receiver implementer"); + } + } + } + + function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { + uint256[] memory array = new uint256[](1); + array[0] = element; + + return array; + } +} diff --git a/certora/munged/token/ERC1155/IERC1155.sol b/certora/munged/token/ERC1155/IERC1155.sol new file mode 100644 index 000000000..c6c204898 --- /dev/null +++ b/certora/munged/token/ERC1155/IERC1155.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC1155/IERC1155.sol) + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * @dev Required interface of an ERC1155 compliant contract, as defined in the + * https://eips.ethereum.org/EIPS/eip-1155[EIP]. + * + * _Available since v3.1._ + */ +interface IERC1155 is IERC165 { + /** + * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. + */ + event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); + + /** + * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all + * transfers. + */ + event TransferBatch( + address indexed operator, + address indexed from, + address indexed to, + uint256[] ids, + uint256[] values + ); + + /** + * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to + * `approved`. + */ + event ApprovalForAll(address indexed account, address indexed operator, bool approved); + + /** + * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. + * + * If an {URI} event was emitted for `id`, the standard + * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value + * returned by {IERC1155MetadataURI-uri}. + */ + event URI(string value, uint256 indexed id); + + /** + * @dev Returns the amount of tokens of token type `id` owned by `account`. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function balanceOf(address account, uint256 id) external view returns (uint256); + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. + * + * Requirements: + * + * - `accounts` and `ids` must have the same length. + */ + function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) + external + view + returns (uint256[] memory); + + /** + * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, + * + * Emits an {ApprovalForAll} event. + * + * Requirements: + * + * - `operator` cannot be the caller. + */ + function setApprovalForAll(address operator, bool approved) external; + + /** + * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. + * + * See {setApprovalForAll}. + */ + function isApprovedForAll(address account, address operator) external view returns (bool); + + /** + * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}. + * - `from` must have a balance of tokens of type `id` of at least `amount`. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes calldata data + ) external; + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - `ids` and `amounts` must have the same length. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + */ + function safeBatchTransferFrom( + address from, + address to, + uint256[] calldata ids, + uint256[] calldata amounts, + bytes calldata data + ) external; +} diff --git a/certora/munged/token/ERC1155/IERC1155Receiver.sol b/certora/munged/token/ERC1155/IERC1155Receiver.sol new file mode 100644 index 000000000..b7f7f7373 --- /dev/null +++ b/certora/munged/token/ERC1155/IERC1155Receiver.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC1155/IERC1155Receiver.sol) + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * @dev _Available since v3.1._ + */ +interface IERC1155Receiver is IERC165 { + /** + * @dev Handles the receipt of a single ERC1155 token type. This function is + * called at the end of a `safeTransferFrom` after the balance has been updated. + * + * NOTE: To accept the transfer, this must return + * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` + * (i.e. 0xf23a6e61, or its own function selector). + * + * @param operator The address which initiated the transfer (i.e. msg.sender) + * @param from The address which previously owned the token + * @param id The ID of the token being transferred + * @param value The amount of tokens being transferred + * @param data Additional data with no specified format + * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed + */ + function onERC1155Received( + address operator, + address from, + uint256 id, + uint256 value, + bytes calldata data + ) external returns (bytes4); + + /** + * @dev Handles the receipt of a multiple ERC1155 token types. This function + * is called at the end of a `safeBatchTransferFrom` after the balances have + * been updated. + * + * NOTE: To accept the transfer(s), this must return + * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` + * (i.e. 0xbc197c81, or its own function selector). + * + * @param operator The address which initiated the batch transfer (i.e. msg.sender) + * @param from The address which previously owned the token + * @param ids An array containing ids of each token being transferred (order and length must match values array) + * @param values An array containing amounts of each token being transferred (order and length must match ids array) + * @param data Additional data with no specified format + * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed + */ + function onERC1155BatchReceived( + address operator, + address from, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external returns (bytes4); +} diff --git a/certora/munged/token/ERC1155/README.adoc b/certora/munged/token/ERC1155/README.adoc new file mode 100644 index 000000000..2e0b22bae --- /dev/null +++ b/certora/munged/token/ERC1155/README.adoc @@ -0,0 +1,47 @@ += ERC 1155 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc1155 + +This set of interfaces and contracts are all related to the https://eips.ethereum.org/EIPS/eip-1155[ERC1155 Multi Token Standard]. + +The EIP consists of three interfaces which fulfill different roles, found here as {IERC1155}, {IERC1155MetadataURI} and {IERC1155Receiver}. + +{ERC1155} implements the mandatory {IERC1155} interface, as well as the optional extension {IERC1155MetadataURI}, by relying on the substitution mechanism to use the same URI for all token types, dramatically reducing gas costs. + +Additionally there are multiple custom extensions, including: + +* designation of addresses that can pause token transfers for all users ({ERC1155Pausable}). +* destruction of own tokens ({ERC1155Burnable}). + +NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC1155 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc1155.adoc#Presets[ERC1155 Presets] (such as {ERC1155PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. + +== Core + +{{IERC1155}} + +{{IERC1155MetadataURI}} + +{{ERC1155}} + +{{IERC1155Receiver}} + +{{ERC1155Receiver}} + +== Extensions + +{{ERC1155Pausable}} + +{{ERC1155Burnable}} + +{{ERC1155Supply}} + +== Presets + +These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC1155PresetMinterPauser}} + +== Utilities + +{{ERC1155Holder}} diff --git a/certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol b/certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol new file mode 100644 index 000000000..a16d840d9 --- /dev/null +++ b/certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC1155/extensions/ERC1155Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; + +/** + * @dev Extension of {ERC1155} that allows token holders to destroy both their + * own tokens and those that they have been approved to use. + * + * _Available since v3.1._ + */ +abstract contract ERC1155Burnable is ERC1155 { + function burn( + address account, + uint256 id, + uint256 value + ) public virtual { + require( + account == _msgSender() || isApprovedForAll(account, _msgSender()), + "ERC1155: caller is not owner nor approved" + ); + + _burn(account, id, value); + } + + function burnBatch( + address account, + uint256[] memory ids, + uint256[] memory values + ) public virtual { + require( + account == _msgSender() || isApprovedForAll(account, _msgSender()), + "ERC1155: caller is not owner nor approved" + ); + + _burnBatch(account, ids, values); + } +} diff --git a/certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol b/certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol new file mode 100644 index 000000000..d08be8ced --- /dev/null +++ b/certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC1155/extensions/ERC1155Pausable.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; +import "../../../security/Pausable.sol"; + +/** + * @dev ERC1155 token with pausable token transfers, minting and burning. + * + * Useful for scenarios such as preventing trades until the end of an evaluation + * period, or having an emergency switch for freezing all token transfers in the + * event of a large bug. + * + * _Available since v3.1._ + */ +abstract contract ERC1155Pausable is ERC1155, Pausable { + /** + * @dev See {ERC1155-_beforeTokenTransfer}. + * + * Requirements: + * + * - the contract must not be paused. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + + require(!paused(), "ERC1155Pausable: token transfer while paused"); + } +} diff --git a/certora/munged/token/ERC1155/extensions/ERC1155Supply.sol b/certora/munged/token/ERC1155/extensions/ERC1155Supply.sol new file mode 100644 index 000000000..693bef63e --- /dev/null +++ b/certora/munged/token/ERC1155/extensions/ERC1155Supply.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC1155/extensions/ERC1155Supply.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; + +/** + * @dev Extension of ERC1155 that adds tracking of total supply per id. + * + * Useful for scenarios where Fungible and Non-fungible tokens have to be + * clearly identified. Note: While a totalSupply of 1 might mean the + * corresponding is an NFT, there is no guarantees that no other token with the + * same id are not going to be minted. + */ +abstract contract ERC1155Supply is ERC1155 { + mapping(uint256 => uint256) private _totalSupply; + + /** + * @dev Total amount of tokens in with a given id. + */ + function totalSupply(uint256 id) public view virtual returns (uint256) { + return _totalSupply[id]; + } + + /** + * @dev Indicates whether any token exist with a given id, or not. + */ + function exists(uint256 id) public view virtual returns (bool) { + return ERC1155Supply.totalSupply(id) > 0; + } + + /** + * @dev See {ERC1155-_beforeTokenTransfer}. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + + if (from == address(0)) { + for (uint256 i = 0; i < ids.length; ++i) { + _totalSupply[ids[i]] += amounts[i]; + } + } + + if (to == address(0)) { + for (uint256 i = 0; i < ids.length; ++i) { + _totalSupply[ids[i]] -= amounts[i]; + } + } + } +} diff --git a/certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol b/certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol new file mode 100644 index 000000000..f150cfb03 --- /dev/null +++ b/certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC1155/extensions/IERC1155MetadataURI.sol) + +pragma solidity ^0.8.0; + +import "../IERC1155.sol"; + +/** + * @dev Interface of the optional ERC1155MetadataExtension interface, as defined + * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP]. + * + * _Available since v3.1._ + */ +interface IERC1155MetadataURI is IERC1155 { + /** + * @dev Returns the URI for token type `id`. + * + * If the `\{id\}` substring is present in the URI, it must be replaced by + * clients with the actual token type ID. + */ + function uri(uint256 id) external view returns (string memory); +} diff --git a/certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol b/certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol new file mode 100644 index 000000000..c7840bc26 --- /dev/null +++ b/certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC1155/presets/ERC1155PresetMinterPauser.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; +import "../extensions/ERC1155Burnable.sol"; +import "../extensions/ERC1155Pausable.sol"; +import "../../../access/AccessControlEnumerable.sol"; +import "../../../utils/Context.sol"; + +/** + * @dev {ERC1155} token, including: + * + * - ability for holders to burn (destroy) their tokens + * - a minter role that allows for token minting (creation) + * - a pauser role that allows to stop all token transfers + * + * This contract uses {AccessControl} to lock permissioned functions using the + * different roles - head to its documentation for details. + * + * The account that deploys the contract will be granted the minter and pauser + * roles, as well as the default admin role, which will let it grant both minter + * and pauser roles to other accounts. + */ +contract ERC1155PresetMinterPauser is Context, AccessControlEnumerable, ERC1155Burnable, ERC1155Pausable { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + /** + * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, and `PAUSER_ROLE` to the account that + * deploys the contract. + */ + constructor(string memory uri) ERC1155(uri) { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + + _setupRole(MINTER_ROLE, _msgSender()); + _setupRole(PAUSER_ROLE, _msgSender()); + } + + /** + * @dev Creates `amount` new tokens for `to`, of token type `id`. + * + * See {ERC1155-_mint}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function mint( + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint"); + + _mint(to, id, amount, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] variant of {mint}. + */ + function mintBatch( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint"); + + _mintBatch(to, ids, amounts, data); + } + + /** + * @dev Pauses all token transfers. + * + * See {ERC1155Pausable} and {Pausable-_pause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function pause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have pauser role to pause"); + _pause(); + } + + /** + * @dev Unpauses all token transfers. + * + * See {ERC1155Pausable} and {Pausable-_unpause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function unpause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have pauser role to unpause"); + _unpause(); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(AccessControlEnumerable, ERC1155) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override(ERC1155, ERC1155Pausable) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + } +} diff --git a/certora/munged/token/ERC1155/utils/ERC1155Holder.sol b/certora/munged/token/ERC1155/utils/ERC1155Holder.sol new file mode 100644 index 000000000..d11aa0567 --- /dev/null +++ b/certora/munged/token/ERC1155/utils/ERC1155Holder.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC1155/utils/ERC1155Holder.sol) + +pragma solidity ^0.8.0; + +import "./ERC1155Receiver.sol"; + +/** + * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens. + * + * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be + * stuck. + * + * @dev _Available since v3.1._ + */ +contract ERC1155Holder is ERC1155Receiver { + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] memory, + uint256[] memory, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155BatchReceived.selector; + } +} diff --git a/certora/munged/token/ERC1155/utils/ERC1155Receiver.sol b/certora/munged/token/ERC1155/utils/ERC1155Receiver.sol new file mode 100644 index 000000000..87b34e2bb --- /dev/null +++ b/certora/munged/token/ERC1155/utils/ERC1155Receiver.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC1155/utils/ERC1155Receiver.sol) + +pragma solidity ^0.8.0; + +import "../IERC1155Receiver.sol"; +import "../../../utils/introspection/ERC165.sol"; + +/** + * @dev _Available since v3.1._ + */ +abstract contract ERC1155Receiver is ERC165, IERC1155Receiver { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId); + } +} diff --git a/certora/munged/token/ERC20/ERC20.sol b/certora/munged/token/ERC20/ERC20.sol new file mode 100644 index 000000000..eddd84567 --- /dev/null +++ b/certora/munged/token/ERC20/ERC20.sol @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/ERC20.sol) + +pragma solidity ^0.8.0; + +import "./IERC20.sol"; +import "./extensions/IERC20Metadata.sol"; +import "../../utils/Context.sol"; + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC20 + * applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20, IERC20Metadata { + mapping(address => uint256) private _balances; + + mapping(address => mapping(address => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + /** + * @dev Sets the values for {name} and {symbol}. + * + * The default value of {decimals} is 18. To select a different value for + * {decimals} you should overload it. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC20} uses, unless this function is + * overridden; + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual override returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}. + * + * Requirements: + * + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + * - the caller must have allowance for ``sender``'s tokens of at least + * `amount`. + */ + function transferFrom( + address sender, + address recipient, + uint256 amount + ) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + + uint256 currentAllowance = _allowances[sender][_msgSender()]; + require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); + unchecked { + _approve(sender, _msgSender(), currentAllowance - amount); + } + + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + uint256 currentAllowance = _allowances[_msgSender()][spender]; + require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); + unchecked { + _approve(_msgSender(), spender, currentAllowance - subtractedValue); + } + + return true; + } + + /** + * @dev Moves `amount` of tokens from `sender` to `recipient`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + */ + function _transfer( + address sender, + address recipient, + uint256 amount + ) internal virtual { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + uint256 senderBalance = _balances[sender]; + require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); + unchecked { + _balances[sender] = senderBalance - amount; + } + _balances[recipient] += amount; + + emit Transfer(sender, recipient, amount); + + _afterTokenTransfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply += amount; + _balances[account] += amount; + emit Transfer(address(0), account, amount); + + _afterTokenTransfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + uint256 accountBalance = _balances[account]; + require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); + unchecked { + _balances[account] = accountBalance - amount; + } + _totalSupply -= amount; + + emit Transfer(account, address(0), amount); + + _afterTokenTransfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve( + address owner, + address spender, + uint256 amount + ) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual {} + + /** + * @dev Hook that is called after any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * has been transferred to `to`. + * - when `from` is zero, `amount` tokens have been minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens have been burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual {} +} diff --git a/certora/munged/token/ERC20/IERC20.sol b/certora/munged/token/ERC20/IERC20.sol new file mode 100644 index 000000000..1cebc855d --- /dev/null +++ b/certora/munged/token/ERC20/IERC20.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/certora/munged/token/ERC20/README.adoc b/certora/munged/token/ERC20/README.adoc new file mode 100644 index 000000000..f2892293d --- /dev/null +++ b/certora/munged/token/ERC20/README.adoc @@ -0,0 +1,83 @@ += ERC 20 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc20 + +This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-20[ERC20 Token Standard]. + +TIP: For an overview of ERC20 tokens and a walk through on how to create a token contract read our xref:ROOT:erc20.adoc[ERC20 guide]. + +There a few core contracts that implement the behavior specified in the EIP: + +* {IERC20}: the interface all ERC20 implementations should conform to. +* {IERC20Metadata}: the extended ERC20 interface including the <>, <> and <> functions. +* {ERC20}: the implementation of the ERC20 interface, including the <>, <> and <> optional standard extension to the base interface. + +Additionally there are multiple custom extensions, including: + +* {ERC20Burnable}: destruction of own tokens. +* {ERC20Capped}: enforcement of a cap to the total supply when minting tokens. +* {ERC20Pausable}: ability to pause token transfers. +* {ERC20Snapshot}: efficient storage of past token balances to be later queried at any point in time. +* {ERC20Permit}: gasless approval of tokens (standardized as ERC2612). +* {ERC20FlashMint}: token level support for flash loans through the minting and burning of ephemeral tokens (standardized as ERC3156). +* {ERC20Votes}: support for voting and vote delegation. +* {ERC20VotesComp}: support for voting and vote delegation (compatible with Compound's token, with uint96 restrictions). +* {ERC20Wrapper}: wrapper to create an ERC20 backed by another ERC20, with deposit and withdraw methods. Useful in conjunction with {ERC20Votes}. + +Finally, there are some utilities to interact with ERC20 contracts in various ways. + +* {SafeERC20}: a wrapper around the interface that eliminates the need to handle boolean return values. +* {TokenTimelock}: hold tokens for a beneficiary until a specified time. + +The following related EIPs are in draft status. + +- {ERC20Permit} + +NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC20 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc20.adoc#Presets[ERC20 Presets] (such as {ERC20PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. + +== Core + +{{IERC20}} + +{{IERC20Metadata}} + +{{ERC20}} + +== Extensions + +{{ERC20Burnable}} + +{{ERC20Capped}} + +{{ERC20Pausable}} + +{{ERC20Snapshot}} + +{{ERC20Votes}} + +{{ERC20VotesComp}} + +{{ERC20Wrapper}} + +{{ERC20FlashMint}} + +== Draft EIPs + +The following EIPs are still in Draft status. Due to their nature as drafts, the details of these contracts may change and we cannot guarantee their xref:ROOT:releases-stability.adoc[stability]. Minor releases of OpenZeppelin Contracts may contain breaking changes for the contracts in this directory, which will be duly announced in the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md[changelog]. The EIPs included here are used by projects in production and this may make them less likely to change significantly. + +{{ERC20Permit}} + +== Presets + +These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC20PresetMinterPauser}} + +{{ERC20PresetFixedSupply}} + +== Utilities + +{{SafeERC20}} + +{{TokenTimelock}} diff --git a/certora/munged/token/ERC20/extensions/ERC20Burnable.sol b/certora/munged/token/ERC20/extensions/ERC20Burnable.sol new file mode 100644 index 000000000..12402a982 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Burnable.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../../../utils/Context.sol"; + +/** + * @dev Extension of {ERC20} that allows token holders to destroy both their own + * tokens and those that they have an allowance for, in a way that can be + * recognized off-chain (via event analysis). + */ +abstract contract ERC20Burnable is Context, ERC20 { + /** + * @dev Destroys `amount` tokens from the caller. + * + * See {ERC20-_burn}. + */ + function burn(uint256 amount) public virtual { + _burn(_msgSender(), amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, deducting from the caller's + * allowance. + * + * See {ERC20-_burn} and {ERC20-allowance}. + * + * Requirements: + * + * - the caller must have allowance for ``accounts``'s tokens of at least + * `amount`. + */ + function burnFrom(address account, uint256 amount) public virtual { + uint256 currentAllowance = allowance(account, _msgSender()); + require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance"); + unchecked { + _approve(account, _msgSender(), currentAllowance - amount); + } + _burn(account, amount); + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Capped.sol b/certora/munged/token/ERC20/extensions/ERC20Capped.sol new file mode 100644 index 000000000..c85ccce30 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Capped.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Capped.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; + +/** + * @dev Extension of {ERC20} that adds a cap to the supply of tokens. + */ +abstract contract ERC20Capped is ERC20 { + uint256 private immutable _cap; + + /** + * @dev Sets the value of the `cap`. This value is immutable, it can only be + * set once during construction. + */ + constructor(uint256 cap_) { + require(cap_ > 0, "ERC20Capped: cap is 0"); + _cap = cap_; + } + + /** + * @dev Returns the cap on the token's total supply. + */ + function cap() public view virtual returns (uint256) { + return _cap; + } + + /** + * @dev See {ERC20-_mint}. + */ + function _mint(address account, uint256 amount) internal virtual override { + require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded"); + super._mint(account, amount); + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol b/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol new file mode 100644 index 000000000..4ab082f08 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20FlashMint.sol) + +pragma solidity ^0.8.0; + +import "../../../interfaces/IERC3156.sol"; +import "../ERC20.sol"; + +/** + * @dev Implementation of the ERC3156 Flash loans extension, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * Adds the {flashLoan} method, which provides flash loan support at the token + * level. By default there is no fee, but this can be changed by overriding {flashFee}. + * + * _Available since v4.1._ + */ +abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { + bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); + + /** + * @dev Returns the maximum amount of tokens available for loan. + * @param token The address of the token that is requested. + * @return The amont of token that can be loaned. + */ + function maxFlashLoan(address token) public view override returns (uint256) { + return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0; + } + + /** + * @dev Returns the fee applied when doing flash loans. By default this + * implementation has 0 fees. This function can be overloaded to make + * the flash loan mechanism deflationary. + * @param token The token to be flash loaned. + * @param amount The amount of tokens to be loaned. + * @return The fees applied to the corresponding flash loan. + */ + function flashFee(address token, uint256 amount) public view virtual override returns (uint256) { + require(token == address(this), "ERC20FlashMint: wrong token"); + // silence warning about unused variable without the addition of bytecode. + amount; + return 0; + } + + /** + * @dev Performs a flash loan. New tokens are minted and sent to the + * `receiver`, who is required to implement the {IERC3156FlashBorrower} + * interface. By the end of the flash loan, the receiver is expected to own + * amount + fee tokens and have them approved back to the token contract itself so + * they can be burned. + * @param receiver The receiver of the flash loan. Should implement the + * {IERC3156FlashBorrower.onFlashLoan} interface. + * @param token The token to be flash loaned. Only `address(this)` is + * supported. + * @param amount The amount of tokens to be loaned. + * @param data An arbitrary datafield that is passed to the receiver. + * @return `true` is the flash loan was successful. + */ + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) public virtual override returns (bool) { + uint256 fee = flashFee(token, amount); + _mint(address(receiver), amount); + require( + receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, + "ERC20FlashMint: invalid return value" + ); + uint256 currentAllowance = allowance(address(receiver), address(this)); + require(currentAllowance >= amount + fee, "ERC20FlashMint: allowance does not allow refund"); + _approve(address(receiver), address(this), currentAllowance - amount - fee); + _burn(address(receiver), amount + fee); + return true; + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Pausable.sol b/certora/munged/token/ERC20/extensions/ERC20Pausable.sol new file mode 100644 index 000000000..5c4963137 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Pausable.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Pausable.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../../../security/Pausable.sol"; + +/** + * @dev ERC20 token with pausable token transfers, minting and burning. + * + * Useful for scenarios such as preventing trades until the end of an evaluation + * period, or having an emergency switch for freezing all token transfers in the + * event of a large bug. + */ +abstract contract ERC20Pausable is ERC20, Pausable { + /** + * @dev See {ERC20-_beforeTokenTransfer}. + * + * Requirements: + * + * - the contract must not be paused. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override { + super._beforeTokenTransfer(from, to, amount); + + require(!paused(), "ERC20Pausable: token transfer while paused"); + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Snapshot.sol b/certora/munged/token/ERC20/extensions/ERC20Snapshot.sol new file mode 100644 index 000000000..6fbf54a3a --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Snapshot.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Snapshot.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../../../utils/Arrays.sol"; +import "../../../utils/Counters.sol"; + +/** + * @dev This contract extends an ERC20 token with a snapshot mechanism. When a snapshot is created, the balances and + * total supply at the time are recorded for later access. + * + * This can be used to safely create mechanisms based on token balances such as trustless dividends or weighted voting. + * In naive implementations it's possible to perform a "double spend" attack by reusing the same balance from different + * accounts. By using snapshots to calculate dividends or voting power, those attacks no longer apply. It can also be + * used to create an efficient ERC20 forking mechanism. + * + * Snapshots are created by the internal {_snapshot} function, which will emit the {Snapshot} event and return a + * snapshot id. To get the total supply at the time of a snapshot, call the function {totalSupplyAt} with the snapshot + * id. To get the balance of an account at the time of a snapshot, call the {balanceOfAt} function with the snapshot id + * and the account address. + * + * NOTE: Snapshot policy can be customized by overriding the {_getCurrentSnapshotId} method. For example, having it + * return `block.number` will trigger the creation of snapshot at the begining of each new block. When overridding this + * function, be careful about the monotonicity of its result. Non-monotonic snapshot ids will break the contract. + * + * Implementing snapshots for every block using this method will incur significant gas costs. For a gas-efficient + * alternative consider {ERC20Votes}. + * + * ==== Gas Costs + * + * Snapshots are efficient. Snapshot creation is _O(1)_. Retrieval of balances or total supply from a snapshot is _O(log + * n)_ in the number of snapshots that have been created, although _n_ for a specific account will generally be much + * smaller since identical balances in subsequent snapshots are stored as a single entry. + * + * There is a constant overhead for normal ERC20 transfers due to the additional snapshot bookkeeping. This overhead is + * only significant for the first transfer that immediately follows a snapshot for a particular account. Subsequent + * transfers will have normal cost until the next snapshot, and so on. + */ + +abstract contract ERC20Snapshot is ERC20 { + // Inspired by Jordi Baylina's MiniMeToken to record historical balances: + // https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol + + using Arrays for uint256[]; + using Counters for Counters.Counter; + + // Snapshotted values have arrays of ids and the value corresponding to that id. These could be an array of a + // Snapshot struct, but that would impede usage of functions that work on an array. + struct Snapshots { + uint256[] ids; + uint256[] values; + } + + mapping(address => Snapshots) private _accountBalanceSnapshots; + Snapshots private _totalSupplySnapshots; + + // Snapshot ids increase monotonically, with the first value being 1. An id of 0 is invalid. + Counters.Counter private _currentSnapshotId; + + /** + * @dev Emitted by {_snapshot} when a snapshot identified by `id` is created. + */ + event Snapshot(uint256 id); + + /** + * @dev Creates a new snapshot and returns its snapshot id. + * + * Emits a {Snapshot} event that contains the same id. + * + * {_snapshot} is `internal` and you have to decide how to expose it externally. Its usage may be restricted to a + * set of accounts, for example using {AccessControl}, or it may be open to the public. + * + * [WARNING] + * ==== + * While an open way of calling {_snapshot} is required for certain trust minimization mechanisms such as forking, + * you must consider that it can potentially be used by attackers in two ways. + * + * First, it can be used to increase the cost of retrieval of values from snapshots, although it will grow + * logarithmically thus rendering this attack ineffective in the long term. Second, it can be used to target + * specific accounts and increase the cost of ERC20 transfers for them, in the ways specified in the Gas Costs + * section above. + * + * We haven't measured the actual numbers; if this is something you're interested in please reach out to us. + * ==== + */ + function _snapshot() internal virtual returns (uint256) { + _currentSnapshotId.increment(); + + uint256 currentId = _getCurrentSnapshotId(); + emit Snapshot(currentId); + return currentId; + } + + /** + * @dev Get the current snapshotId + */ + function _getCurrentSnapshotId() internal view virtual returns (uint256) { + return _currentSnapshotId.current(); + } + + /** + * @dev Retrieves the balance of `account` at the time `snapshotId` was created. + */ + function balanceOfAt(address account, uint256 snapshotId) public view virtual returns (uint256) { + (bool snapshotted, uint256 value) = _valueAt(snapshotId, _accountBalanceSnapshots[account]); + + return snapshotted ? value : balanceOf(account); + } + + /** + * @dev Retrieves the total supply at the time `snapshotId` was created. + */ + function totalSupplyAt(uint256 snapshotId) public view virtual returns (uint256) { + (bool snapshotted, uint256 value) = _valueAt(snapshotId, _totalSupplySnapshots); + + return snapshotted ? value : totalSupply(); + } + + // Update balance and/or total supply snapshots before the values are modified. This is implemented + // in the _beforeTokenTransfer hook, which is executed for _mint, _burn, and _transfer operations. + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override { + super._beforeTokenTransfer(from, to, amount); + + if (from == address(0)) { + // mint + _updateAccountSnapshot(to); + _updateTotalSupplySnapshot(); + } else if (to == address(0)) { + // burn + _updateAccountSnapshot(from); + _updateTotalSupplySnapshot(); + } else { + // transfer + _updateAccountSnapshot(from); + _updateAccountSnapshot(to); + } + } + + function _valueAt(uint256 snapshotId, Snapshots storage snapshots) private view returns (bool, uint256) { + require(snapshotId > 0, "ERC20Snapshot: id is 0"); + require(snapshotId <= _getCurrentSnapshotId(), "ERC20Snapshot: nonexistent id"); + + // When a valid snapshot is queried, there are three possibilities: + // a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never + // created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds + // to this id is the current one. + // b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the + // requested id, and its value is the one to return. + // c) More snapshots were created after the requested one, and the queried value was later modified. There will be + // no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is + // larger than the requested one. + // + // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if + // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does + // exactly this. + + uint256 index = snapshots.ids.findUpperBound(snapshotId); + + if (index == snapshots.ids.length) { + return (false, 0); + } else { + return (true, snapshots.values[index]); + } + } + + function _updateAccountSnapshot(address account) private { + _updateSnapshot(_accountBalanceSnapshots[account], balanceOf(account)); + } + + function _updateTotalSupplySnapshot() private { + _updateSnapshot(_totalSupplySnapshots, totalSupply()); + } + + function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) private { + uint256 currentId = _getCurrentSnapshotId(); + if (_lastSnapshotId(snapshots.ids) < currentId) { + snapshots.ids.push(currentId); + snapshots.values.push(currentValue); + } + } + + function _lastSnapshotId(uint256[] storage ids) private view returns (uint256) { + if (ids.length == 0) { + return 0; + } else { + return ids[ids.length - 1]; + } + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Votes.sol b/certora/munged/token/ERC20/extensions/ERC20Votes.sol new file mode 100644 index 000000000..06fd68831 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Votes.sol @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Votes.sol) + +pragma solidity ^0.8.0; + +import "./draft-ERC20Permit.sol"; +import "../../../utils/math/Math.sol"; +import "../../../utils/math/SafeCast.sol"; +import "../../../utils/cryptography/ECDSA.sol"; + +/** + * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's, + * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1. + * + * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module. + * + * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either + * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting + * power can be queried through the public accessors {getVotes} and {getPastVotes}. + * + * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it + * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + * Enabling self-delegation can easily be done by overriding the {delegates} function. Keep in mind however that this + * will significantly increase the base gas cost of transfers. + * + * _Available since v4.2._ + */ +abstract contract ERC20Votes is ERC20Permit { + struct Checkpoint { + uint32 fromBlock; + uint224 votes; + } + + bytes32 private constant _DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + + mapping(address => address) private _delegates; + mapping(address => Checkpoint[]) private _checkpoints; + Checkpoint[] private _totalSupplyCheckpoints; + + /** + * @dev Emitted when an account changes their delegate. + */ + event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); + + /** + * @dev Emitted when a token transfer or delegate change results in changes to an account's voting power. + */ + event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); + + /** + * @dev Get the `pos`-th checkpoint for `account`. + */ + function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) { + return _checkpoints[account][pos]; + } + + /** + * @dev Get number of checkpoints for `account`. + */ + function numCheckpoints(address account) public view virtual returns (uint32) { + return SafeCast.toUint32(_checkpoints[account].length); + } + + /** + * @dev Get the address `account` is currently delegating to. + */ + function delegates(address account) public view virtual returns (address) { + return _delegates[account]; + } + + /** + * @dev Gets the current votes balance for `account` + */ + function getVotes(address account) public view returns (uint256) { + uint256 pos = _checkpoints[account].length; + return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; + } + + /** + * @dev Retrieve the number of votes for `account` at the end of `blockNumber`. + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastVotes(address account, uint256 blockNumber) public view virtual returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_checkpoints[account], blockNumber); + } + + /** + * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. + * It is but NOT the sum of all the delegated votes! + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastTotalSupply(uint256 blockNumber) public view returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); + } + + /** + * @dev Lookup a value in a list of (sorted) checkpoints. + */ + function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) { + // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. + // + // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). + // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant. + // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) + // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) + // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not + // out of bounds (in which case we're looking too far in the past and the result is 0). + // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is + // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out + // the same. + uint256 high = ckpts.length; + uint256 low = 0; + while (low < high) { + uint256 mid = Math.average(low, high); + if (ckpts[mid].fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + return high == 0 ? 0 : ckpts[high - 1].votes; + } + + /** + * @dev Delegate votes from the sender to `delegatee`. + */ + function delegate(address delegatee) public virtual { + _delegate(_msgSender(), delegatee); + } + + /** + * @dev Delegates votes from signer to `delegatee` + */ + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual { + require(block.timestamp <= expiry, "ERC20Votes: signature expired"); + address signer = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), + v, + r, + s + ); + require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce"); + _delegate(signer, delegatee); + } + + /** + * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). + */ + function _maxSupply() internal view virtual returns (uint224) { + return type(uint224).max; + } + + /** + * @dev Snapshots the totalSupply after it has been increased. + */ + function _mint(address account, uint256 amount) internal virtual override { + super._mint(account, amount); + require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); + + _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); + } + + /** + * @dev Snapshots the totalSupply after it has been decreased. + */ + function _burn(address account, uint256 amount) internal virtual override { + super._burn(account, amount); + + _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); + } + + /** + * @dev Move voting power when tokens are transferred. + * + * Emits a {DelegateVotesChanged} event. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override { + super._afterTokenTransfer(from, to, amount); + + _moveVotingPower(delegates(from), delegates(to), amount); + } + + /** + * @dev Change delegation for `delegator` to `delegatee`. + * + * Emits events {DelegateChanged} and {DelegateVotesChanged}. + */ + function _delegate(address delegator, address delegatee) internal virtual { + address currentDelegate = delegates(delegator); + uint256 delegatorBalance = balanceOf(delegator); + _delegates[delegator] = delegatee; + + emit DelegateChanged(delegator, currentDelegate, delegatee); + + _moveVotingPower(currentDelegate, delegatee, delegatorBalance); + } + + function _moveVotingPower( + address src, + address dst, + uint256 amount + ) private { + if (src != dst && amount > 0) { + if (src != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); + emit DelegateVotesChanged(src, oldWeight, newWeight); + } + + if (dst != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); + emit DelegateVotesChanged(dst, oldWeight, newWeight); + } + } + } + + function _writeCheckpoint( + Checkpoint[] storage ckpts, + function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; + newWeight = op(oldWeight, delta); + + if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { + ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); + } else { + ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + } + } + + function _add(uint256 a, uint256 b) private pure returns (uint256) { + return a + b; + } + + function _subtract(uint256 a, uint256 b) private pure returns (uint256) { + return a - b; + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20VotesComp.sol b/certora/munged/token/ERC20/extensions/ERC20VotesComp.sol new file mode 100644 index 000000000..52151111a --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20VotesComp.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20VotesComp.sol) + +pragma solidity ^0.8.0; + +import "./ERC20Votes.sol"; + +/** + * @dev Extension of ERC20 to support Compound's voting and delegation. This version exactly matches Compound's + * interface, with the drawback of only supporting supply up to (2^96^ - 1). + * + * NOTE: You should use this contract if you need exact compatibility with COMP (for example in order to use your token + * with Governor Alpha or Bravo) and if you are sure the supply cap of 2^96^ is enough for you. Otherwise, use the + * {ERC20Votes} variant of this module. + * + * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either + * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting + * power can be queried through the public accessors {getCurrentVotes} and {getPriorVotes}. + * + * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it + * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + * Enabling self-delegation can easily be done by overriding the {delegates} function. Keep in mind however that this + * will significantly increase the base gas cost of transfers. + * + * _Available since v4.2._ + */ +abstract contract ERC20VotesComp is ERC20Votes { + /** + * @dev Comp version of the {getVotes} accessor, with `uint96` return type. + */ + function getCurrentVotes(address account) external view returns (uint96) { + return SafeCast.toUint96(getVotes(account)); + } + + /** + * @dev Comp version of the {getPastVotes} accessor, with `uint96` return type. + */ + function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96) { + return SafeCast.toUint96(getPastVotes(account, blockNumber)); + } + + /** + * @dev Maximum token supply. Reduced to `type(uint96).max` (2^96^ - 1) to fit COMP interface. + */ + function _maxSupply() internal view virtual override returns (uint224) { + return type(uint96).max; + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol b/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol new file mode 100644 index 000000000..4404c3821 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Wrapper.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../utils/SafeERC20.sol"; + +/** + * @dev Extension of the ERC20 token contract to support token wrapping. + * + * Users can deposit and withdraw "underlying tokens" and receive a matching number of "wrapped tokens". This is useful + * in conjunction with other modules. For example, combining this wrapping mechanism with {ERC20Votes} will allow the + * wrapping of an existing "basic" ERC20 into a governance token. + * + * _Available since v4.2._ + */ +abstract contract ERC20Wrapper is ERC20 { + IERC20 public immutable underlying; + + constructor(IERC20 underlyingToken) { + underlying = underlyingToken; + } + + /** + * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens. + */ + function depositFor(address account, uint256 amount) public virtual returns (bool) { + SafeERC20.safeTransferFrom(underlying, _msgSender(), address(this), amount); + _mint(account, amount); + return true; + } + + /** + * @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens. + */ + function withdrawTo(address account, uint256 amount) public virtual returns (bool) { + _burn(_msgSender(), amount); + SafeERC20.safeTransfer(underlying, account, amount); + return true; + } + + /** + * @dev Mint wrapped token to cover any underlyingTokens that would have been transfered by mistake. Internal + * function that can be exposed with access control if desired. + */ + function _recover(address account) internal virtual returns (uint256) { + uint256 value = underlying.balanceOf(address(this)) - totalSupply(); + _mint(account, value); + return value; + } +} diff --git a/certora/munged/token/ERC20/extensions/IERC20Metadata.sol b/certora/munged/token/ERC20/extensions/IERC20Metadata.sol new file mode 100644 index 000000000..ba019f213 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/IERC20Metadata.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/IERC20Metadata.sol) + +pragma solidity ^0.8.0; + +import "../IERC20.sol"; + +/** + * @dev Interface for the optional metadata functions from the ERC20 standard. + * + * _Available since v4.1._ + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} diff --git a/certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol b/certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol new file mode 100644 index 000000000..920a5f5bf --- /dev/null +++ b/certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/draft-ERC20Permit.sol) + +pragma solidity ^0.8.0; + +import "./draft-IERC20Permit.sol"; +import "../ERC20.sol"; +import "../../../utils/cryptography/draft-EIP712.sol"; +import "../../../utils/cryptography/ECDSA.sol"; +import "../../../utils/Counters.sol"; + +/** + * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + * + * _Available since v3.4._ + */ +abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { + using Counters for Counters.Counter; + + mapping(address => Counters.Counter) private _nonces; + + // solhint-disable-next-line var-name-mixedcase + bytes32 private immutable _PERMIT_TYPEHASH = + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + + /** + * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. + * + * It's a good idea to use the same `name` that is defined as the ERC20 token name. + */ + constructor(string memory name) EIP712(name, "1") {} + + /** + * @dev See {IERC20Permit-permit}. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); + + bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); + + bytes32 hash = _hashTypedDataV4(structHash); + + address signer = ECDSA.recover(hash, v, r, s); + require(signer == owner, "ERC20Permit: invalid signature"); + + _approve(owner, spender, value); + } + + /** + * @dev See {IERC20Permit-nonces}. + */ + function nonces(address owner) public view virtual override returns (uint256) { + return _nonces[owner].current(); + } + + /** + * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view override returns (bytes32) { + return _domainSeparatorV4(); + } + + /** + * @dev "Consume a nonce": return the current value and increment. + * + * _Available since v4.1._ + */ + function _useNonce(address owner) internal virtual returns (uint256 current) { + Counters.Counter storage nonce = _nonces[owner]; + current = nonce.current(); + nonce.increment(); + } +} diff --git a/certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol b/certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol new file mode 100644 index 000000000..11a49cad9 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/draft-IERC20Permit.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + */ +interface IERC20Permit { + /** + * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, + * given ``owner``'s signed approval. + * + * IMPORTANT: The same issues {IERC20-approve} has related to transaction + * ordering also apply here. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `deadline` must be a timestamp in the future. + * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` + * over the EIP712-formatted function arguments. + * - the signature must use ``owner``'s current nonce (see {nonces}). + * + * For more information on the signature format, see the + * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP + * section]. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + /** + * @dev Returns the current nonce for `owner`. This value must be + * included whenever a signature is generated for {permit}. + * + * Every successful call to {permit} increases ``owner``'s nonce by one. This + * prevents a signature from being used multiple times. + */ + function nonces(address owner) external view returns (uint256); + + /** + * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32); +} diff --git a/certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol b/certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol new file mode 100644 index 000000000..e761a6ac9 --- /dev/null +++ b/certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/presets/ERC20PresetFixedSupply.sol) +pragma solidity ^0.8.0; + +import "../extensions/ERC20Burnable.sol"; + +/** + * @dev {ERC20} token, including: + * + * - Preminted initial supply + * - Ability for holders to burn (destroy) their tokens + * - No access control mechanism (for minting/pausing) and hence no governance + * + * This contract uses {ERC20Burnable} to include burn capabilities - head to + * its documentation for details. + * + * _Available since v3.4._ + */ +contract ERC20PresetFixedSupply is ERC20Burnable { + /** + * @dev Mints `initialSupply` amount of token and transfers them to `owner`. + * + * See {ERC20-constructor}. + */ + constructor( + string memory name, + string memory symbol, + uint256 initialSupply, + address owner + ) ERC20(name, symbol) { + _mint(owner, initialSupply); + } +} diff --git a/certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol b/certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol new file mode 100644 index 000000000..1ba34a43e --- /dev/null +++ b/certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/presets/ERC20PresetMinterPauser.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../extensions/ERC20Burnable.sol"; +import "../extensions/ERC20Pausable.sol"; +import "../../../access/AccessControlEnumerable.sol"; +import "../../../utils/Context.sol"; + +/** + * @dev {ERC20} token, including: + * + * - ability for holders to burn (destroy) their tokens + * - a minter role that allows for token minting (creation) + * - a pauser role that allows to stop all token transfers + * + * This contract uses {AccessControl} to lock permissioned functions using the + * different roles - head to its documentation for details. + * + * The account that deploys the contract will be granted the minter and pauser + * roles, as well as the default admin role, which will let it grant both minter + * and pauser roles to other accounts. + */ +contract ERC20PresetMinterPauser is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + /** + * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the + * account that deploys the contract. + * + * See {ERC20-constructor}. + */ + constructor(string memory name, string memory symbol) ERC20(name, symbol) { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + + _setupRole(MINTER_ROLE, _msgSender()); + _setupRole(PAUSER_ROLE, _msgSender()); + } + + /** + * @dev Creates `amount` new tokens for `to`. + * + * See {ERC20-_mint}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function mint(address to, uint256 amount) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint"); + _mint(to, amount); + } + + /** + * @dev Pauses all token transfers. + * + * See {ERC20Pausable} and {Pausable-_pause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function pause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause"); + _pause(); + } + + /** + * @dev Unpauses all token transfers. + * + * See {ERC20Pausable} and {Pausable-_unpause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function unpause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause"); + _unpause(); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override(ERC20, ERC20Pausable) { + super._beforeTokenTransfer(from, to, amount); + } +} diff --git a/certora/munged/token/ERC20/utils/SafeERC20.sol b/certora/munged/token/ERC20/utils/SafeERC20.sol new file mode 100644 index 000000000..09e38db62 --- /dev/null +++ b/certora/munged/token/ERC20/utils/SafeERC20.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/utils/SafeERC20.sol) + +pragma solidity ^0.8.0; + +import "../IERC20.sol"; +import "../../../utils/Address.sol"; + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using Address for address; + + function safeTransfer( + IERC20 token, + address to, + uint256 value + ) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom( + IERC20 token, + address from, + address to, + uint256 value + ) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + /** + * @dev Deprecated. This function has issues similar to the ones found in + * {IERC20-approve}, and its usage is discouraged. + * + * Whenever possible, use {safeIncreaseAllowance} and + * {safeDecreaseAllowance} instead. + */ + function safeApprove( + IERC20 token, + address spender, + uint256 value + ) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function safeIncreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + uint256 newAllowance = token.allowance(address(this), spender) + value; + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + function safeDecreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + unchecked { + uint256 oldAllowance = token.allowance(address(this), spender); + require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); + uint256 newAllowance = oldAllowance - value; + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); + if (returndata.length > 0) { + // Return data is optional + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} diff --git a/certora/munged/token/ERC20/utils/TokenTimelock.sol b/certora/munged/token/ERC20/utils/TokenTimelock.sol new file mode 100644 index 000000000..5f9acceee --- /dev/null +++ b/certora/munged/token/ERC20/utils/TokenTimelock.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC20/utils/TokenTimelock.sol) + +pragma solidity ^0.8.0; + +import "./SafeERC20.sol"; + +/** + * @dev A token holder contract that will allow a beneficiary to extract the + * tokens after a given release time. + * + * Useful for simple vesting schedules like "advisors get all of their tokens + * after 1 year". + */ +contract TokenTimelock { + using SafeERC20 for IERC20; + + // ERC20 basic token contract being held + IERC20 private immutable _token; + + // beneficiary of tokens after they are released + address private immutable _beneficiary; + + // timestamp when token release is enabled + uint256 private immutable _releaseTime; + + constructor( + IERC20 token_, + address beneficiary_, + uint256 releaseTime_ + ) { + require(releaseTime_ > block.timestamp, "TokenTimelock: release time is before current time"); + _token = token_; + _beneficiary = beneficiary_; + _releaseTime = releaseTime_; + } + + /** + * @return the token being held. + */ + function token() public view virtual returns (IERC20) { + return _token; + } + + /** + * @return the beneficiary of the tokens. + */ + function beneficiary() public view virtual returns (address) { + return _beneficiary; + } + + /** + * @return the time when the tokens are released. + */ + function releaseTime() public view virtual returns (uint256) { + return _releaseTime; + } + + /** + * @notice Transfers tokens held by timelock to beneficiary. + */ + function release() public virtual { + require(block.timestamp >= releaseTime(), "TokenTimelock: current time is before release time"); + + uint256 amount = token().balanceOf(address(this)); + require(amount > 0, "TokenTimelock: no tokens to release"); + + token().safeTransfer(beneficiary(), amount); + } +} diff --git a/certora/munged/token/ERC721/ERC721.sol b/certora/munged/token/ERC721/ERC721.sol new file mode 100644 index 000000000..dbd91bcbc --- /dev/null +++ b/certora/munged/token/ERC721/ERC721.sol @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/ERC721.sol) + +pragma solidity ^0.8.0; + +import "./IERC721.sol"; +import "./IERC721Receiver.sol"; +import "./extensions/IERC721Metadata.sol"; +import "../../utils/Address.sol"; +import "../../utils/Context.sol"; +import "../../utils/Strings.sol"; +import "../../utils/introspection/ERC165.sol"; + +/** + * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including + * the Metadata extension, but not including the Enumerable extension, which is available separately as + * {ERC721Enumerable}. + */ +contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { + using Address for address; + using Strings for uint256; + + // Token name + string private _name; + + // Token symbol + string private _symbol; + + // Mapping from token ID to owner address + mapping(uint256 => address) private _owners; + + // Mapping owner address to token count + mapping(address => uint256) private _balances; + + // Mapping from token ID to approved address + mapping(uint256 => address) private _tokenApprovals; + + // Mapping from owner to operator approvals + mapping(address => mapping(address => bool)) private _operatorApprovals; + + /** + * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IERC721).interfaceId || + interfaceId == type(IERC721Metadata).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC721-balanceOf}. + */ + function balanceOf(address owner) public view virtual override returns (uint256) { + require(owner != address(0), "ERC721: balance query for the zero address"); + return _balances[owner]; + } + + /** + * @dev See {IERC721-ownerOf}. + */ + function ownerOf(uint256 tokenId) public view virtual override returns (address) { + address owner = _owners[tokenId]; + require(owner != address(0), "ERC721: owner query for nonexistent token"); + return owner; + } + + /** + * @dev See {IERC721Metadata-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IERC721Metadata-symbol}. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev See {IERC721Metadata-tokenURI}. + */ + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); + + string memory baseURI = _baseURI(); + return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; + } + + /** + * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each + * token will be the concatenation of the `baseURI` and the `tokenId`. Empty + * by default, can be overriden in child contracts. + */ + function _baseURI() internal view virtual returns (string memory) { + return ""; + } + + /** + * @dev See {IERC721-approve}. + */ + function approve(address to, uint256 tokenId) public virtual override { + address owner = ERC721.ownerOf(tokenId); + require(to != owner, "ERC721: approval to current owner"); + + require( + _msgSender() == owner || isApprovedForAll(owner, _msgSender()), + "ERC721: approve caller is not owner nor approved for all" + ); + + _approve(to, tokenId); + } + + /** + * @dev See {IERC721-getApproved}. + */ + function getApproved(uint256 tokenId) public view virtual override returns (address) { + require(_exists(tokenId), "ERC721: approved query for nonexistent token"); + + return _tokenApprovals[tokenId]; + } + + /** + * @dev See {IERC721-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) public virtual override { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /** + * @dev See {IERC721-isApprovedForAll}. + */ + function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { + return _operatorApprovals[owner][operator]; + } + + /** + * @dev See {IERC721-transferFrom}. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + //solhint-disable-next-line max-line-length + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); + + _transfer(from, to, tokenId); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + safeTransferFrom(from, to, tokenId, ""); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) public virtual override { + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); + _safeTransfer(from, to, tokenId, _data); + } + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * `_data` is additional data, it has no specified format and it is sent in call to `to`. + * + * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. + * implement alternative mechanisms to perform token transfer, such as signature-based. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeTransfer( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) internal virtual { + _transfer(from, to, tokenId); + require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); + } + + /** + * @dev Returns whether `tokenId` exists. + * + * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. + * + * Tokens start existing when they are minted (`_mint`), + * and stop existing when they are burned (`_burn`). + */ + function _exists(uint256 tokenId) internal view virtual returns (bool) { + return _owners[tokenId] != address(0); + } + + /** + * @dev Returns whether `spender` is allowed to manage `tokenId`. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { + require(_exists(tokenId), "ERC721: operator query for nonexistent token"); + address owner = ERC721.ownerOf(tokenId); + return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); + } + + /** + * @dev Safely mints `tokenId` and transfers it to `to`. + * + * Requirements: + * + * - `tokenId` must not exist. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeMint(address to, uint256 tokenId) internal virtual { + _safeMint(to, tokenId, ""); + } + + /** + * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is + * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. + */ + function _safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) internal virtual { + _mint(to, tokenId); + require( + _checkOnERC721Received(address(0), to, tokenId, _data), + "ERC721: transfer to non ERC721Receiver implementer" + ); + } + + /** + * @dev Mints `tokenId` and transfers it to `to`. + * + * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible + * + * Requirements: + * + * - `tokenId` must not exist. + * - `to` cannot be the zero address. + * + * Emits a {Transfer} event. + */ + function _mint(address to, uint256 tokenId) internal virtual { + require(to != address(0), "ERC721: mint to the zero address"); + require(!_exists(tokenId), "ERC721: token already minted"); + + _beforeTokenTransfer(address(0), to, tokenId); + + _balances[to] += 1; + _owners[tokenId] = to; + + emit Transfer(address(0), to, tokenId); + } + + /** + * @dev Destroys `tokenId`. + * The approval is cleared when the token is burned. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Transfer} event. + */ + function _burn(uint256 tokenId) internal virtual { + address owner = ERC721.ownerOf(tokenId); + + _beforeTokenTransfer(owner, address(0), tokenId); + + // Clear approvals + _approve(address(0), tokenId); + + _balances[owner] -= 1; + delete _owners[tokenId]; + + emit Transfer(owner, address(0), tokenId); + } + + /** + * @dev Transfers `tokenId` from `from` to `to`. + * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * + * Emits a {Transfer} event. + */ + function _transfer( + address from, + address to, + uint256 tokenId + ) internal virtual { + require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); + require(to != address(0), "ERC721: transfer to the zero address"); + + _beforeTokenTransfer(from, to, tokenId); + + // Clear approvals from the previous owner + _approve(address(0), tokenId); + + _balances[from] -= 1; + _balances[to] += 1; + _owners[tokenId] = to; + + emit Transfer(from, to, tokenId); + } + + /** + * @dev Approve `to` to operate on `tokenId` + * + * Emits a {Approval} event. + */ + function _approve(address to, uint256 tokenId) internal virtual { + _tokenApprovals[tokenId] = to; + emit Approval(ERC721.ownerOf(tokenId), to, tokenId); + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Emits a {ApprovalForAll} event. + */ + function _setApprovalForAll( + address owner, + address operator, + bool approved + ) internal virtual { + require(owner != operator, "ERC721: approve to caller"); + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. + * The call is not executed if the target address is not a contract. + * + * @param from address representing the previous owner of the given token ID + * @param to target address that will receive the tokens + * @param tokenId uint256 ID of the token to be transferred + * @param _data bytes optional data to send along with the call + * @return bool whether the call correctly returned the expected magic value + */ + function _checkOnERC721Received( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) private returns (bool) { + if (to.isContract()) { + try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { + return retval == IERC721Receiver.onERC721Received.selector; + } catch (bytes memory reason) { + if (reason.length == 0) { + revert("ERC721: transfer to non ERC721Receiver implementer"); + } else { + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } else { + return true; + } + } + + /** + * @dev Hook that is called before any token transfer. This includes minting + * and burning. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be + * transferred to `to`. + * - When `from` is zero, `tokenId` will be minted for `to`. + * - When `to` is zero, ``from``'s `tokenId` will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual {} +} diff --git a/certora/munged/token/ERC721/IERC721.sol b/certora/munged/token/ERC721/IERC721.sol new file mode 100644 index 000000000..f5e91749e --- /dev/null +++ b/certora/munged/token/ERC721/IERC721.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/IERC721.sol) + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * @dev Required interface of an ERC721 compliant contract. + */ +interface IERC721 is IERC165 { + /** + * @dev Emitted when `tokenId` token is transferred from `from` to `to`. + */ + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. + */ + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. + */ + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /** + * @dev Returns the number of tokens in ``owner``'s account. + */ + function balanceOf(address owner) external view returns (uint256 balance); + + /** + * @dev Returns the owner of the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function ownerOf(uint256 tokenId) external view returns (address owner); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /** + * @dev Transfers `tokenId` token from `from` to `to`. + * + * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. + * The approval is cleared when the token is transferred. + * + * Only a single account can be approved at a time, so approving the zero address clears previous approvals. + * + * Requirements: + * + * - The caller must own the token or be an approved operator. + * - `tokenId` must exist. + * + * Emits an {Approval} event. + */ + function approve(address to, uint256 tokenId) external; + + /** + * @dev Returns the account approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getApproved(uint256 tokenId) external view returns (address operator); + + /** + * @dev Approve or remove `operator` as an operator for the caller. + * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the caller. + * + * Emits an {ApprovalForAll} event. + */ + function setApprovalForAll(address operator, bool _approved) external; + + /** + * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. + * + * See {setApprovalForAll} + */ + function isApprovedForAll(address owner, address operator) external view returns (bool); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes calldata data + ) external; +} diff --git a/certora/munged/token/ERC721/IERC721Receiver.sol b/certora/munged/token/ERC721/IERC721Receiver.sol new file mode 100644 index 000000000..d10250ea8 --- /dev/null +++ b/certora/munged/token/ERC721/IERC721Receiver.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/IERC721Receiver.sol) + +pragma solidity ^0.8.0; + +/** + * @title ERC721 token receiver interface + * @dev Interface for any contract that wants to support safeTransfers + * from ERC721 asset contracts. + */ +interface IERC721Receiver { + /** + * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} + * by `operator` from `from`, this function is called. + * + * It must return its Solidity selector to confirm the token transfer. + * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. + * + * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. + */ + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external returns (bytes4); +} diff --git a/certora/munged/token/ERC721/README.adoc b/certora/munged/token/ERC721/README.adoc new file mode 100644 index 000000000..f1122c53a --- /dev/null +++ b/certora/munged/token/ERC721/README.adoc @@ -0,0 +1,52 @@ += ERC 721 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc721 + +This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-721[ERC721 Non-Fungible Token Standard]. + +TIP: For a walk through on how to create an ERC721 token read our xref:ROOT:erc721.adoc[ERC721 guide]. + +The EIP consists of three interfaces, found here as {IERC721}, {IERC721Metadata}, and {IERC721Enumerable}. Only the first one is required in a contract to be ERC721 compliant. The core interface and the metadata extension are both implemented in {ERC721}. The enumerable extension is provided separately in {ERC721Enumerable}. + +Additionally, {IERC721Receiver} can be used to prevent tokens from becoming forever locked in contracts. Imagine sending an in-game item to an exchange address that can't send it back!. When using <>, the token contract checks to see that the receiver is an {IERC721Receiver}, which implies that it knows how to handle {ERC721} tokens. If you're writing a contract that needs to receive {ERC721} tokens, you'll want to include this interface. + +Additionally there are multiple custom extensions, including: + +* designation of addresses that can pause token transfers for all users ({ERC721Pausable}). +* destruction of own tokens ({ERC721Burnable}). + +NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC721 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc721.adoc#Presets[ERC721 Presets] (such as {ERC721PresetMinterPauserAutoId}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. + + +== Core + +{{IERC721}} + +{{IERC721Metadata}} + +{{IERC721Enumerable}} + +{{ERC721}} + +{{ERC721Enumerable}} + +{{IERC721Receiver}} + +== Extensions + +{{ERC721Pausable}} + +{{ERC721Burnable}} + +{{ERC721URIStorage}} + +== Presets + +These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC721PresetMinterPauserAutoId}} + +== Utilities + +{{ERC721Holder}} diff --git a/certora/munged/token/ERC721/extensions/ERC721Burnable.sol b/certora/munged/token/ERC721/extensions/ERC721Burnable.sol new file mode 100644 index 000000000..b41ba74d0 --- /dev/null +++ b/certora/munged/token/ERC721/extensions/ERC721Burnable.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/ERC721Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../../utils/Context.sol"; + +/** + * @title ERC721 Burnable Token + * @dev ERC721 Token that can be irreversibly burned (destroyed). + */ +abstract contract ERC721Burnable is Context, ERC721 { + /** + * @dev Burns `tokenId`. See {ERC721-_burn}. + * + * Requirements: + * + * - The caller must own `tokenId` or be an approved operator. + */ + function burn(uint256 tokenId) public virtual { + //solhint-disable-next-line max-line-length + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Burnable: caller is not owner nor approved"); + _burn(tokenId); + } +} diff --git a/certora/munged/token/ERC721/extensions/ERC721Enumerable.sol b/certora/munged/token/ERC721/extensions/ERC721Enumerable.sol new file mode 100644 index 000000000..49b31389b --- /dev/null +++ b/certora/munged/token/ERC721/extensions/ERC721Enumerable.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/ERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "./IERC721Enumerable.sol"; + +/** + * @dev This implements an optional extension of {ERC721} defined in the EIP that adds + * enumerability of all the token ids in the contract as well as all token ids owned by each + * account. + */ +abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { + // Mapping from owner to list of owned token IDs + mapping(address => mapping(uint256 => uint256)) private _ownedTokens; + + // Mapping from token ID to index of the owner tokens list + mapping(uint256 => uint256) private _ownedTokensIndex; + + // Array with all token ids, used for enumeration + uint256[] private _allTokens; + + // Mapping from token id to position in the allTokens array + mapping(uint256 => uint256) private _allTokensIndex; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { + return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { + require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds"); + return _ownedTokens[owner][index]; + } + + /** + * @dev See {IERC721Enumerable-totalSupply}. + */ + function totalSupply() public view virtual override returns (uint256) { + return _allTokens.length; + } + + /** + * @dev See {IERC721Enumerable-tokenByIndex}. + */ + function tokenByIndex(uint256 index) public view virtual override returns (uint256) { + require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds"); + return _allTokens[index]; + } + + /** + * @dev Hook that is called before any token transfer. This includes minting + * and burning. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be + * transferred to `to`. + * - When `from` is zero, `tokenId` will be minted for `to`. + * - When `to` is zero, ``from``'s `tokenId` will be burned. + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual override { + super._beforeTokenTransfer(from, to, tokenId); + + if (from == address(0)) { + _addTokenToAllTokensEnumeration(tokenId); + } else if (from != to) { + _removeTokenFromOwnerEnumeration(from, tokenId); + } + if (to == address(0)) { + _removeTokenFromAllTokensEnumeration(tokenId); + } else if (to != from) { + _addTokenToOwnerEnumeration(to, tokenId); + } + } + + /** + * @dev Private function to add a token to this extension's ownership-tracking data structures. + * @param to address representing the new owner of the given token ID + * @param tokenId uint256 ID of the token to be added to the tokens list of the given address + */ + function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { + uint256 length = ERC721.balanceOf(to); + _ownedTokens[to][length] = tokenId; + _ownedTokensIndex[tokenId] = length; + } + + /** + * @dev Private function to add a token to this extension's token tracking data structures. + * @param tokenId uint256 ID of the token to be added to the tokens list + */ + function _addTokenToAllTokensEnumeration(uint256 tokenId) private { + _allTokensIndex[tokenId] = _allTokens.length; + _allTokens.push(tokenId); + } + + /** + * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that + * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for + * gas optimizations e.g. when performing a transfer operation (avoiding double writes). + * This has O(1) time complexity, but alters the order of the _ownedTokens array. + * @param from address representing the previous owner of the given token ID + * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address + */ + function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { + // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = ERC721.balanceOf(from) - 1; + uint256 tokenIndex = _ownedTokensIndex[tokenId]; + + // When the token to delete is the last token, the swap operation is unnecessary + if (tokenIndex != lastTokenIndex) { + uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; + + _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + } + + // This also deletes the contents at the last position of the array + delete _ownedTokensIndex[tokenId]; + delete _ownedTokens[from][lastTokenIndex]; + } + + /** + * @dev Private function to remove a token from this extension's token tracking data structures. + * This has O(1) time complexity, but alters the order of the _allTokens array. + * @param tokenId uint256 ID of the token to be removed from the tokens list + */ + function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { + // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = _allTokens.length - 1; + uint256 tokenIndex = _allTokensIndex[tokenId]; + + // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so + // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding + // an 'if' statement (like in _removeTokenFromOwnerEnumeration) + uint256 lastTokenId = _allTokens[lastTokenIndex]; + + _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + + // This also deletes the contents at the last position of the array + delete _allTokensIndex[tokenId]; + _allTokens.pop(); + } +} diff --git a/certora/munged/token/ERC721/extensions/ERC721Pausable.sol b/certora/munged/token/ERC721/extensions/ERC721Pausable.sol new file mode 100644 index 000000000..5994cf36b --- /dev/null +++ b/certora/munged/token/ERC721/extensions/ERC721Pausable.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/ERC721Pausable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../../security/Pausable.sol"; + +/** + * @dev ERC721 token with pausable token transfers, minting and burning. + * + * Useful for scenarios such as preventing trades until the end of an evaluation + * period, or having an emergency switch for freezing all token transfers in the + * event of a large bug. + */ +abstract contract ERC721Pausable is ERC721, Pausable { + /** + * @dev See {ERC721-_beforeTokenTransfer}. + * + * Requirements: + * + * - the contract must not be paused. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual override { + super._beforeTokenTransfer(from, to, tokenId); + + require(!paused(), "ERC721Pausable: token transfer while paused"); + } +} diff --git a/certora/munged/token/ERC721/extensions/ERC721URIStorage.sol b/certora/munged/token/ERC721/extensions/ERC721URIStorage.sol new file mode 100644 index 000000000..ac9070144 --- /dev/null +++ b/certora/munged/token/ERC721/extensions/ERC721URIStorage.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/ERC721URIStorage.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; + +/** + * @dev ERC721 token with storage based token URI management. + */ +abstract contract ERC721URIStorage is ERC721 { + using Strings for uint256; + + // Optional mapping for token URIs + mapping(uint256 => string) private _tokenURIs; + + /** + * @dev See {IERC721Metadata-tokenURI}. + */ + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + require(_exists(tokenId), "ERC721URIStorage: URI query for nonexistent token"); + + string memory _tokenURI = _tokenURIs[tokenId]; + string memory base = _baseURI(); + + // If there is no base URI, return the token URI. + if (bytes(base).length == 0) { + return _tokenURI; + } + // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). + if (bytes(_tokenURI).length > 0) { + return string(abi.encodePacked(base, _tokenURI)); + } + + return super.tokenURI(tokenId); + } + + /** + * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { + require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); + _tokenURIs[tokenId] = _tokenURI; + } + + /** + * @dev Destroys `tokenId`. + * The approval is cleared when the token is burned. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Transfer} event. + */ + function _burn(uint256 tokenId) internal virtual override { + super._burn(tokenId); + + if (bytes(_tokenURIs[tokenId]).length != 0) { + delete _tokenURIs[tokenId]; + } + } +} diff --git a/certora/munged/token/ERC721/extensions/IERC721Enumerable.sol b/certora/munged/token/ERC721/extensions/IERC721Enumerable.sol new file mode 100644 index 000000000..904639956 --- /dev/null +++ b/certora/munged/token/ERC721/extensions/IERC721Enumerable.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/IERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../IERC721.sol"; + +/** + * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Enumerable is IERC721 { + /** + * @dev Returns the total amount of tokens stored by the contract. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns a token ID owned by `owner` at a given `index` of its token list. + * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId); + + /** + * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. + * Use along with {totalSupply} to enumerate all tokens. + */ + function tokenByIndex(uint256 index) external view returns (uint256); +} diff --git a/certora/munged/token/ERC721/extensions/IERC721Metadata.sol b/certora/munged/token/ERC721/extensions/IERC721Metadata.sol new file mode 100644 index 000000000..a50ea0db5 --- /dev/null +++ b/certora/munged/token/ERC721/extensions/IERC721Metadata.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/IERC721Metadata.sol) + +pragma solidity ^0.8.0; + +import "../IERC721.sol"; + +/** + * @title ERC-721 Non-Fungible Token Standard, optional metadata extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Metadata is IERC721 { + /** + * @dev Returns the token collection name. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the token collection symbol. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. + */ + function tokenURI(uint256 tokenId) external view returns (string memory); +} diff --git a/certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol b/certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol new file mode 100644 index 000000000..82ea1dc7d --- /dev/null +++ b/certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../extensions/ERC721Enumerable.sol"; +import "../extensions/ERC721Burnable.sol"; +import "../extensions/ERC721Pausable.sol"; +import "../../../access/AccessControlEnumerable.sol"; +import "../../../utils/Context.sol"; +import "../../../utils/Counters.sol"; + +/** + * @dev {ERC721} token, including: + * + * - ability for holders to burn (destroy) their tokens + * - a minter role that allows for token minting (creation) + * - a pauser role that allows to stop all token transfers + * - token ID and URI autogeneration + * + * This contract uses {AccessControl} to lock permissioned functions using the + * different roles - head to its documentation for details. + * + * The account that deploys the contract will be granted the minter and pauser + * roles, as well as the default admin role, which will let it grant both minter + * and pauser roles to other accounts. + */ +contract ERC721PresetMinterPauserAutoId is + Context, + AccessControlEnumerable, + ERC721Enumerable, + ERC721Burnable, + ERC721Pausable +{ + using Counters for Counters.Counter; + + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + Counters.Counter private _tokenIdTracker; + + string private _baseTokenURI; + + /** + * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the + * account that deploys the contract. + * + * Token URIs will be autogenerated based on `baseURI` and their token IDs. + * See {ERC721-tokenURI}. + */ + constructor( + string memory name, + string memory symbol, + string memory baseTokenURI + ) ERC721(name, symbol) { + _baseTokenURI = baseTokenURI; + + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + + _setupRole(MINTER_ROLE, _msgSender()); + _setupRole(PAUSER_ROLE, _msgSender()); + } + + function _baseURI() internal view virtual override returns (string memory) { + return _baseTokenURI; + } + + /** + * @dev Creates a new token for `to`. Its token ID will be automatically + * assigned (and available on the emitted {IERC721-Transfer} event), and the token + * URI autogenerated based on the base URI passed at construction. + * + * See {ERC721-_mint}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function mint(address to) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have minter role to mint"); + + // We cannot just use balanceOf to create the new tokenId because tokens + // can be burned (destroyed), so we need a separate counter. + _mint(to, _tokenIdTracker.current()); + _tokenIdTracker.increment(); + } + + /** + * @dev Pauses all token transfers. + * + * See {ERC721Pausable} and {Pausable-_pause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function pause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to pause"); + _pause(); + } + + /** + * @dev Unpauses all token transfers. + * + * See {ERC721Pausable} and {Pausable-_unpause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function unpause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to unpause"); + _unpause(); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual override(ERC721, ERC721Enumerable, ERC721Pausable) { + super._beforeTokenTransfer(from, to, tokenId); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(AccessControlEnumerable, ERC721, ERC721Enumerable) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/munged/token/ERC721/utils/ERC721Holder.sol b/certora/munged/token/ERC721/utils/ERC721Holder.sol new file mode 100644 index 000000000..e2aa303d8 --- /dev/null +++ b/certora/munged/token/ERC721/utils/ERC721Holder.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC721/utils/ERC721Holder.sol) + +pragma solidity ^0.8.0; + +import "../IERC721Receiver.sol"; + +/** + * @dev Implementation of the {IERC721Receiver} interface. + * + * Accepts all token transfers. + * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. + */ +contract ERC721Holder is IERC721Receiver { + /** + * @dev See {IERC721Receiver-onERC721Received}. + * + * Always returns `IERC721Receiver.onERC721Received.selector`. + */ + function onERC721Received( + address, + address, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC721Received.selector; + } +} diff --git a/certora/munged/token/ERC777/ERC777.sol b/certora/munged/token/ERC777/ERC777.sol new file mode 100644 index 000000000..643e5a3f7 --- /dev/null +++ b/certora/munged/token/ERC777/ERC777.sol @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC777/ERC777.sol) + +pragma solidity ^0.8.0; + +import "./IERC777.sol"; +import "./IERC777Recipient.sol"; +import "./IERC777Sender.sol"; +import "../ERC20/IERC20.sol"; +import "../../utils/Address.sol"; +import "../../utils/Context.sol"; +import "../../utils/introspection/IERC1820Registry.sol"; + +/** + * @dev Implementation of the {IERC777} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * + * Support for ERC20 is included in this contract, as specified by the EIP: both + * the ERC777 and ERC20 interfaces can be safely used when interacting with it. + * Both {IERC777-Sent} and {IERC20-Transfer} events are emitted on token + * movements. + * + * Additionally, the {IERC777-granularity} value is hard-coded to `1`, meaning that there + * are no special restrictions in the amount of tokens that created, moved, or + * destroyed. This makes integration with ERC20 applications seamless. + */ +contract ERC777 is Context, IERC777, IERC20 { + using Address for address; + + IERC1820Registry internal constant _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + + mapping(address => uint256) private _balances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); + bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + + // This isn't ever read from - it's only used to respond to the defaultOperators query. + address[] private _defaultOperatorsArray; + + // Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators). + mapping(address => bool) private _defaultOperators; + + // For each account, a mapping of its operators and revoked default operators. + mapping(address => mapping(address => bool)) private _operators; + mapping(address => mapping(address => bool)) private _revokedDefaultOperators; + + // ERC20-allowances + mapping(address => mapping(address => uint256)) private _allowances; + + /** + * @dev `defaultOperators` may be an empty array. + */ + constructor( + string memory name_, + string memory symbol_, + address[] memory defaultOperators_ + ) { + _name = name_; + _symbol = symbol_; + + _defaultOperatorsArray = defaultOperators_; + for (uint256 i = 0; i < defaultOperators_.length; i++) { + _defaultOperators[defaultOperators_[i]] = true; + } + + // register interfaces + _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this)); + _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this)); + } + + /** + * @dev See {IERC777-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IERC777-symbol}. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev See {ERC20-decimals}. + * + * Always returns 18, as per the + * [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility). + */ + function decimals() public pure virtual returns (uint8) { + return 18; + } + + /** + * @dev See {IERC777-granularity}. + * + * This implementation always returns `1`. + */ + function granularity() public view virtual override returns (uint256) { + return 1; + } + + /** + * @dev See {IERC777-totalSupply}. + */ + function totalSupply() public view virtual override(IERC20, IERC777) returns (uint256) { + return _totalSupply; + } + + /** + * @dev Returns the amount of tokens owned by an account (`tokenHolder`). + */ + function balanceOf(address tokenHolder) public view virtual override(IERC20, IERC777) returns (uint256) { + return _balances[tokenHolder]; + } + + /** + * @dev See {IERC777-send}. + * + * Also emits a {IERC20-Transfer} event for ERC20 compatibility. + */ + function send( + address recipient, + uint256 amount, + bytes memory data + ) public virtual override { + _send(_msgSender(), recipient, amount, data, "", true); + } + + /** + * @dev See {IERC20-transfer}. + * + * Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient} + * interface if it is a contract. + * + * Also emits a {Sent} event. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + require(recipient != address(0), "ERC777: transfer to the zero address"); + + address from = _msgSender(); + + _callTokensToSend(from, from, recipient, amount, "", ""); + + _move(from, from, recipient, amount, "", ""); + + _callTokensReceived(from, from, recipient, amount, "", "", false); + + return true; + } + + /** + * @dev See {IERC777-burn}. + * + * Also emits a {IERC20-Transfer} event for ERC20 compatibility. + */ + function burn(uint256 amount, bytes memory data) public virtual override { + _burn(_msgSender(), amount, data, ""); + } + + /** + * @dev See {IERC777-isOperatorFor}. + */ + function isOperatorFor(address operator, address tokenHolder) public view virtual override returns (bool) { + return + operator == tokenHolder || + (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) || + _operators[tokenHolder][operator]; + } + + /** + * @dev See {IERC777-authorizeOperator}. + */ + function authorizeOperator(address operator) public virtual override { + require(_msgSender() != operator, "ERC777: authorizing self as operator"); + + if (_defaultOperators[operator]) { + delete _revokedDefaultOperators[_msgSender()][operator]; + } else { + _operators[_msgSender()][operator] = true; + } + + emit AuthorizedOperator(operator, _msgSender()); + } + + /** + * @dev See {IERC777-revokeOperator}. + */ + function revokeOperator(address operator) public virtual override { + require(operator != _msgSender(), "ERC777: revoking self as operator"); + + if (_defaultOperators[operator]) { + _revokedDefaultOperators[_msgSender()][operator] = true; + } else { + delete _operators[_msgSender()][operator]; + } + + emit RevokedOperator(operator, _msgSender()); + } + + /** + * @dev See {IERC777-defaultOperators}. + */ + function defaultOperators() public view virtual override returns (address[] memory) { + return _defaultOperatorsArray; + } + + /** + * @dev See {IERC777-operatorSend}. + * + * Emits {Sent} and {IERC20-Transfer} events. + */ + function operatorSend( + address sender, + address recipient, + uint256 amount, + bytes memory data, + bytes memory operatorData + ) public virtual override { + require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder"); + _send(sender, recipient, amount, data, operatorData, true); + } + + /** + * @dev See {IERC777-operatorBurn}. + * + * Emits {Burned} and {IERC20-Transfer} events. + */ + function operatorBurn( + address account, + uint256 amount, + bytes memory data, + bytes memory operatorData + ) public virtual override { + require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder"); + _burn(account, amount, data, operatorData); + } + + /** + * @dev See {IERC20-allowance}. + * + * Note that operator and allowance concepts are orthogonal: operators may + * not have allowance, and accounts with allowance may not be operators + * themselves. + */ + function allowance(address holder, address spender) public view virtual override returns (uint256) { + return _allowances[holder][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * Note that accounts cannot have allowance issued by their operators. + */ + function approve(address spender, uint256 value) public virtual override returns (bool) { + address holder = _msgSender(); + _approve(holder, spender, value); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Note that operator and allowance concepts are orthogonal: operators cannot + * call `transferFrom` (unless they have allowance), and accounts with + * allowance cannot call `operatorSend` (unless they are operators). + * + * Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events. + */ + function transferFrom( + address holder, + address recipient, + uint256 amount + ) public virtual override returns (bool) { + require(recipient != address(0), "ERC777: transfer to the zero address"); + require(holder != address(0), "ERC777: transfer from the zero address"); + + address spender = _msgSender(); + + _callTokensToSend(spender, holder, recipient, amount, "", ""); + + _move(spender, holder, recipient, amount, "", ""); + + uint256 currentAllowance = _allowances[holder][spender]; + require(currentAllowance >= amount, "ERC777: transfer amount exceeds allowance"); + _approve(holder, spender, currentAllowance - amount); + + _callTokensReceived(spender, holder, recipient, amount, "", "", false); + + return true; + } + + /** + * @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * If a send hook is registered for `account`, the corresponding function + * will be called with `operator`, `data` and `operatorData`. + * + * See {IERC777Sender} and {IERC777Recipient}. + * + * Emits {Minted} and {IERC20-Transfer} events. + * + * Requirements + * + * - `account` cannot be the zero address. + * - if `account` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function _mint( + address account, + uint256 amount, + bytes memory userData, + bytes memory operatorData + ) internal virtual { + _mint(account, amount, userData, operatorData, true); + } + + /** + * @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * If `requireReceptionAck` is set to true, and if a send hook is + * registered for `account`, the corresponding function will be called with + * `operator`, `data` and `operatorData`. + * + * See {IERC777Sender} and {IERC777Recipient}. + * + * Emits {Minted} and {IERC20-Transfer} events. + * + * Requirements + * + * - `account` cannot be the zero address. + * - if `account` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function _mint( + address account, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) internal virtual { + require(account != address(0), "ERC777: mint to the zero address"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, address(0), account, amount); + + // Update state variables + _totalSupply += amount; + _balances[account] += amount; + + _callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck); + + emit Minted(operator, account, amount, userData, operatorData); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Send tokens + * @param from address token holder address + * @param to address recipient address + * @param amount uint256 amount of tokens to transfer + * @param userData bytes extra information provided by the token holder (if any) + * @param operatorData bytes extra information provided by the operator (if any) + * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient + */ + function _send( + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) internal virtual { + require(from != address(0), "ERC777: send from the zero address"); + require(to != address(0), "ERC777: send to the zero address"); + + address operator = _msgSender(); + + _callTokensToSend(operator, from, to, amount, userData, operatorData); + + _move(operator, from, to, amount, userData, operatorData); + + _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck); + } + + /** + * @dev Burn tokens + * @param from address token holder address + * @param amount uint256 amount of tokens to burn + * @param data bytes extra information provided by the token holder + * @param operatorData bytes extra information provided by the operator (if any) + */ + function _burn( + address from, + uint256 amount, + bytes memory data, + bytes memory operatorData + ) internal virtual { + require(from != address(0), "ERC777: burn from the zero address"); + + address operator = _msgSender(); + + _callTokensToSend(operator, from, address(0), amount, data, operatorData); + + _beforeTokenTransfer(operator, from, address(0), amount); + + // Update state variables + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC777: burn amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + } + _totalSupply -= amount; + + emit Burned(operator, from, amount, data, operatorData); + emit Transfer(from, address(0), amount); + } + + function _move( + address operator, + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData + ) private { + _beforeTokenTransfer(operator, from, to, amount); + + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC777: transfer amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + } + _balances[to] += amount; + + emit Sent(operator, from, to, amount, userData, operatorData); + emit Transfer(from, to, amount); + } + + /** + * @dev See {ERC20-_approve}. + * + * Note that accounts cannot have allowance issued by their operators. + */ + function _approve( + address holder, + address spender, + uint256 value + ) internal { + require(holder != address(0), "ERC777: approve from the zero address"); + require(spender != address(0), "ERC777: approve to the zero address"); + + _allowances[holder][spender] = value; + emit Approval(holder, spender, value); + } + + /** + * @dev Call from.tokensToSend() if the interface is registered + * @param operator address operator requesting the transfer + * @param from address token holder address + * @param to address recipient address + * @param amount uint256 amount of tokens to transfer + * @param userData bytes extra information provided by the token holder (if any) + * @param operatorData bytes extra information provided by the operator (if any) + */ + function _callTokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData + ) private { + address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH); + if (implementer != address(0)) { + IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData); + } + } + + /** + * @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but + * tokensReceived() was not registered for the recipient + * @param operator address operator requesting the transfer + * @param from address token holder address + * @param to address recipient address + * @param amount uint256 amount of tokens to transfer + * @param userData bytes extra information provided by the token holder (if any) + * @param operatorData bytes extra information provided by the operator (if any) + * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient + */ + function _callTokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) private { + address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH); + if (implementer != address(0)) { + IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData); + } else if (requireReceptionAck) { + require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient"); + } + } + + /** + * @dev Hook that is called before any token transfer. This includes + * calls to {send}, {transfer}, {operatorSend}, minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be to transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256 amount + ) internal virtual {} +} diff --git a/certora/munged/token/ERC777/IERC777.sol b/certora/munged/token/ERC777/IERC777.sol new file mode 100644 index 000000000..675d6133e --- /dev/null +++ b/certora/munged/token/ERC777/IERC777.sol @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC777/IERC777.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC777Token standard as defined in the EIP. + * + * This contract uses the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let + * token holders and recipients react to token movements by using setting implementers + * for the associated interfaces in said registry. See {IERC1820Registry} and + * {ERC1820Implementer}. + */ +interface IERC777 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the smallest part of the token that is not divisible. This + * means all token operations (creation, movement and destruction) must have + * amounts that are a multiple of this number. + * + * For most token contracts, this value will equal 1. + */ + function granularity() external view returns (uint256); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by an account (`owner`). + */ + function balanceOf(address owner) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * If send or receive hooks are registered for the caller and `recipient`, + * the corresponding functions will be called with `data` and empty + * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. + * + * Emits a {Sent} event. + * + * Requirements + * + * - the caller must have at least `amount` tokens. + * - `recipient` cannot be the zero address. + * - if `recipient` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function send( + address recipient, + uint256 amount, + bytes calldata data + ) external; + + /** + * @dev Destroys `amount` tokens from the caller's account, reducing the + * total supply. + * + * If a send hook is registered for the caller, the corresponding function + * will be called with `data` and empty `operatorData`. See {IERC777Sender}. + * + * Emits a {Burned} event. + * + * Requirements + * + * - the caller must have at least `amount` tokens. + */ + function burn(uint256 amount, bytes calldata data) external; + + /** + * @dev Returns true if an account is an operator of `tokenHolder`. + * Operators can send and burn tokens on behalf of their owners. All + * accounts are their own operator. + * + * See {operatorSend} and {operatorBurn}. + */ + function isOperatorFor(address operator, address tokenHolder) external view returns (bool); + + /** + * @dev Make an account an operator of the caller. + * + * See {isOperatorFor}. + * + * Emits an {AuthorizedOperator} event. + * + * Requirements + * + * - `operator` cannot be calling address. + */ + function authorizeOperator(address operator) external; + + /** + * @dev Revoke an account's operator status for the caller. + * + * See {isOperatorFor} and {defaultOperators}. + * + * Emits a {RevokedOperator} event. + * + * Requirements + * + * - `operator` cannot be calling address. + */ + function revokeOperator(address operator) external; + + /** + * @dev Returns the list of default operators. These accounts are operators + * for all token holders, even if {authorizeOperator} was never called on + * them. + * + * This list is immutable, but individual holders may revoke these via + * {revokeOperator}, in which case {isOperatorFor} will return false. + */ + function defaultOperators() external view returns (address[] memory); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must + * be an operator of `sender`. + * + * If send or receive hooks are registered for `sender` and `recipient`, + * the corresponding functions will be called with `data` and + * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. + * + * Emits a {Sent} event. + * + * Requirements + * + * - `sender` cannot be the zero address. + * - `sender` must have at least `amount` tokens. + * - the caller must be an operator for `sender`. + * - `recipient` cannot be the zero address. + * - if `recipient` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function operatorSend( + address sender, + address recipient, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external; + + /** + * @dev Destroys `amount` tokens from `account`, reducing the total supply. + * The caller must be an operator of `account`. + * + * If a send hook is registered for `account`, the corresponding function + * will be called with `data` and `operatorData`. See {IERC777Sender}. + * + * Emits a {Burned} event. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + * - the caller must be an operator for `account`. + */ + function operatorBurn( + address account, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external; + + event Sent( + address indexed operator, + address indexed from, + address indexed to, + uint256 amount, + bytes data, + bytes operatorData + ); + + event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); + + event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); + + event AuthorizedOperator(address indexed operator, address indexed tokenHolder); + + event RevokedOperator(address indexed operator, address indexed tokenHolder); +} diff --git a/certora/munged/token/ERC777/IERC777Recipient.sol b/certora/munged/token/ERC777/IERC777Recipient.sol new file mode 100644 index 000000000..3a845f662 --- /dev/null +++ b/certora/munged/token/ERC777/IERC777Recipient.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC777/IERC777Recipient.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. + * + * Accounts can be notified of {IERC777} tokens being sent to them by having a + * contract implement this interface (contract holders can be their own + * implementer) and registering it on the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. + * + * See {IERC1820Registry} and {ERC1820Implementer}. + */ +interface IERC777Recipient { + /** + * @dev Called by an {IERC777} token contract whenever tokens are being + * moved or created into a registered account (`to`). The type of operation + * is conveyed by `from` being the zero address or not. + * + * This call occurs _after_ the token contract's state is updated, so + * {IERC777-balanceOf}, etc., can be used to query the post-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} diff --git a/certora/munged/token/ERC777/IERC777Sender.sol b/certora/munged/token/ERC777/IERC777Sender.sol new file mode 100644 index 000000000..6f2e36080 --- /dev/null +++ b/certora/munged/token/ERC777/IERC777Sender.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC777/IERC777Sender.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC777TokensSender standard as defined in the EIP. + * + * {IERC777} Token holders can be notified of operations performed on their + * tokens by having a contract implement this interface (contract holders can be + * their own implementer) and registering it on the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. + * + * See {IERC1820Registry} and {ERC1820Implementer}. + */ +interface IERC777Sender { + /** + * @dev Called by an {IERC777} token contract whenever a registered holder's + * (`from`) tokens are about to be moved or destroyed. The type of operation + * is conveyed by `to` being the zero address or not. + * + * This call occurs _before_ the token contract's state is updated, so + * {IERC777-balanceOf}, etc., can be used to query the pre-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} diff --git a/certora/munged/token/ERC777/README.adoc b/certora/munged/token/ERC777/README.adoc new file mode 100644 index 000000000..d8f25f060 --- /dev/null +++ b/certora/munged/token/ERC777/README.adoc @@ -0,0 +1,30 @@ += ERC 777 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc777 + +This set of interfaces and contracts are all related to the [ERC777 token standard](https://eips.ethereum.org/EIPS/eip-777). + +TIP: For an overview of ERC777 tokens and a walk through on how to create a token contract read our xref:ROOT:erc777.adoc[ERC777 guide]. + +The token behavior itself is implemented in the core contracts: {IERC777}, {ERC777}. + +Additionally there are interfaces used to develop contracts that react to token movements: {IERC777Sender}, {IERC777Recipient}. + +== Core + +{{IERC777}} + +{{ERC777}} + +== Hooks + +{{IERC777Sender}} + +{{IERC777Recipient}} + +== Presets + +These contracts are preconfigured combinations of features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC777PresetFixedSupply}} diff --git a/certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol b/certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol new file mode 100644 index 000000000..d0ce2366a --- /dev/null +++ b/certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (token/ERC777/presets/ERC777PresetFixedSupply.sol) +pragma solidity ^0.8.0; + +import "../ERC777.sol"; + +/** + * @dev {ERC777} token, including: + * + * - Preminted initial supply + * - No access control mechanism (for minting/pausing) and hence no governance + * + * _Available since v3.4._ + */ +contract ERC777PresetFixedSupply is ERC777 { + /** + * @dev Mints `initialSupply` amount of token and transfers them to `owner`. + * + * See {ERC777-constructor}. + */ + constructor( + string memory name, + string memory symbol, + address[] memory defaultOperators, + uint256 initialSupply, + address owner + ) ERC777(name, symbol, defaultOperators) { + _mint(owner, initialSupply, "", ""); + } +} diff --git a/certora/munged/utils/Address.sol b/certora/munged/utils/Address.sol new file mode 100644 index 000000000..1bb7af5c7 --- /dev/null +++ b/certora/munged/utils/Address.sol @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/Address.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + assembly { + size := extcodesize(account) + } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + (bool success, ) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + (bool success, bytes memory returndata) = target.call{value: value}(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + (bool success, bytes memory returndata) = target.delegatecall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the + * revert reason using the provided one. + * + * _Available since v4.3._ + */ + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} diff --git a/certora/munged/utils/Arrays.sol b/certora/munged/utils/Arrays.sol new file mode 100644 index 000000000..70ba37e70 --- /dev/null +++ b/certora/munged/utils/Arrays.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/Arrays.sol) + +pragma solidity ^0.8.0; + +import "./math/Math.sol"; + +/** + * @dev Collection of functions related to array types. + */ +library Arrays { + /** + * @dev Searches a sorted `array` and returns the first index that contains + * a value greater or equal to `element`. If no such index exists (i.e. all + * values in the array are strictly less than `element`), the array length is + * returned. Time complexity O(log n). + * + * `array` is expected to be sorted in ascending order, and to contain no + * repeated elements. + */ + function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) { + if (array.length == 0) { + return 0; + } + + uint256 low = 0; + uint256 high = array.length; + + while (low < high) { + uint256 mid = Math.average(low, high); + + // Note that mid will always be strictly less than high (i.e. it will be a valid array index) + // because Math.average rounds down (it does integer division with truncation). + if (array[mid] > element) { + high = mid; + } else { + low = mid + 1; + } + } + + // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound. + if (low > 0 && array[low - 1] == element) { + return low - 1; + } else { + return low; + } + } +} diff --git a/certora/munged/utils/Context.sol b/certora/munged/utils/Context.sol new file mode 100644 index 000000000..1a4936b24 --- /dev/null +++ b/certora/munged/utils/Context.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/Context.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } +} diff --git a/certora/munged/utils/Counters.sol b/certora/munged/utils/Counters.sol new file mode 100644 index 000000000..148c9fda1 --- /dev/null +++ b/certora/munged/utils/Counters.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/Counters.sol) + +pragma solidity ^0.8.0; + +/** + * @title Counters + * @author Matt Condon (@shrugs) + * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number + * of elements in a mapping, issuing ERC721 ids, or counting request ids. + * + * Include with `using Counters for Counters.Counter;` + */ +library Counters { + struct Counter { + // This variable should never be directly accessed by users of the library: interactions must be restricted to + // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add + // this feature: see https://github.com/ethereum/solidity/issues/4637 + uint256 _value; // default: 0 + } + + function current(Counter storage counter) internal view returns (uint256) { + return counter._value; + } + + function increment(Counter storage counter) internal { + unchecked { + counter._value += 1; + } + } + + function decrement(Counter storage counter) internal { + uint256 value = counter._value; + require(value > 0, "Counter: decrement overflow"); + unchecked { + counter._value = value - 1; + } + } + + function reset(Counter storage counter) internal { + counter._value = 0; + } +} diff --git a/certora/munged/utils/Create2.sol b/certora/munged/utils/Create2.sol new file mode 100644 index 000000000..0faa469f4 --- /dev/null +++ b/certora/munged/utils/Create2.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/Create2.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. + * `CREATE2` can be used to compute in advance the address where a smart + * contract will be deployed, which allows for interesting new mechanisms known + * as 'counterfactual interactions'. + * + * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more + * information. + */ +library Create2 { + /** + * @dev Deploys a contract using `CREATE2`. The address where the contract + * will be deployed can be known in advance via {computeAddress}. + * + * The bytecode for a contract can be obtained from Solidity with + * `type(contractName).creationCode`. + * + * Requirements: + * + * - `bytecode` must not be empty. + * - `salt` must have not been used for `bytecode` already. + * - the factory must have a balance of at least `amount`. + * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. + */ + function deploy( + uint256 amount, + bytes32 salt, + bytes memory bytecode + ) internal returns (address) { + address addr; + require(address(this).balance >= amount, "Create2: insufficient balance"); + require(bytecode.length != 0, "Create2: bytecode length is zero"); + assembly { + addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) + } + require(addr != address(0), "Create2: Failed on deploy"); + return addr; + } + + /** + * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the + * `bytecodeHash` or `salt` will result in a new destination address. + */ + function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { + return computeAddress(salt, bytecodeHash, address(this)); + } + + /** + * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at + * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. + */ + function computeAddress( + bytes32 salt, + bytes32 bytecodeHash, + address deployer + ) internal pure returns (address) { + bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)); + return address(uint160(uint256(_data))); + } +} diff --git a/certora/munged/utils/Multicall.sol b/certora/munged/utils/Multicall.sol new file mode 100644 index 000000000..81a0291da --- /dev/null +++ b/certora/munged/utils/Multicall.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/Multicall.sol) + +pragma solidity ^0.8.0; + +import "./Address.sol"; + +/** + * @dev Provides a function to batch together multiple calls in a single external call. + * + * _Available since v4.1._ + */ +abstract contract Multicall { + /** + * @dev Receives and executes a batch of function calls on this contract. + */ + function multicall(bytes[] calldata data) external returns (bytes[] memory results) { + results = new bytes[](data.length); + for (uint256 i = 0; i < data.length; i++) { + results[i] = Address.functionDelegateCall(address(this), data[i]); + } + return results; + } +} diff --git a/certora/munged/utils/README.adoc b/certora/munged/utils/README.adoc new file mode 100644 index 000000000..4edcf923b --- /dev/null +++ b/certora/munged/utils/README.adoc @@ -0,0 +1,103 @@ += Utilities + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/utils + +Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives. + +The {Address}, {Arrays} and {Strings} libraries provide more operations related to these native data types, while {SafeCast} adds ways to safely convert between the different signed and unsigned numeric types. +{Multicall} provides a function to batch together multiple calls in a single external call. + +For new data types: + + * {Counters}: a simple way to get a counter that can only be incremented, decremented or reset. Very useful for ID generation, counting contract activity, among others. + * {EnumerableMap}: like Solidity's https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] type, but with key-value _enumeration_: this will let you know how many entries a mapping has, and iterate over them (which is not possible with `mapping`). + * {EnumerableSet}: like {EnumerableMap}, but for https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets]. Can be used to store privileged accounts, issued IDs, etc. + +[NOTE] +==== +Because Solidity does not support generic types, {EnumerableMap} and {EnumerableSet} are specialized to a limited number of key-value types. + +As of v3.0, {EnumerableMap} supports `uint256 -> address` (`UintToAddressMap`), and {EnumerableSet} supports `address` and `uint256` (`AddressSet` and `UintSet`). +==== + +Finally, {Create2} contains all necessary utilities to safely use the https://blog.openzeppelin.com/getting-the-most-out-of-create2/[`CREATE2` EVM opcode], without having to deal with low-level assembly. + +== Math + +{{Math}} + +{{SafeCast}} + +{{SafeMath}} + +{{SignedSafeMath}} + +== Cryptography + +{{ECDSA}} + +{{SignatureChecker}} + +{{MerkleProof}} + +{{EIP712}} + +== Escrow + +{{ConditionalEscrow}} + +{{Escrow}} + +{{RefundEscrow}} + +== Introspection + +This set of interfaces and contracts deal with https://en.wikipedia.org/wiki/Type_introspection[type introspection] of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract's _interface_. + +Ethereum contracts have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. `ERC20` tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract _declaring_ its interface can be very helpful in preventing errors. + +There are two main ways to approach this. + +* Locally, where a contract implements `IERC165` and declares an interface, and a second one queries it directly via `ERC165Checker`. +* Globally, where a global and unique registry (`IERC1820Registry`) is used to register implementers of a certain interface (`IERC1820Implementer`). It is then the registry that is queried, which allows for more complex setups, like contracts implementing interfaces for externally-owned accounts. + +Note that, in all cases, accounts simply _declare_ their interfaces, but they are not required to actually implement them. This mechanism can therefore be used to both prevent errors and allow for complex interactions (see `ERC777`), but it must not be relied on for security. + +{{IERC165}} + +{{ERC165}} + +{{ERC165Storage}} + +{{ERC165Checker}} + +{{IERC1820Registry}} + +{{IERC1820Implementer}} + +{{ERC1820Implementer}} + +== Data Structures + +{{BitMaps}} + +{{EnumerableMap}} + +{{EnumerableSet}} + +== Libraries + +{{Create2}} + +{{Address}} + +{{Arrays}} + +{{Counters}} + +{{Strings}} + +{{StorageSlot}} + +{{Multicall}} diff --git a/certora/munged/utils/StorageSlot.sol b/certora/munged/utils/StorageSlot.sol new file mode 100644 index 000000000..d8ceee4af --- /dev/null +++ b/certora/munged/utils/StorageSlot.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/StorageSlot.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Library for reading and writing primitive types to specific storage slots. + * + * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. + * This library helps with reading and writing to such slots without the need for inline assembly. + * + * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. + * + * Example usage to set ERC1967 implementation slot: + * ``` + * contract ERC1967 { + * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + * + * function _getImplementation() internal view returns (address) { + * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + * } + * + * function _setImplementation(address newImplementation) internal { + * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + * } + * } + * ``` + * + * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ + */ +library StorageSlot { + struct AddressSlot { + address value; + } + + struct BooleanSlot { + bool value; + } + + struct Bytes32Slot { + bytes32 value; + } + + struct Uint256Slot { + uint256 value; + } + + /** + * @dev Returns an `AddressSlot` with member `value` located at `slot`. + */ + function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `BooleanSlot` with member `value` located at `slot`. + */ + function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. + */ + function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Uint256Slot` with member `value` located at `slot`. + */ + function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { + assembly { + r.slot := slot + } + } +} diff --git a/certora/munged/utils/Strings.sol b/certora/munged/utils/Strings.sol new file mode 100644 index 000000000..b84cda609 --- /dev/null +++ b/certora/munged/utils/Strings.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/Strings.sol) + +pragma solidity ^0.8.0; + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + // Inspired by OraclizeAPI's implementation - MIT licence + // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol + + if (value == 0) { + return "0"; + } + uint256 temp = value; + uint256 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + bytes memory buffer = new bytes(digits); + while (value != 0) { + digits -= 1; + buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); + value /= 10; + } + return string(buffer); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + if (value == 0) { + return "0x00"; + } + uint256 temp = value; + uint256 length = 0; + while (temp != 0) { + length++; + temp >>= 8; + } + return toHexString(value, length); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = _HEX_SYMBOLS[value & 0xf]; + value >>= 4; + } + require(value == 0, "Strings: hex length insufficient"); + return string(buffer); + } +} diff --git a/certora/munged/utils/Timers.sol b/certora/munged/utils/Timers.sol new file mode 100644 index 000000000..5c2145c2f --- /dev/null +++ b/certora/munged/utils/Timers.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/Timers.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Tooling for timepoints, timers and delays + */ +library Timers { + struct Timestamp { + uint64 _deadline; + } + + function getDeadline(Timestamp memory timer) internal pure returns (uint64) { + return timer._deadline; + } + + function setDeadline(Timestamp storage timer, uint64 timestamp) internal { + timer._deadline = timestamp; + } + + function reset(Timestamp storage timer) internal { + timer._deadline = 0; + } + + function isUnset(Timestamp memory timer) internal pure returns (bool) { + return timer._deadline == 0; + } + + function isStarted(Timestamp memory timer) internal pure returns (bool) { + return timer._deadline > 0; + } + + function isPending(Timestamp memory timer) internal view returns (bool) { + return timer._deadline > block.timestamp; + } + + function isExpired(Timestamp memory timer) internal view returns (bool) { + return isStarted(timer) && timer._deadline <= block.timestamp; + } + + struct BlockNumber { + uint64 _deadline; + } + + function getDeadline(BlockNumber memory timer) internal pure returns (uint64) { + return timer._deadline; + } + + function setDeadline(BlockNumber storage timer, uint64 timestamp) internal { + timer._deadline = timestamp; + } + + function reset(BlockNumber storage timer) internal { + timer._deadline = 0; + } + + function isUnset(BlockNumber memory timer) internal pure returns (bool) { + return timer._deadline == 0; + } + + function isStarted(BlockNumber memory timer) internal pure returns (bool) { + return timer._deadline > 0; + } + + function isPending(BlockNumber memory timer) internal view returns (bool) { + return timer._deadline > block.number; + } + + function isExpired(BlockNumber memory timer) internal view returns (bool) { + return isStarted(timer) && timer._deadline <= block.number; + } +} diff --git a/certora/munged/utils/cryptography/ECDSA.sol b/certora/munged/utils/cryptography/ECDSA.sol new file mode 100644 index 000000000..7de57c04b --- /dev/null +++ b/certora/munged/utils/cryptography/ECDSA.sol @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/cryptography/ECDSA.sol) + +pragma solidity ^0.8.0; + +import "../Strings.sol"; + +/** + * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. + * + * These functions can be used to verify that a message was signed by the holder + * of the private keys of a given address. + */ +library ECDSA { + enum RecoverError { + NoError, + InvalidSignature, + InvalidSignatureLength, + InvalidSignatureS, + InvalidSignatureV + } + + function _throwError(RecoverError error) private pure { + if (error == RecoverError.NoError) { + return; // no error: do nothing + } else if (error == RecoverError.InvalidSignature) { + revert("ECDSA: invalid signature"); + } else if (error == RecoverError.InvalidSignatureLength) { + revert("ECDSA: invalid signature length"); + } else if (error == RecoverError.InvalidSignatureS) { + revert("ECDSA: invalid signature 's' value"); + } else if (error == RecoverError.InvalidSignatureV) { + revert("ECDSA: invalid signature 'v' value"); + } + } + + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature` or error string. This address can then be used for verification purposes. + * + * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {toEthSignedMessageHash} on it. + * + * Documentation for signature generation: + * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] + * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] + * + * _Available since v4.3._ + */ + function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { + // Check the signature length + // - case 65: r,s,v signature (standard) + // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ + if (signature.length == 65) { + bytes32 r; + bytes32 s; + uint8 v; + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + return tryRecover(hash, v, r, s); + } else if (signature.length == 64) { + bytes32 r; + bytes32 vs; + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + assembly { + r := mload(add(signature, 0x20)) + vs := mload(add(signature, 0x40)) + } + return tryRecover(hash, r, vs); + } else { + return (address(0), RecoverError.InvalidSignatureLength); + } + } + + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature`. This address can then be used for verification purposes. + * + * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {toEthSignedMessageHash} on it. + */ + function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, signature); + _throwError(error); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. + * + * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] + * + * _Available since v4.3._ + */ + function tryRecover( + bytes32 hash, + bytes32 r, + bytes32 vs + ) internal pure returns (address, RecoverError) { + bytes32 s; + uint8 v; + assembly { + s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + v := add(shr(255, vs), 27) + } + return tryRecover(hash, v, r, s); + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. + * + * _Available since v4.2._ + */ + function recover( + bytes32 hash, + bytes32 r, + bytes32 vs + ) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, r, vs); + _throwError(error); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `v`, + * `r` and `s` signature fields separately. + * + * _Available since v4.3._ + */ + function tryRecover( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address, RecoverError) { + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { + return (address(0), RecoverError.InvalidSignatureS); + } + if (v != 27 && v != 28) { + return (address(0), RecoverError.InvalidSignatureV); + } + + // If the signature is valid (and not malleable), return the signer address + address signer = ecrecover(hash, v, r, s); + if (signer == address(0)) { + return (address(0), RecoverError.InvalidSignature); + } + + return (signer, RecoverError.NoError); + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `v`, + * `r` and `s` signature fields separately. + */ + function recover( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, v, r, s); + _throwError(error); + return recovered; + } + + /** + * @dev Returns an Ethereum Signed Message, created from a `hash`. This + * produces hash corresponding to the one signed with the + * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] + * JSON-RPC method as part of EIP-191. + * + * See {recover}. + */ + function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { + // 32 is the length in bytes of hash, + // enforced by the type signature above + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); + } + + /** + * @dev Returns an Ethereum Signed Message, created from `s`. This + * produces hash corresponding to the one signed with the + * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] + * JSON-RPC method as part of EIP-191. + * + * See {recover}. + */ + function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); + } + + /** + * @dev Returns an Ethereum Signed Typed Data, created from a + * `domainSeparator` and a `structHash`. This produces hash corresponding + * to the one signed with the + * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] + * JSON-RPC method as part of EIP-712. + * + * See {recover}. + */ + function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + } +} diff --git a/certora/munged/utils/cryptography/MerkleProof.sol b/certora/munged/utils/cryptography/MerkleProof.sol new file mode 100644 index 000000000..74e248f81 --- /dev/null +++ b/certora/munged/utils/cryptography/MerkleProof.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/cryptography/MerkleProof.sol) + +pragma solidity ^0.8.0; + +/** + * @dev These functions deal with verification of Merkle Trees proofs. + * + * The proofs can be generated using the JavaScript library + * https://github.com/miguelmota/merkletreejs[merkletreejs]. + * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. + * + * See `test/utils/cryptography/MerkleProof.test.js` for some examples. + */ +library MerkleProof { + /** + * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree + * defined by `root`. For this, a `proof` must be provided, containing + * sibling hashes on the branch from the leaf to the root of the tree. Each + * pair of leaves and each pair of pre-images are assumed to be sorted. + */ + function verify( + bytes32[] memory proof, + bytes32 root, + bytes32 leaf + ) internal pure returns (bool) { + return processProof(proof, leaf) == root; + } + + /** + * @dev Returns the rebuilt hash obtained by traversing a Merklee tree up + * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt + * hash matches the root of the tree. When processing the proof, the pairs + * of leafs & pre-images are assumed to be sorted. + * + * _Available since v4.4._ + */ + function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { + bytes32 computedHash = leaf; + for (uint256 i = 0; i < proof.length; i++) { + bytes32 proofElement = proof[i]; + if (computedHash <= proofElement) { + // Hash(current computed hash + current element of the proof) + computedHash = keccak256(abi.encodePacked(computedHash, proofElement)); + } else { + // Hash(current element of the proof + current computed hash) + computedHash = keccak256(abi.encodePacked(proofElement, computedHash)); + } + } + return computedHash; + } +} diff --git a/certora/munged/utils/cryptography/SignatureChecker.sol b/certora/munged/utils/cryptography/SignatureChecker.sol new file mode 100644 index 000000000..f392feb8d --- /dev/null +++ b/certora/munged/utils/cryptography/SignatureChecker.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/cryptography/SignatureChecker.sol) + +pragma solidity ^0.8.0; + +import "./ECDSA.sol"; +import "../Address.sol"; +import "../../interfaces/IERC1271.sol"; + +/** + * @dev Signature verification helper: Provide a single mechanism to verify both private-key (EOA) ECDSA signature and + * ERC1271 contract signatures. Using this instead of ECDSA.recover in your contract will make them compatible with + * smart contract wallets such as Argent and Gnosis. + * + * Note: unlike ECDSA signatures, contract signature's are revocable, and the outcome of this function can thus change + * through time. It could return true at block N and false at block N+1 (or the opposite). + * + * _Available since v4.1._ + */ +library SignatureChecker { + function isValidSignatureNow( + address signer, + bytes32 hash, + bytes memory signature + ) internal view returns (bool) { + (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature); + if (error == ECDSA.RecoverError.NoError && recovered == signer) { + return true; + } + + (bool success, bytes memory result) = signer.staticcall( + abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature) + ); + return (success && result.length == 32 && abi.decode(result, (bytes4)) == IERC1271.isValidSignature.selector); + } +} diff --git a/certora/munged/utils/cryptography/draft-EIP712.sol b/certora/munged/utils/cryptography/draft-EIP712.sol new file mode 100644 index 000000000..918fd3297 --- /dev/null +++ b/certora/munged/utils/cryptography/draft-EIP712.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/cryptography/draft-EIP712.sol) + +pragma solidity ^0.8.0; + +import "./ECDSA.sol"; + +/** + * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. + * + * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, + * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding + * they need in their contracts using a combination of `abi.encode` and `keccak256`. + * + * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding + * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA + * ({_hashTypedDataV4}). + * + * The implementation of the domain separator was designed to be as efficient as possible while still properly updating + * the chain id to protect against replay attacks on an eventual fork of the chain. + * + * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method + * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. + * + * _Available since v3.4._ + */ +abstract contract EIP712 { + /* solhint-disable var-name-mixedcase */ + // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to + // invalidate the cached domain separator if the chain id changes. + bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; + uint256 private immutable _CACHED_CHAIN_ID; + address private immutable _CACHED_THIS; + + bytes32 private immutable _HASHED_NAME; + bytes32 private immutable _HASHED_VERSION; + bytes32 private immutable _TYPE_HASH; + + /* solhint-enable var-name-mixedcase */ + + /** + * @dev Initializes the domain separator and parameter caches. + * + * The meaning of `name` and `version` is specified in + * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: + * + * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. + * - `version`: the current major version of the signing domain. + * + * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart + * contract upgrade]. + */ + constructor(string memory name, string memory version) { + bytes32 hashedName = keccak256(bytes(name)); + bytes32 hashedVersion = keccak256(bytes(version)); + bytes32 typeHash = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + _HASHED_NAME = hashedName; + _HASHED_VERSION = hashedVersion; + _CACHED_CHAIN_ID = block.chainid; + _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); + _CACHED_THIS = address(this); + _TYPE_HASH = typeHash; + } + + /** + * @dev Returns the domain separator for the current chain. + */ + function _domainSeparatorV4() internal view returns (bytes32) { + if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { + return _CACHED_DOMAIN_SEPARATOR; + } else { + return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); + } + } + + function _buildDomainSeparator( + bytes32 typeHash, + bytes32 nameHash, + bytes32 versionHash + ) private view returns (bytes32) { + return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); + } + + /** + * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this + * function returns the hash of the fully encoded EIP712 message for this domain. + * + * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: + * + * ```solidity + * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( + * keccak256("Mail(address to,string contents)"), + * mailTo, + * keccak256(bytes(mailContents)) + * ))); + * address signer = ECDSA.recover(digest, signature); + * ``` + */ + function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { + return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); + } +} diff --git a/certora/munged/utils/escrow/ConditionalEscrow.sol b/certora/munged/utils/escrow/ConditionalEscrow.sol new file mode 100644 index 000000000..9f4c6aee3 --- /dev/null +++ b/certora/munged/utils/escrow/ConditionalEscrow.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/escrow/ConditionalEscrow.sol) + +pragma solidity ^0.8.0; + +import "./Escrow.sol"; + +/** + * @title ConditionalEscrow + * @dev Base abstract escrow to only allow withdrawal if a condition is met. + * @dev Intended usage: See {Escrow}. Same usage guidelines apply here. + */ +abstract contract ConditionalEscrow is Escrow { + /** + * @dev Returns whether an address is allowed to withdraw their funds. To be + * implemented by derived contracts. + * @param payee The destination address of the funds. + */ + function withdrawalAllowed(address payee) public view virtual returns (bool); + + function withdraw(address payable payee) public virtual override { + require(withdrawalAllowed(payee), "ConditionalEscrow: payee is not allowed to withdraw"); + super.withdraw(payee); + } +} diff --git a/certora/munged/utils/escrow/Escrow.sol b/certora/munged/utils/escrow/Escrow.sol new file mode 100644 index 000000000..9f23f84c2 --- /dev/null +++ b/certora/munged/utils/escrow/Escrow.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/escrow/Escrow.sol) + +pragma solidity ^0.8.0; + +import "../../access/Ownable.sol"; +import "../Address.sol"; + +/** + * @title Escrow + * @dev Base escrow contract, holds funds designated for a payee until they + * withdraw them. + * + * Intended usage: This contract (and derived escrow contracts) should be a + * standalone contract, that only interacts with the contract that instantiated + * it. That way, it is guaranteed that all Ether will be handled according to + * the `Escrow` rules, and there is no need to check for payable functions or + * transfers in the inheritance tree. The contract that uses the escrow as its + * payment method should be its owner, and provide public methods redirecting + * to the escrow's deposit and withdraw. + */ +contract Escrow is Ownable { + using Address for address payable; + + event Deposited(address indexed payee, uint256 weiAmount); + event Withdrawn(address indexed payee, uint256 weiAmount); + + mapping(address => uint256) private _deposits; + + function depositsOf(address payee) public view returns (uint256) { + return _deposits[payee]; + } + + /** + * @dev Stores the sent amount as credit to be withdrawn. + * @param payee The destination address of the funds. + */ + function deposit(address payee) public payable virtual onlyOwner { + uint256 amount = msg.value; + _deposits[payee] += amount; + emit Deposited(payee, amount); + } + + /** + * @dev Withdraw accumulated balance for a payee, forwarding all gas to the + * recipient. + * + * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. + * Make sure you trust the recipient, or are either following the + * checks-effects-interactions pattern or using {ReentrancyGuard}. + * + * @param payee The address whose funds will be withdrawn and transferred to. + */ + function withdraw(address payable payee) public virtual onlyOwner { + uint256 payment = _deposits[payee]; + + _deposits[payee] = 0; + + payee.sendValue(payment); + + emit Withdrawn(payee, payment); + } +} diff --git a/certora/munged/utils/escrow/RefundEscrow.sol b/certora/munged/utils/escrow/RefundEscrow.sol new file mode 100644 index 000000000..d1218068a --- /dev/null +++ b/certora/munged/utils/escrow/RefundEscrow.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/escrow/RefundEscrow.sol) + +pragma solidity ^0.8.0; + +import "./ConditionalEscrow.sol"; + +/** + * @title RefundEscrow + * @dev Escrow that holds funds for a beneficiary, deposited from multiple + * parties. + * @dev Intended usage: See {Escrow}. Same usage guidelines apply here. + * @dev The owner account (that is, the contract that instantiates this + * contract) may deposit, close the deposit period, and allow for either + * withdrawal by the beneficiary, or refunds to the depositors. All interactions + * with `RefundEscrow` will be made through the owner contract. + */ +contract RefundEscrow is ConditionalEscrow { + using Address for address payable; + + enum State { + Active, + Refunding, + Closed + } + + event RefundsClosed(); + event RefundsEnabled(); + + State private _state; + address payable private immutable _beneficiary; + + /** + * @dev Constructor. + * @param beneficiary_ The beneficiary of the deposits. + */ + constructor(address payable beneficiary_) { + require(beneficiary_ != address(0), "RefundEscrow: beneficiary is the zero address"); + _beneficiary = beneficiary_; + _state = State.Active; + } + + /** + * @return The current state of the escrow. + */ + function state() public view virtual returns (State) { + return _state; + } + + /** + * @return The beneficiary of the escrow. + */ + function beneficiary() public view virtual returns (address payable) { + return _beneficiary; + } + + /** + * @dev Stores funds that may later be refunded. + * @param refundee The address funds will be sent to if a refund occurs. + */ + function deposit(address refundee) public payable virtual override { + require(state() == State.Active, "RefundEscrow: can only deposit while active"); + super.deposit(refundee); + } + + /** + * @dev Allows for the beneficiary to withdraw their funds, rejecting + * further deposits. + */ + function close() public virtual onlyOwner { + require(state() == State.Active, "RefundEscrow: can only close while active"); + _state = State.Closed; + emit RefundsClosed(); + } + + /** + * @dev Allows for refunds to take place, rejecting further deposits. + */ + function enableRefunds() public virtual onlyOwner { + require(state() == State.Active, "RefundEscrow: can only enable refunds while active"); + _state = State.Refunding; + emit RefundsEnabled(); + } + + /** + * @dev Withdraws the beneficiary's funds. + */ + function beneficiaryWithdraw() public virtual { + require(state() == State.Closed, "RefundEscrow: beneficiary can only withdraw while closed"); + beneficiary().sendValue(address(this).balance); + } + + /** + * @dev Returns whether refundees can withdraw their deposits (be refunded). The overridden function receives a + * 'payee' argument, but we ignore it here since the condition is global, not per-payee. + */ + function withdrawalAllowed(address) public view override returns (bool) { + return state() == State.Refunding; + } +} diff --git a/certora/munged/utils/introspection/ERC165.sol b/certora/munged/utils/introspection/ERC165.sol new file mode 100644 index 000000000..8253d2ddc --- /dev/null +++ b/certora/munged/utils/introspection/ERC165.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/introspection/ERC165.sol) + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +/** + * @dev Implementation of the {IERC165} interface. + * + * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check + * for the additional interface id that will be supported. For example: + * + * ```solidity + * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); + * } + * ``` + * + * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. + */ +abstract contract ERC165 is IERC165 { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IERC165).interfaceId; + } +} diff --git a/certora/munged/utils/introspection/ERC165Checker.sol b/certora/munged/utils/introspection/ERC165Checker.sol new file mode 100644 index 000000000..5f5473332 --- /dev/null +++ b/certora/munged/utils/introspection/ERC165Checker.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/introspection/ERC165Checker.sol) + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +/** + * @dev Library used to query support of an interface declared via {IERC165}. + * + * Note that these functions return the actual result of the query: they do not + * `revert` if an interface is not supported. It is up to the caller to decide + * what to do in these cases. + */ +library ERC165Checker { + // As per the EIP-165 spec, no interface should ever match 0xffffffff + bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; + + /** + * @dev Returns true if `account` supports the {IERC165} interface, + */ + function supportsERC165(address account) internal view returns (bool) { + // Any contract that implements ERC165 must explicitly indicate support of + // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid + return + _supportsERC165Interface(account, type(IERC165).interfaceId) && + !_supportsERC165Interface(account, _INTERFACE_ID_INVALID); + } + + /** + * @dev Returns true if `account` supports the interface defined by + * `interfaceId`. Support for {IERC165} itself is queried automatically. + * + * See {IERC165-supportsInterface}. + */ + function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { + // query support of both ERC165 as per the spec and support of _interfaceId + return supportsERC165(account) && _supportsERC165Interface(account, interfaceId); + } + + /** + * @dev Returns a boolean array where each value corresponds to the + * interfaces passed in and whether they're supported or not. This allows + * you to batch check interfaces for a contract where your expectation + * is that some interfaces may not be supported. + * + * See {IERC165-supportsInterface}. + * + * _Available since v3.4._ + */ + function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) + internal + view + returns (bool[] memory) + { + // an array of booleans corresponding to interfaceIds and whether they're supported or not + bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); + + // query support of ERC165 itself + if (supportsERC165(account)) { + // query support of each interface in interfaceIds + for (uint256 i = 0; i < interfaceIds.length; i++) { + interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]); + } + } + + return interfaceIdsSupported; + } + + /** + * @dev Returns true if `account` supports all the interfaces defined in + * `interfaceIds`. Support for {IERC165} itself is queried automatically. + * + * Batch-querying can lead to gas savings by skipping repeated checks for + * {IERC165} support. + * + * See {IERC165-supportsInterface}. + */ + function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { + // query support of ERC165 itself + if (!supportsERC165(account)) { + return false; + } + + // query support of each interface in _interfaceIds + for (uint256 i = 0; i < interfaceIds.length; i++) { + if (!_supportsERC165Interface(account, interfaceIds[i])) { + return false; + } + } + + // all interfaces supported + return true; + } + + /** + * @notice Query if a contract implements an interface, does not check ERC165 support + * @param account The address of the contract to query for support of an interface + * @param interfaceId The interface identifier, as specified in ERC-165 + * @return true if the contract at account indicates support of the interface with + * identifier interfaceId, false otherwise + * @dev Assumes that account contains a contract that supports ERC165, otherwise + * the behavior of this method is undefined. This precondition can be checked + * with {supportsERC165}. + * Interface identification is specified in ERC-165. + */ + function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) { + bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId); + (bool success, bytes memory result) = account.staticcall{gas: 30000}(encodedParams); + if (result.length < 32) return false; + return success && abi.decode(result, (bool)); + } +} diff --git a/certora/munged/utils/introspection/ERC165Storage.sol b/certora/munged/utils/introspection/ERC165Storage.sol new file mode 100644 index 000000000..6151a11e6 --- /dev/null +++ b/certora/munged/utils/introspection/ERC165Storage.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/introspection/ERC165Storage.sol) + +pragma solidity ^0.8.0; + +import "./ERC165.sol"; + +/** + * @dev Storage based implementation of the {IERC165} interface. + * + * Contracts may inherit from this and call {_registerInterface} to declare + * their support of an interface. + */ +abstract contract ERC165Storage is ERC165 { + /** + * @dev Mapping of interface ids to whether or not it's supported. + */ + mapping(bytes4 => bool) private _supportedInterfaces; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return super.supportsInterface(interfaceId) || _supportedInterfaces[interfaceId]; + } + + /** + * @dev Registers the contract as an implementer of the interface defined by + * `interfaceId`. Support of the actual ERC165 interface is automatic and + * registering its interface id is not required. + * + * See {IERC165-supportsInterface}. + * + * Requirements: + * + * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). + */ + function _registerInterface(bytes4 interfaceId) internal virtual { + require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); + _supportedInterfaces[interfaceId] = true; + } +} diff --git a/certora/munged/utils/introspection/ERC1820Implementer.sol b/certora/munged/utils/introspection/ERC1820Implementer.sol new file mode 100644 index 000000000..bf081f981 --- /dev/null +++ b/certora/munged/utils/introspection/ERC1820Implementer.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/introspection/ERC1820Implementer.sol) + +pragma solidity ^0.8.0; + +import "./IERC1820Implementer.sol"; + +/** + * @dev Implementation of the {IERC1820Implementer} interface. + * + * Contracts may inherit from this and call {_registerInterfaceForAddress} to + * declare their willingness to be implementers. + * {IERC1820Registry-setInterfaceImplementer} should then be called for the + * registration to be complete. + */ +contract ERC1820Implementer is IERC1820Implementer { + bytes32 private constant _ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC"); + + mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces; + + /** + * @dev See {IERC1820Implementer-canImplementInterfaceForAddress}. + */ + function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) + public + view + virtual + override + returns (bytes32) + { + return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00); + } + + /** + * @dev Declares the contract as willing to be an implementer of + * `interfaceHash` for `account`. + * + * See {IERC1820Registry-setInterfaceImplementer} and + * {IERC1820Registry-interfaceHash}. + */ + function _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal virtual { + _supportedInterfaces[interfaceHash][account] = true; + } +} diff --git a/certora/munged/utils/introspection/IERC165.sol b/certora/munged/utils/introspection/IERC165.sol new file mode 100644 index 000000000..71eb80151 --- /dev/null +++ b/certora/munged/utils/introspection/IERC165.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/introspection/IERC165.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC165 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-165[EIP]. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others ({ERC165Checker}). + * + * For an implementation, see {ERC165}. + */ +interface IERC165 { + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} diff --git a/certora/munged/utils/introspection/IERC1820Implementer.sol b/certora/munged/utils/introspection/IERC1820Implementer.sol new file mode 100644 index 000000000..98ee372e4 --- /dev/null +++ b/certora/munged/utils/introspection/IERC1820Implementer.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/introspection/IERC1820Implementer.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface for an ERC1820 implementer, as defined in the + * https://eips.ethereum.org/EIPS/eip-1820#interface-implementation-erc1820implementerinterface[EIP]. + * Used by contracts that will be registered as implementers in the + * {IERC1820Registry}. + */ +interface IERC1820Implementer { + /** + * @dev Returns a special value (`ERC1820_ACCEPT_MAGIC`) if this contract + * implements `interfaceHash` for `account`. + * + * See {IERC1820Registry-setInterfaceImplementer}. + */ + function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) external view returns (bytes32); +} diff --git a/certora/munged/utils/introspection/IERC1820Registry.sol b/certora/munged/utils/introspection/IERC1820Registry.sol new file mode 100644 index 000000000..eb5699b8e --- /dev/null +++ b/certora/munged/utils/introspection/IERC1820Registry.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/introspection/IERC1820Registry.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the global ERC1820 Registry, as defined in the + * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register + * implementers for interfaces in this registry, as well as query support. + * + * Implementers may be shared by multiple accounts, and can also implement more + * than a single interface for each account. Contracts can implement interfaces + * for themselves, but externally-owned accounts (EOA) must delegate this to a + * contract. + * + * {IERC165} interfaces can also be queried via the registry. + * + * For an in-depth explanation and source code analysis, see the EIP text. + */ +interface IERC1820Registry { + /** + * @dev Sets `newManager` as the manager for `account`. A manager of an + * account is able to set interface implementers for it. + * + * By default, each account is its own manager. Passing a value of `0x0` in + * `newManager` will reset the manager to this initial state. + * + * Emits a {ManagerChanged} event. + * + * Requirements: + * + * - the caller must be the current manager for `account`. + */ + function setManager(address account, address newManager) external; + + /** + * @dev Returns the manager for `account`. + * + * See {setManager}. + */ + function getManager(address account) external view returns (address); + + /** + * @dev Sets the `implementer` contract as ``account``'s implementer for + * `interfaceHash`. + * + * `account` being the zero address is an alias for the caller's address. + * The zero address can also be used in `implementer` to remove an old one. + * + * See {interfaceHash} to learn how these are created. + * + * Emits an {InterfaceImplementerSet} event. + * + * Requirements: + * + * - the caller must be the current manager for `account`. + * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not + * end in 28 zeroes). + * - `implementer` must implement {IERC1820Implementer} and return true when + * queried for support, unless `implementer` is the caller. See + * {IERC1820Implementer-canImplementInterfaceForAddress}. + */ + function setInterfaceImplementer( + address account, + bytes32 _interfaceHash, + address implementer + ) external; + + /** + * @dev Returns the implementer of `interfaceHash` for `account`. If no such + * implementer is registered, returns the zero address. + * + * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28 + * zeroes), `account` will be queried for support of it. + * + * `account` being the zero address is an alias for the caller's address. + */ + function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address); + + /** + * @dev Returns the interface hash for an `interfaceName`, as defined in the + * corresponding + * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP]. + */ + function interfaceHash(string calldata interfaceName) external pure returns (bytes32); + + /** + * @notice Updates the cache with whether the contract implements an ERC165 interface or not. + * @param account Address of the contract for which to update the cache. + * @param interfaceId ERC165 interface for which to update the cache. + */ + function updateERC165Cache(address account, bytes4 interfaceId) external; + + /** + * @notice Checks whether a contract implements an ERC165 interface or not. + * If the result is not cached a direct lookup on the contract address is performed. + * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling + * {updateERC165Cache} with the contract address. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account` implements `interfaceId`, false otherwise. + */ + function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); + + /** + * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account` implements `interfaceId`, false otherwise. + */ + function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); + + event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); + + event ManagerChanged(address indexed account, address indexed newManager); +} diff --git a/certora/munged/utils/math/Math.sol b/certora/munged/utils/math/Math.sol new file mode 100644 index 000000000..b31bca303 --- /dev/null +++ b/certora/munged/utils/math/Math.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/math/Math.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Standard math utilities missing in the Solidity language. + */ +library Math { + /** + * @dev Returns the largest of two numbers. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return a >= b ? a : b; + } + + /** + * @dev Returns the smallest of two numbers. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two numbers. The result is rounded towards + * zero. + */ + function average(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b) / 2 can overflow. + return (a & b) + (a ^ b) / 2; + } + + /** + * @dev Returns the ceiling of the division of two numbers. + * + * This differs from standard division with `/` in that it rounds up instead + * of rounding down. + */ + function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b - 1) / b can overflow on addition, so we distribute. + return a / b + (a % b == 0 ? 0 : 1); + } +} diff --git a/certora/munged/utils/math/SafeCast.sol b/certora/munged/utils/math/SafeCast.sol new file mode 100644 index 000000000..19648dc91 --- /dev/null +++ b/certora/munged/utils/math/SafeCast.sol @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/math/SafeCast.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. `SafeCast` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing + * all math on `uint256` and `int256` and then downcasting. + */ +library SafeCast { + /** + * @dev Returns the downcasted uint224 from uint256, reverting on + * overflow (when the input is greater than largest uint224). + * + * Counterpart to Solidity's `uint224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + */ + function toUint224(uint256 value) internal pure returns (uint224) { + require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); + return uint224(value); + } + + /** + * @dev Returns the downcasted uint128 from uint256, reverting on + * overflow (when the input is greater than largest uint128). + * + * Counterpart to Solidity's `uint128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + */ + function toUint128(uint256 value) internal pure returns (uint128) { + require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); + return uint128(value); + } + + /** + * @dev Returns the downcasted uint96 from uint256, reverting on + * overflow (when the input is greater than largest uint96). + * + * Counterpart to Solidity's `uint96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + */ + function toUint96(uint256 value) internal pure returns (uint96) { + require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); + return uint96(value); + } + + /** + * @dev Returns the downcasted uint64 from uint256, reverting on + * overflow (when the input is greater than largest uint64). + * + * Counterpart to Solidity's `uint64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + */ + function toUint64(uint256 value) internal pure returns (uint64) { + require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); + return uint64(value); + } + + /** + * @dev Returns the downcasted uint32 from uint256, reverting on + * overflow (when the input is greater than largest uint32). + * + * Counterpart to Solidity's `uint32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + */ + function toUint32(uint256 value) internal pure returns (uint32) { + require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); + return uint32(value); + } + + /** + * @dev Returns the downcasted uint16 from uint256, reverting on + * overflow (when the input is greater than largest uint16). + * + * Counterpart to Solidity's `uint16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + */ + function toUint16(uint256 value) internal pure returns (uint16) { + require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); + return uint16(value); + } + + /** + * @dev Returns the downcasted uint8 from uint256, reverting on + * overflow (when the input is greater than largest uint8). + * + * Counterpart to Solidity's `uint8` operator. + * + * Requirements: + * + * - input must fit into 8 bits. + */ + function toUint8(uint256 value) internal pure returns (uint8) { + require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); + return uint8(value); + } + + /** + * @dev Converts a signed int256 into an unsigned uint256. + * + * Requirements: + * + * - input must be greater than or equal to 0. + */ + function toUint256(int256 value) internal pure returns (uint256) { + require(value >= 0, "SafeCast: value must be positive"); + return uint256(value); + } + + /** + * @dev Returns the downcasted int128 from int256, reverting on + * overflow (when the input is less than smallest int128 or + * greater than largest int128). + * + * Counterpart to Solidity's `int128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + * + * _Available since v3.1._ + */ + function toInt128(int256 value) internal pure returns (int128) { + require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits"); + return int128(value); + } + + /** + * @dev Returns the downcasted int64 from int256, reverting on + * overflow (when the input is less than smallest int64 or + * greater than largest int64). + * + * Counterpart to Solidity's `int64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + * + * _Available since v3.1._ + */ + function toInt64(int256 value) internal pure returns (int64) { + require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits"); + return int64(value); + } + + /** + * @dev Returns the downcasted int32 from int256, reverting on + * overflow (when the input is less than smallest int32 or + * greater than largest int32). + * + * Counterpart to Solidity's `int32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + * + * _Available since v3.1._ + */ + function toInt32(int256 value) internal pure returns (int32) { + require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits"); + return int32(value); + } + + /** + * @dev Returns the downcasted int16 from int256, reverting on + * overflow (when the input is less than smallest int16 or + * greater than largest int16). + * + * Counterpart to Solidity's `int16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + * + * _Available since v3.1._ + */ + function toInt16(int256 value) internal pure returns (int16) { + require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits"); + return int16(value); + } + + /** + * @dev Returns the downcasted int8 from int256, reverting on + * overflow (when the input is less than smallest int8 or + * greater than largest int8). + * + * Counterpart to Solidity's `int8` operator. + * + * Requirements: + * + * - input must fit into 8 bits. + * + * _Available since v3.1._ + */ + function toInt8(int256 value) internal pure returns (int8) { + require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits"); + return int8(value); + } + + /** + * @dev Converts an unsigned uint256 into a signed int256. + * + * Requirements: + * + * - input must be less than or equal to maxInt256. + */ + function toInt256(uint256 value) internal pure returns (int256) { + // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive + require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); + return int256(value); + } +} diff --git a/certora/munged/utils/math/SafeMath.sol b/certora/munged/utils/math/SafeMath.sol new file mode 100644 index 000000000..275331061 --- /dev/null +++ b/certora/munged/utils/math/SafeMath.sol @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/math/SafeMath.sol) + +pragma solidity ^0.8.0; + +// CAUTION +// This version of SafeMath should only be used with Solidity 0.8 or later, +// because it relies on the compiler's built in overflow checks. + +/** + * @dev Wrappers over Solidity's arithmetic operations. + * + * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler + * now has built in overflow checking. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + uint256 c = a + b; + if (c < a) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the substraction of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b > a) return (false, 0); + return (true, a - b); + } + } + + /** + * @dev Returns the multiplication of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) return (true, 0); + uint256 c = a * b; + if (c / a != b) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the division of two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a / b); + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a % b); + } + } + + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + return a + b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return a - b; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + return a * b; + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return a / b; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + return a % b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {trySub}. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b <= a, errorMessage); + return a - b; + } + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting with custom message on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b > 0, errorMessage); + return a / b; + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting with custom message when dividing by zero. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryMod}. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b > 0, errorMessage); + return a % b; + } + } +} diff --git a/certora/munged/utils/math/SignedSafeMath.sol b/certora/munged/utils/math/SignedSafeMath.sol new file mode 100644 index 000000000..f9230da9b --- /dev/null +++ b/certora/munged/utils/math/SignedSafeMath.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/math/SignedSafeMath.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations. + * + * NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler + * now has built in overflow checking. + */ +library SignedSafeMath { + /** + * @dev Returns the multiplication of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(int256 a, int256 b) internal pure returns (int256) { + return a * b; + } + + /** + * @dev Returns the integer division of two signed integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(int256 a, int256 b) internal pure returns (int256) { + return a / b; + } + + /** + * @dev Returns the subtraction of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(int256 a, int256 b) internal pure returns (int256) { + return a - b; + } + + /** + * @dev Returns the addition of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(int256 a, int256 b) internal pure returns (int256) { + return a + b; + } +} diff --git a/certora/munged/utils/structs/BitMaps.sol b/certora/munged/utils/structs/BitMaps.sol new file mode 100644 index 000000000..966691e3a --- /dev/null +++ b/certora/munged/utils/structs/BitMaps.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/structs/BitMaps.sol) +pragma solidity ^0.8.0; + +/** + * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential. + * Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor]. + */ +library BitMaps { + struct BitMap { + mapping(uint256 => uint256) _data; + } + + /** + * @dev Returns whether the bit at `index` is set. + */ + function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { + uint256 bucket = index >> 8; + uint256 mask = 1 << (index & 0xff); + return bitmap._data[bucket] & mask != 0; + } + + /** + * @dev Sets the bit at `index` to the boolean `value`. + */ + function setTo( + BitMap storage bitmap, + uint256 index, + bool value + ) internal { + if (value) { + set(bitmap, index); + } else { + unset(bitmap, index); + } + } + + /** + * @dev Sets the bit at `index`. + */ + function set(BitMap storage bitmap, uint256 index) internal { + uint256 bucket = index >> 8; + uint256 mask = 1 << (index & 0xff); + bitmap._data[bucket] |= mask; + } + + /** + * @dev Unsets the bit at `index`. + */ + function unset(BitMap storage bitmap, uint256 index) internal { + uint256 bucket = index >> 8; + uint256 mask = 1 << (index & 0xff); + bitmap._data[bucket] &= ~mask; + } +} diff --git a/certora/munged/utils/structs/EnumerableMap.sol b/certora/munged/utils/structs/EnumerableMap.sol new file mode 100644 index 000000000..83a7f17ce --- /dev/null +++ b/certora/munged/utils/structs/EnumerableMap.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/structs/EnumerableMap.sol) + +pragma solidity ^0.8.0; + +import "./EnumerableSet.sol"; + +/** + * @dev Library for managing an enumerable variant of Solidity's + * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] + * type. + * + * Maps have the following properties: + * + * - Entries are added, removed, and checked for existence in constant time + * (O(1)). + * - Entries are enumerated in O(n). No guarantees are made on the ordering. + * + * ``` + * contract Example { + * // Add the library methods + * using EnumerableMap for EnumerableMap.UintToAddressMap; + * + * // Declare a set state variable + * EnumerableMap.UintToAddressMap private myMap; + * } + * ``` + * + * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are + * supported. + */ +library EnumerableMap { + using EnumerableSet for EnumerableSet.Bytes32Set; + + // To implement this library for multiple types with as little code + // repetition as possible, we write it in terms of a generic Map type with + // bytes32 keys and values. + // The Map implementation uses private functions, and user-facing + // implementations (such as Uint256ToAddressMap) are just wrappers around + // the underlying Map. + // This means that we can only create new EnumerableMaps for types that fit + // in bytes32. + + struct Map { + // Storage of keys + EnumerableSet.Bytes32Set _keys; + mapping(bytes32 => bytes32) _values; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function _set( + Map storage map, + bytes32 key, + bytes32 value + ) private returns (bool) { + map._values[key] = value; + return map._keys.add(key); + } + + /** + * @dev Removes a key-value pair from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function _remove(Map storage map, bytes32 key) private returns (bool) { + delete map._values[key]; + return map._keys.remove(key); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function _contains(Map storage map, bytes32 key) private view returns (bool) { + return map._keys.contains(key); + } + + /** + * @dev Returns the number of key-value pairs in the map. O(1). + */ + function _length(Map storage map) private view returns (uint256) { + return map._keys.length(); + } + + /** + * @dev Returns the key-value pair stored at position `index` in the map. O(1). + * + * Note that there are no guarantees on the ordering of entries inside the + * array, and it may change when more entries are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) { + bytes32 key = map._keys.at(index); + return (key, map._values[key]); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) { + bytes32 value = map._values[key]; + if (value == bytes32(0)) { + return (_contains(map, key), bytes32(0)); + } else { + return (true, value); + } + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function _get(Map storage map, bytes32 key) private view returns (bytes32) { + bytes32 value = map._values[key]; + require(value != 0 || _contains(map, key), "EnumerableMap: nonexistent key"); + return value; + } + + /** + * @dev Same as {_get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {_tryGet}. + */ + function _get( + Map storage map, + bytes32 key, + string memory errorMessage + ) private view returns (bytes32) { + bytes32 value = map._values[key]; + require(value != 0 || _contains(map, key), errorMessage); + return value; + } + + // UintToAddressMap + + struct UintToAddressMap { + Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set( + UintToAddressMap storage map, + uint256 key, + address value + ) internal returns (bool) { + return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value)))); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { + return _remove(map._inner, bytes32(key)); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { + return _contains(map._inner, bytes32(key)); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(UintToAddressMap storage map) internal view returns (uint256) { + return _length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) { + (bytes32 key, bytes32 value) = _at(map._inner, index); + return (uint256(key), address(uint160(uint256(value)))); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + * + * _Available since v3.4._ + */ + function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) { + (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key)); + return (success, address(uint160(uint256(value)))); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { + return address(uint160(uint256(_get(map._inner, bytes32(key))))); + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + UintToAddressMap storage map, + uint256 key, + string memory errorMessage + ) internal view returns (address) { + return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage)))); + } +} diff --git a/certora/munged/utils/structs/EnumerableSet.sol b/certora/munged/utils/structs/EnumerableSet.sol new file mode 100644 index 000000000..2945ecca9 --- /dev/null +++ b/certora/munged/utils/structs/EnumerableSet.sol @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.3.2 (utils/structs/EnumerableSet.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Library for managing + * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive + * types. + * + * Sets have the following properties: + * + * - Elements are added, removed, and checked for existence in constant time + * (O(1)). + * - Elements are enumerated in O(n). No guarantees are made on the ordering. + * + * ``` + * contract Example { + * // Add the library methods + * using EnumerableSet for EnumerableSet.AddressSet; + * + * // Declare a set state variable + * EnumerableSet.AddressSet private mySet; + * } + * ``` + * + * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) + * and `uint256` (`UintSet`) are supported. + */ +library EnumerableSet { + // To implement this library for multiple types with as little code + // repetition as possible, we write it in terms of a generic Set type with + // bytes32 values. + // The Set implementation uses private functions, and user-facing + // implementations (such as AddressSet) are just wrappers around the + // underlying Set. + // This means that we can only create new EnumerableSets for types that fit + // in bytes32. + + struct Set { + // Storage of set values + bytes32[] _values; + // Position of the value in the `values` array, plus 1 because index 0 + // means a value is not in the set. + mapping(bytes32 => uint256) _indexes; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function _add(Set storage set, bytes32 value) private returns (bool) { + if (!_contains(set, value)) { + set._values.push(value); + // The value is stored at length-1, but we add 1 to all indexes + // and use 0 as a sentinel value + set._indexes[value] = set._values.length; + return true; + } else { + return false; + } + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function _remove(Set storage set, bytes32 value) private returns (bool) { + // We read and store the value's index to prevent multiple reads from the same storage slot + uint256 valueIndex = set._indexes[value]; + + if (valueIndex != 0) { + // Equivalent to contains(set, value) + // To delete an element from the _values array in O(1), we swap the element to delete with the last one in + // the array, and then remove the last element (sometimes called as 'swap and pop'). + // This modifies the order of the array, as noted in {at}. + + uint256 toDeleteIndex = valueIndex - 1; + uint256 lastIndex = set._values.length - 1; + + if (lastIndex != toDeleteIndex) { + bytes32 lastvalue = set._values[lastIndex]; + + // Move the last value to the index where the value to delete is + set._values[toDeleteIndex] = lastvalue; + // Update the index for the moved value + set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex + } + + // Delete the slot where the moved value was stored + set._values.pop(); + + // Delete the index for the deleted slot + delete set._indexes[value]; + + return true; + } else { + return false; + } + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function _contains(Set storage set, bytes32 value) private view returns (bool) { + return set._indexes[value] != 0; + } + + /** + * @dev Returns the number of values on the set. O(1). + */ + function _length(Set storage set) private view returns (uint256) { + return set._values.length; + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function _at(Set storage set, uint256 index) private view returns (bytes32) { + return set._values[index]; + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function _values(Set storage set) private view returns (bytes32[] memory) { + return set._values; + } + + // Bytes32Set + + struct Bytes32Set { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { + return _add(set._inner, value); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { + return _remove(set._inner, value); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { + return _contains(set._inner, value); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(Bytes32Set storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { + return _at(set._inner, index); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { + return _values(set._inner); + } + + // AddressSet + + struct AddressSet { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(AddressSet storage set, address value) internal returns (bool) { + return _add(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(AddressSet storage set, address value) internal returns (bool) { + return _remove(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(AddressSet storage set, address value) internal view returns (bool) { + return _contains(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(AddressSet storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(AddressSet storage set, uint256 index) internal view returns (address) { + return address(uint160(uint256(_at(set._inner, index)))); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(AddressSet storage set) internal view returns (address[] memory) { + bytes32[] memory store = _values(set._inner); + address[] memory result; + + assembly { + result := store + } + + return result; + } + + // UintSet + + struct UintSet { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(UintSet storage set, uint256 value) internal returns (bool) { + return _add(set._inner, bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(UintSet storage set, uint256 value) internal returns (bool) { + return _remove(set._inner, bytes32(value)); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(UintSet storage set, uint256 value) internal view returns (bool) { + return _contains(set._inner, bytes32(value)); + } + + /** + * @dev Returns the number of values on the set. O(1). + */ + function length(UintSet storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintSet storage set, uint256 index) internal view returns (uint256) { + return uint256(_at(set._inner, index)); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(UintSet storage set) internal view returns (uint256[] memory) { + bytes32[] memory store = _values(set._inner); + uint256[] memory result; + + assembly { + result := store + } + + return result; + } +} diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index e8d369452..f11287ca8 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -38,8 +38,8 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { string private _name; - mapping(uint256 => ProposalCore) public _proposals; - + mapping(uint256 => ProposalCore) private _proposals; + /** * @dev Restrict access to governor executing address. Some module might override the _executor function to make * sure this modifier is consistant with the execution model. @@ -154,12 +154,12 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { /** * @dev Amount of votes already cast passes the threshold limit. */ - function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal + function _quorumReached(uint256 proposalId) internal view virtual returns (bool); /** * @dev Is the proposal successful or not. */ - function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal + function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool); /** * @dev Register a vote with a given support and voting weight. @@ -320,7 +320,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { v, r, s - ); // mention that we assume that hashing works correctly + ); return _castVote(proposalId, voter, support, ""); } diff --git a/contracts/governance/TimelockController.sol b/contracts/governance/TimelockController.sol index b3b551dde..affcbbdc6 100644 --- a/contracts/governance/TimelockController.sol +++ b/contracts/governance/TimelockController.sol @@ -299,7 +299,6 @@ contract TimelockController is AccessControl { _call(id, i, targets[i], values[i], datas[i]); } _afterCall(id); - // ASSUME THAT THERE IS NO REENTRANCY IN WIZARDHARNESS1 } /** diff --git a/contracts/governance/compatibility/GovernorCompatibilityBravo.sol b/contracts/governance/compatibility/GovernorCompatibilityBravo.sol index 20e507b61..b05130186 100644 --- a/contracts/governance/compatibility/GovernorCompatibilityBravo.sol +++ b/contracts/governance/compatibility/GovernorCompatibilityBravo.sol @@ -245,7 +245,7 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp /** * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. */ - function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal + function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { ProposalDetails storage details = _proposalDetails[proposalId]; return quorum(proposalSnapshot(proposalId)) < details.forVotes; } @@ -253,7 +253,7 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp /** * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be scritly over the againstVotes. */ - function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal + function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { ProposalDetails storage details = _proposalDetails[proposalId]; return details.forVotes > details.againstVotes; } diff --git a/contracts/governance/extensions/GovernorCountingSimple.sol b/contracts/governance/extensions/GovernorCountingSimple.sol index b8c72ed9e..782c8a699 100644 --- a/contracts/governance/extensions/GovernorCountingSimple.sol +++ b/contracts/governance/extensions/GovernorCountingSimple.sol @@ -64,7 +64,7 @@ abstract contract GovernorCountingSimple is Governor { /** * @dev See {Governor-_quorumReached}. */ - function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { + function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { ProposalVote storage proposalvote = _proposalVotes[proposalId]; return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes; @@ -73,7 +73,7 @@ abstract contract GovernorCountingSimple is Governor { /** * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. */ - function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { + function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { ProposalVote storage proposalvote = _proposalVotes[proposalId]; return proposalvote.forVotes > proposalvote.againstVotes; diff --git a/contracts/governance/extensions/GovernorTimelockControl.sol b/contracts/governance/extensions/GovernorTimelockControl.sol index 892ec3a55..f7a01c06d 100644 --- a/contracts/governance/extensions/GovernorTimelockControl.sol +++ b/contracts/governance/extensions/GovernorTimelockControl.sol @@ -109,7 +109,7 @@ abstract contract GovernorTimelockControl is IGovernorTimelock, Governor { bytes[] memory calldatas, bytes32 descriptionHash ) internal virtual override { - _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); + _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); } /** diff --git a/contracts/token/ERC20/extensions/ERC20Votes.sol b/contracts/token/ERC20/extensions/ERC20Votes.sol index 06fd68831..5e176973e 100644 --- a/contracts/token/ERC20/extensions/ERC20Votes.sol +++ b/contracts/token/ERC20/extensions/ERC20Votes.sol @@ -84,7 +84,7 @@ abstract contract ERC20Votes is ERC20Permit { * * - `blockNumber` must have been already mined */ - function getPastVotes(address account, uint256 blockNumber) public view virtual returns (uint256) { + function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) { require(blockNumber < block.number, "ERC20Votes: block not yet mined"); return _checkpointsLookup(_checkpoints[account], blockNumber); } From f3087407c6e54487ba9c25d0d5e38ff450e95f07 Mon Sep 17 00:00:00 2001 From: Michael George Date: Wed, 1 Dec 2021 10:08:06 -0500 Subject: [PATCH 124/254] created applyHarness --- certora/Makefile | 24 + certora/applyHarness.patch | 121 ++++ certora/munged/.gitignore | 2 + certora/munged/access/AccessControl.sol | 223 -------- .../munged/access/AccessControlEnumerable.sol | 64 --- certora/munged/access/IAccessControl.sol | 88 --- .../access/IAccessControlEnumerable.sol | 31 - certora/munged/access/Ownable.sol | 76 --- certora/munged/access/README.adoc | 21 - certora/munged/finance/PaymentSplitter.sol | 189 ------ certora/munged/finance/README.adoc | 20 - certora/munged/finance/VestingWallet.sol | 135 ----- certora/munged/governance/Governor.sol | 357 ------------ certora/munged/governance/IGovernor.sol | 218 ------- certora/munged/governance/README.adoc | 168 ------ .../munged/governance/TimelockController.sol | 354 ------------ .../GovernorCompatibilityBravo.sol | 288 ---------- .../IGovernorCompatibilityBravo.sol | 114 ---- .../extensions/GovernorCountingSimple.sol | 106 ---- .../extensions/GovernorProposalThreshold.sol | 23 - .../extensions/GovernorSettings.sol | 114 ---- .../extensions/GovernorTimelockCompound.sol | 244 -------- .../extensions/GovernorTimelockControl.sol | 154 ----- .../governance/extensions/GovernorVotes.sol | 28 - .../extensions/GovernorVotesComp.sol | 27 - .../GovernorVotesQuorumFraction.sol | 50 -- .../extensions/IGovernorTimelock.sol | 26 - certora/munged/interfaces/IERC1155.sol | 6 - .../munged/interfaces/IERC1155MetadataURI.sol | 6 - .../munged/interfaces/IERC1155Receiver.sol | 6 - certora/munged/interfaces/IERC1271.sol | 19 - certora/munged/interfaces/IERC1363.sol | 95 --- .../munged/interfaces/IERC1363Receiver.sol | 32 -- certora/munged/interfaces/IERC1363Spender.sol | 30 - certora/munged/interfaces/IERC165.sol | 6 - .../munged/interfaces/IERC1820Implementer.sol | 6 - .../munged/interfaces/IERC1820Registry.sol | 6 - certora/munged/interfaces/IERC20.sol | 6 - certora/munged/interfaces/IERC20Metadata.sol | 6 - certora/munged/interfaces/IERC2981.sol | 23 - certora/munged/interfaces/IERC3156.sol | 7 - .../interfaces/IERC3156FlashBorrower.sol | 29 - .../munged/interfaces/IERC3156FlashLender.sol | 43 -- certora/munged/interfaces/IERC721.sol | 6 - .../munged/interfaces/IERC721Enumerable.sol | 6 - certora/munged/interfaces/IERC721Metadata.sol | 6 - certora/munged/interfaces/IERC721Receiver.sol | 6 - certora/munged/interfaces/IERC777.sol | 6 - .../munged/interfaces/IERC777Recipient.sol | 6 - certora/munged/interfaces/IERC777Sender.sol | 6 - certora/munged/interfaces/README.adoc | 50 -- certora/munged/interfaces/draft-IERC2612.sol | 8 - certora/munged/metatx/ERC2771Context.sol | 40 -- certora/munged/metatx/MinimalForwarder.sol | 59 -- certora/munged/metatx/README.adoc | 12 - .../mocks/AccessControlEnumerableMock.sol | 17 - certora/munged/mocks/AccessControlMock.sol | 17 - certora/munged/mocks/AddressImpl.sol | 46 -- certora/munged/mocks/ArraysImpl.sol | 19 - certora/munged/mocks/BadBeacon.sol | 11 - certora/munged/mocks/BitmapMock.sol | 27 - certora/munged/mocks/CallReceiverMock.sol | 50 -- .../munged/mocks/ClashingImplementation.sol | 18 - certora/munged/mocks/ClonesMock.sol | 36 -- .../munged/mocks/ConditionalEscrowMock.sol | 18 - certora/munged/mocks/ContextMock.sol | 33 -- certora/munged/mocks/CountersImpl.sol | 27 - certora/munged/mocks/Create2Impl.sol | 34 -- certora/munged/mocks/DummyImplementation.sol | 61 -- certora/munged/mocks/ECDSAMock.sol | 41 -- certora/munged/mocks/EIP712External.sol | 31 - certora/munged/mocks/ERC1155BurnableMock.sol | 18 - certora/munged/mocks/ERC1155Mock.sol | 51 -- certora/munged/mocks/ERC1155PausableMock.sol | 29 - certora/munged/mocks/ERC1155ReceiverMock.sol | 52 -- certora/munged/mocks/ERC1155SupplyMock.sol | 21 - certora/munged/mocks/ERC1271WalletMock.sol | 17 - .../ERC165/ERC165InterfacesSupported.sol | 58 -- .../munged/mocks/ERC165/ERC165MissingData.sol | 7 - .../mocks/ERC165/ERC165NotSupported.sol | 5 - certora/munged/mocks/ERC165CheckerMock.sol | 25 - certora/munged/mocks/ERC165Mock.sol | 7 - certora/munged/mocks/ERC165StorageMock.sol | 11 - .../munged/mocks/ERC1820ImplementerMock.sol | 11 - certora/munged/mocks/ERC20BurnableMock.sol | 16 - certora/munged/mocks/ERC20CappedMock.sol | 17 - certora/munged/mocks/ERC20DecimalsMock.sol | 21 - certora/munged/mocks/ERC20FlashMintMock.sol | 16 - certora/munged/mocks/ERC20Mock.sol | 41 -- certora/munged/mocks/ERC20PausableMock.sol | 33 -- certora/munged/mocks/ERC20PermitMock.sol | 20 - certora/munged/mocks/ERC20SnapshotMock.sol | 28 - certora/munged/mocks/ERC20VotesCompMock.sol | 21 - certora/munged/mocks/ERC20VotesMock.sol | 21 - certora/munged/mocks/ERC20WrapperMock.sol | 17 - certora/munged/mocks/ERC2771ContextMock.sol | 19 - .../munged/mocks/ERC3156FlashBorrowerMock.sol | 53 -- certora/munged/mocks/ERC721BurnableMock.sol | 29 - certora/munged/mocks/ERC721EnumerableMock.sol | 51 -- certora/munged/mocks/ERC721Mock.sol | 41 -- certora/munged/mocks/ERC721PausableMock.sol | 45 -- certora/munged/mocks/ERC721ReceiverMock.sol | 42 -- certora/munged/mocks/ERC721URIStorageMock.sol | 55 -- certora/munged/mocks/ERC777Mock.sol | 56 -- .../mocks/ERC777SenderRecipientMock.sol | 161 ------ certora/munged/mocks/EnumerableMapMock.sol | 47 -- certora/munged/mocks/EnumerableSetMock.sol | 110 ---- certora/munged/mocks/EtherReceiverMock.sol | 17 - certora/munged/mocks/GovernorCompMock.sol | 41 -- .../mocks/GovernorCompatibilityBravoMock.sol | 140 ----- certora/munged/mocks/GovernorMock.sol | 60 -- .../mocks/GovernorTimelockCompoundMock.sol | 108 ---- .../mocks/GovernorTimelockControlMock.sol | 108 ---- certora/munged/mocks/InitializableMock.sol | 34 -- certora/munged/mocks/MathMock.sol | 23 - certora/munged/mocks/MerkleProofWrapper.sol | 19 - certora/munged/mocks/MulticallTest.sol | 23 - certora/munged/mocks/MulticallTokenMock.sol | 10 - .../MultipleInheritanceInitializableMocks.sol | 81 --- certora/munged/mocks/OwnableMock.sol | 7 - certora/munged/mocks/PausableMock.sol | 31 - certora/munged/mocks/PullPaymentMock.sol | 15 - certora/munged/mocks/ReentrancyAttack.sol | 12 - certora/munged/mocks/ReentrancyMock.sol | 43 -- .../munged/mocks/RegressionImplementation.sol | 61 -- certora/munged/mocks/SafeCastMock.sol | 66 --- certora/munged/mocks/SafeERC20Helper.sol | 144 ----- certora/munged/mocks/SafeMathMock.sol | 138 ----- certora/munged/mocks/SignatureCheckerMock.sol | 17 - certora/munged/mocks/SignedSafeMathMock.sol | 23 - .../SingleInheritanceInitializableMocks.sol | 49 -- certora/munged/mocks/StorageSlotMock.sol | 41 -- certora/munged/mocks/StringsMock.sol | 19 - .../munged/mocks/TimersBlockNumberImpl.sol | 39 -- certora/munged/mocks/TimersTimestampImpl.sol | 39 -- certora/munged/mocks/UUPS/TestInProd.sol | 31 - .../munged/mocks/compound/CompTimelock.sol | 174 ------ certora/munged/mocks/wizard/MyGovernor1.sol | 96 ---- certora/munged/mocks/wizard/MyGovernor2.sol | 102 ---- certora/munged/mocks/wizard/MyGovernor3.sol | 105 ---- certora/munged/package.json | 32 -- certora/munged/proxy/Clones.sol | 84 --- certora/munged/proxy/ERC1967/ERC1967Proxy.sol | 33 -- .../munged/proxy/ERC1967/ERC1967Upgrade.sol | 194 ------- certora/munged/proxy/Proxy.sol | 86 --- certora/munged/proxy/README.adoc | 83 --- certora/munged/proxy/beacon/BeaconProxy.sol | 62 -- certora/munged/proxy/beacon/IBeacon.sol | 16 - .../munged/proxy/beacon/UpgradeableBeacon.sol | 65 --- .../munged/proxy/transparent/ProxyAdmin.sol | 81 --- .../TransparentUpgradeableProxy.sol | 125 ---- certora/munged/proxy/utils/Initializable.sol | 62 -- .../munged/proxy/utils/UUPSUpgradeable.sol | 73 --- certora/munged/security/Pausable.sol | 91 --- certora/munged/security/PullPayment.sol | 70 --- certora/munged/security/README.adoc | 20 - certora/munged/security/ReentrancyGuard.sol | 63 -- certora/munged/token/ERC1155/ERC1155.sol | 464 --------------- certora/munged/token/ERC1155/IERC1155.sol | 125 ---- .../munged/token/ERC1155/IERC1155Receiver.sol | 58 -- certora/munged/token/ERC1155/README.adoc | 47 -- .../ERC1155/extensions/ERC1155Burnable.sol | 40 -- .../ERC1155/extensions/ERC1155Pausable.sol | 38 -- .../ERC1155/extensions/ERC1155Supply.sol | 58 -- .../extensions/IERC1155MetadataURI.sol | 22 - .../presets/ERC1155PresetMinterPauser.sol | 126 ---- .../token/ERC1155/utils/ERC1155Holder.sol | 36 -- .../token/ERC1155/utils/ERC1155Receiver.sol | 19 - certora/munged/token/ERC20/ERC20.sol | 356 ------------ certora/munged/token/ERC20/IERC20.sol | 82 --- certora/munged/token/ERC20/README.adoc | 83 --- .../token/ERC20/extensions/ERC20Burnable.sol | 43 -- .../token/ERC20/extensions/ERC20Capped.sol | 37 -- .../token/ERC20/extensions/ERC20FlashMint.sol | 77 --- .../token/ERC20/extensions/ERC20Pausable.sol | 33 -- .../token/ERC20/extensions/ERC20Snapshot.sol | 195 ------- .../token/ERC20/extensions/ERC20Votes.sol | 260 --------- .../token/ERC20/extensions/ERC20VotesComp.sol | 48 -- .../token/ERC20/extensions/ERC20Wrapper.sol | 52 -- .../token/ERC20/extensions/IERC20Metadata.sol | 28 - .../ERC20/extensions/draft-ERC20Permit.sol | 87 --- .../ERC20/extensions/draft-IERC20Permit.sol | 60 -- .../ERC20/presets/ERC20PresetFixedSupply.sol | 33 -- .../ERC20/presets/ERC20PresetMinterPauser.sol | 92 --- .../munged/token/ERC20/utils/SafeERC20.sol | 99 ---- .../token/ERC20/utils/TokenTimelock.sol | 70 --- certora/munged/token/ERC721/ERC721.sol | 424 -------------- certora/munged/token/ERC721/IERC721.sol | 143 ----- .../munged/token/ERC721/IERC721Receiver.sol | 27 - certora/munged/token/ERC721/README.adoc | 52 -- .../ERC721/extensions/ERC721Burnable.sol | 26 - .../ERC721/extensions/ERC721Enumerable.sol | 163 ------ .../ERC721/extensions/ERC721Pausable.sol | 33 -- .../ERC721/extensions/ERC721URIStorage.sol | 67 --- .../ERC721/extensions/IERC721Enumerable.sol | 29 - .../ERC721/extensions/IERC721Metadata.sol | 27 - .../ERC721PresetMinterPauserAutoId.sol | 137 ----- .../token/ERC721/utils/ERC721Holder.sol | 28 - certora/munged/token/ERC777/ERC777.sol | 539 ------------------ certora/munged/token/ERC777/IERC777.sol | 193 ------- .../munged/token/ERC777/IERC777Recipient.sol | 35 -- certora/munged/token/ERC777/IERC777Sender.sol | 35 -- certora/munged/token/ERC777/README.adoc | 30 - .../presets/ERC777PresetFixedSupply.sol | 30 - certora/munged/utils/Address.sol | 217 ------- certora/munged/utils/Arrays.sol | 48 -- certora/munged/utils/Context.sol | 24 - certora/munged/utils/Counters.sol | 43 -- certora/munged/utils/Create2.sol | 65 --- certora/munged/utils/Multicall.sol | 24 - certora/munged/utils/README.adoc | 103 ---- certora/munged/utils/StorageSlot.sol | 84 --- certora/munged/utils/Strings.sol | 67 --- certora/munged/utils/Timers.sol | 73 --- certora/munged/utils/cryptography/ECDSA.sol | 234 -------- .../munged/utils/cryptography/MerkleProof.sol | 52 -- .../utils/cryptography/SignatureChecker.sol | 36 -- .../utils/cryptography/draft-EIP712.sol | 104 ---- .../munged/utils/escrow/ConditionalEscrow.sol | 25 - certora/munged/utils/escrow/Escrow.sol | 63 -- certora/munged/utils/escrow/RefundEscrow.sol | 100 ---- certora/munged/utils/introspection/ERC165.sol | 29 - .../utils/introspection/ERC165Checker.sol | 113 ---- .../utils/introspection/ERC165Storage.sol | 42 -- .../introspection/ERC1820Implementer.sol | 44 -- .../munged/utils/introspection/IERC165.sol | 25 - .../introspection/IERC1820Implementer.sol | 20 - .../utils/introspection/IERC1820Registry.sol | 116 ---- certora/munged/utils/math/Math.sol | 43 -- certora/munged/utils/math/SafeCast.sol | 241 -------- certora/munged/utils/math/SafeMath.sol | 227 -------- certora/munged/utils/math/SignedSafeMath.sol | 68 --- certora/munged/utils/structs/BitMaps.sol | 55 -- .../munged/utils/structs/EnumerableMap.sol | 240 -------- .../munged/utils/structs/EnumerableSet.sol | 357 ------------ 235 files changed, 147 insertions(+), 16337 deletions(-) create mode 100644 certora/Makefile create mode 100644 certora/applyHarness.patch create mode 100644 certora/munged/.gitignore delete mode 100644 certora/munged/access/AccessControl.sol delete mode 100644 certora/munged/access/AccessControlEnumerable.sol delete mode 100644 certora/munged/access/IAccessControl.sol delete mode 100644 certora/munged/access/IAccessControlEnumerable.sol delete mode 100644 certora/munged/access/Ownable.sol delete mode 100644 certora/munged/access/README.adoc delete mode 100644 certora/munged/finance/PaymentSplitter.sol delete mode 100644 certora/munged/finance/README.adoc delete mode 100644 certora/munged/finance/VestingWallet.sol delete mode 100644 certora/munged/governance/Governor.sol delete mode 100644 certora/munged/governance/IGovernor.sol delete mode 100644 certora/munged/governance/README.adoc delete mode 100644 certora/munged/governance/TimelockController.sol delete mode 100644 certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol delete mode 100644 certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol delete mode 100644 certora/munged/governance/extensions/GovernorCountingSimple.sol delete mode 100644 certora/munged/governance/extensions/GovernorProposalThreshold.sol delete mode 100644 certora/munged/governance/extensions/GovernorSettings.sol delete mode 100644 certora/munged/governance/extensions/GovernorTimelockCompound.sol delete mode 100644 certora/munged/governance/extensions/GovernorTimelockControl.sol delete mode 100644 certora/munged/governance/extensions/GovernorVotes.sol delete mode 100644 certora/munged/governance/extensions/GovernorVotesComp.sol delete mode 100644 certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol delete mode 100644 certora/munged/governance/extensions/IGovernorTimelock.sol delete mode 100644 certora/munged/interfaces/IERC1155.sol delete mode 100644 certora/munged/interfaces/IERC1155MetadataURI.sol delete mode 100644 certora/munged/interfaces/IERC1155Receiver.sol delete mode 100644 certora/munged/interfaces/IERC1271.sol delete mode 100644 certora/munged/interfaces/IERC1363.sol delete mode 100644 certora/munged/interfaces/IERC1363Receiver.sol delete mode 100644 certora/munged/interfaces/IERC1363Spender.sol delete mode 100644 certora/munged/interfaces/IERC165.sol delete mode 100644 certora/munged/interfaces/IERC1820Implementer.sol delete mode 100644 certora/munged/interfaces/IERC1820Registry.sol delete mode 100644 certora/munged/interfaces/IERC20.sol delete mode 100644 certora/munged/interfaces/IERC20Metadata.sol delete mode 100644 certora/munged/interfaces/IERC2981.sol delete mode 100644 certora/munged/interfaces/IERC3156.sol delete mode 100644 certora/munged/interfaces/IERC3156FlashBorrower.sol delete mode 100644 certora/munged/interfaces/IERC3156FlashLender.sol delete mode 100644 certora/munged/interfaces/IERC721.sol delete mode 100644 certora/munged/interfaces/IERC721Enumerable.sol delete mode 100644 certora/munged/interfaces/IERC721Metadata.sol delete mode 100644 certora/munged/interfaces/IERC721Receiver.sol delete mode 100644 certora/munged/interfaces/IERC777.sol delete mode 100644 certora/munged/interfaces/IERC777Recipient.sol delete mode 100644 certora/munged/interfaces/IERC777Sender.sol delete mode 100644 certora/munged/interfaces/README.adoc delete mode 100644 certora/munged/interfaces/draft-IERC2612.sol delete mode 100644 certora/munged/metatx/ERC2771Context.sol delete mode 100644 certora/munged/metatx/MinimalForwarder.sol delete mode 100644 certora/munged/metatx/README.adoc delete mode 100644 certora/munged/mocks/AccessControlEnumerableMock.sol delete mode 100644 certora/munged/mocks/AccessControlMock.sol delete mode 100644 certora/munged/mocks/AddressImpl.sol delete mode 100644 certora/munged/mocks/ArraysImpl.sol delete mode 100644 certora/munged/mocks/BadBeacon.sol delete mode 100644 certora/munged/mocks/BitmapMock.sol delete mode 100644 certora/munged/mocks/CallReceiverMock.sol delete mode 100644 certora/munged/mocks/ClashingImplementation.sol delete mode 100644 certora/munged/mocks/ClonesMock.sol delete mode 100644 certora/munged/mocks/ConditionalEscrowMock.sol delete mode 100644 certora/munged/mocks/ContextMock.sol delete mode 100644 certora/munged/mocks/CountersImpl.sol delete mode 100644 certora/munged/mocks/Create2Impl.sol delete mode 100644 certora/munged/mocks/DummyImplementation.sol delete mode 100644 certora/munged/mocks/ECDSAMock.sol delete mode 100644 certora/munged/mocks/EIP712External.sol delete mode 100644 certora/munged/mocks/ERC1155BurnableMock.sol delete mode 100644 certora/munged/mocks/ERC1155Mock.sol delete mode 100644 certora/munged/mocks/ERC1155PausableMock.sol delete mode 100644 certora/munged/mocks/ERC1155ReceiverMock.sol delete mode 100644 certora/munged/mocks/ERC1155SupplyMock.sol delete mode 100644 certora/munged/mocks/ERC1271WalletMock.sol delete mode 100644 certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol delete mode 100644 certora/munged/mocks/ERC165/ERC165MissingData.sol delete mode 100644 certora/munged/mocks/ERC165/ERC165NotSupported.sol delete mode 100644 certora/munged/mocks/ERC165CheckerMock.sol delete mode 100644 certora/munged/mocks/ERC165Mock.sol delete mode 100644 certora/munged/mocks/ERC165StorageMock.sol delete mode 100644 certora/munged/mocks/ERC1820ImplementerMock.sol delete mode 100644 certora/munged/mocks/ERC20BurnableMock.sol delete mode 100644 certora/munged/mocks/ERC20CappedMock.sol delete mode 100644 certora/munged/mocks/ERC20DecimalsMock.sol delete mode 100644 certora/munged/mocks/ERC20FlashMintMock.sol delete mode 100644 certora/munged/mocks/ERC20Mock.sol delete mode 100644 certora/munged/mocks/ERC20PausableMock.sol delete mode 100644 certora/munged/mocks/ERC20PermitMock.sol delete mode 100644 certora/munged/mocks/ERC20SnapshotMock.sol delete mode 100644 certora/munged/mocks/ERC20VotesCompMock.sol delete mode 100644 certora/munged/mocks/ERC20VotesMock.sol delete mode 100644 certora/munged/mocks/ERC20WrapperMock.sol delete mode 100644 certora/munged/mocks/ERC2771ContextMock.sol delete mode 100644 certora/munged/mocks/ERC3156FlashBorrowerMock.sol delete mode 100644 certora/munged/mocks/ERC721BurnableMock.sol delete mode 100644 certora/munged/mocks/ERC721EnumerableMock.sol delete mode 100644 certora/munged/mocks/ERC721Mock.sol delete mode 100644 certora/munged/mocks/ERC721PausableMock.sol delete mode 100644 certora/munged/mocks/ERC721ReceiverMock.sol delete mode 100644 certora/munged/mocks/ERC721URIStorageMock.sol delete mode 100644 certora/munged/mocks/ERC777Mock.sol delete mode 100644 certora/munged/mocks/ERC777SenderRecipientMock.sol delete mode 100644 certora/munged/mocks/EnumerableMapMock.sol delete mode 100644 certora/munged/mocks/EnumerableSetMock.sol delete mode 100644 certora/munged/mocks/EtherReceiverMock.sol delete mode 100644 certora/munged/mocks/GovernorCompMock.sol delete mode 100644 certora/munged/mocks/GovernorCompatibilityBravoMock.sol delete mode 100644 certora/munged/mocks/GovernorMock.sol delete mode 100644 certora/munged/mocks/GovernorTimelockCompoundMock.sol delete mode 100644 certora/munged/mocks/GovernorTimelockControlMock.sol delete mode 100644 certora/munged/mocks/InitializableMock.sol delete mode 100644 certora/munged/mocks/MathMock.sol delete mode 100644 certora/munged/mocks/MerkleProofWrapper.sol delete mode 100644 certora/munged/mocks/MulticallTest.sol delete mode 100644 certora/munged/mocks/MulticallTokenMock.sol delete mode 100644 certora/munged/mocks/MultipleInheritanceInitializableMocks.sol delete mode 100644 certora/munged/mocks/OwnableMock.sol delete mode 100644 certora/munged/mocks/PausableMock.sol delete mode 100644 certora/munged/mocks/PullPaymentMock.sol delete mode 100644 certora/munged/mocks/ReentrancyAttack.sol delete mode 100644 certora/munged/mocks/ReentrancyMock.sol delete mode 100644 certora/munged/mocks/RegressionImplementation.sol delete mode 100644 certora/munged/mocks/SafeCastMock.sol delete mode 100644 certora/munged/mocks/SafeERC20Helper.sol delete mode 100644 certora/munged/mocks/SafeMathMock.sol delete mode 100644 certora/munged/mocks/SignatureCheckerMock.sol delete mode 100644 certora/munged/mocks/SignedSafeMathMock.sol delete mode 100644 certora/munged/mocks/SingleInheritanceInitializableMocks.sol delete mode 100644 certora/munged/mocks/StorageSlotMock.sol delete mode 100644 certora/munged/mocks/StringsMock.sol delete mode 100644 certora/munged/mocks/TimersBlockNumberImpl.sol delete mode 100644 certora/munged/mocks/TimersTimestampImpl.sol delete mode 100644 certora/munged/mocks/UUPS/TestInProd.sol delete mode 100644 certora/munged/mocks/compound/CompTimelock.sol delete mode 100644 certora/munged/mocks/wizard/MyGovernor1.sol delete mode 100644 certora/munged/mocks/wizard/MyGovernor2.sol delete mode 100644 certora/munged/mocks/wizard/MyGovernor3.sol delete mode 100644 certora/munged/package.json delete mode 100644 certora/munged/proxy/Clones.sol delete mode 100644 certora/munged/proxy/ERC1967/ERC1967Proxy.sol delete mode 100644 certora/munged/proxy/ERC1967/ERC1967Upgrade.sol delete mode 100644 certora/munged/proxy/Proxy.sol delete mode 100644 certora/munged/proxy/README.adoc delete mode 100644 certora/munged/proxy/beacon/BeaconProxy.sol delete mode 100644 certora/munged/proxy/beacon/IBeacon.sol delete mode 100644 certora/munged/proxy/beacon/UpgradeableBeacon.sol delete mode 100644 certora/munged/proxy/transparent/ProxyAdmin.sol delete mode 100644 certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol delete mode 100644 certora/munged/proxy/utils/Initializable.sol delete mode 100644 certora/munged/proxy/utils/UUPSUpgradeable.sol delete mode 100644 certora/munged/security/Pausable.sol delete mode 100644 certora/munged/security/PullPayment.sol delete mode 100644 certora/munged/security/README.adoc delete mode 100644 certora/munged/security/ReentrancyGuard.sol delete mode 100644 certora/munged/token/ERC1155/ERC1155.sol delete mode 100644 certora/munged/token/ERC1155/IERC1155.sol delete mode 100644 certora/munged/token/ERC1155/IERC1155Receiver.sol delete mode 100644 certora/munged/token/ERC1155/README.adoc delete mode 100644 certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol delete mode 100644 certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol delete mode 100644 certora/munged/token/ERC1155/extensions/ERC1155Supply.sol delete mode 100644 certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol delete mode 100644 certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol delete mode 100644 certora/munged/token/ERC1155/utils/ERC1155Holder.sol delete mode 100644 certora/munged/token/ERC1155/utils/ERC1155Receiver.sol delete mode 100644 certora/munged/token/ERC20/ERC20.sol delete mode 100644 certora/munged/token/ERC20/IERC20.sol delete mode 100644 certora/munged/token/ERC20/README.adoc delete mode 100644 certora/munged/token/ERC20/extensions/ERC20Burnable.sol delete mode 100644 certora/munged/token/ERC20/extensions/ERC20Capped.sol delete mode 100644 certora/munged/token/ERC20/extensions/ERC20FlashMint.sol delete mode 100644 certora/munged/token/ERC20/extensions/ERC20Pausable.sol delete mode 100644 certora/munged/token/ERC20/extensions/ERC20Snapshot.sol delete mode 100644 certora/munged/token/ERC20/extensions/ERC20Votes.sol delete mode 100644 certora/munged/token/ERC20/extensions/ERC20VotesComp.sol delete mode 100644 certora/munged/token/ERC20/extensions/ERC20Wrapper.sol delete mode 100644 certora/munged/token/ERC20/extensions/IERC20Metadata.sol delete mode 100644 certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol delete mode 100644 certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol delete mode 100644 certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol delete mode 100644 certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol delete mode 100644 certora/munged/token/ERC20/utils/SafeERC20.sol delete mode 100644 certora/munged/token/ERC20/utils/TokenTimelock.sol delete mode 100644 certora/munged/token/ERC721/ERC721.sol delete mode 100644 certora/munged/token/ERC721/IERC721.sol delete mode 100644 certora/munged/token/ERC721/IERC721Receiver.sol delete mode 100644 certora/munged/token/ERC721/README.adoc delete mode 100644 certora/munged/token/ERC721/extensions/ERC721Burnable.sol delete mode 100644 certora/munged/token/ERC721/extensions/ERC721Enumerable.sol delete mode 100644 certora/munged/token/ERC721/extensions/ERC721Pausable.sol delete mode 100644 certora/munged/token/ERC721/extensions/ERC721URIStorage.sol delete mode 100644 certora/munged/token/ERC721/extensions/IERC721Enumerable.sol delete mode 100644 certora/munged/token/ERC721/extensions/IERC721Metadata.sol delete mode 100644 certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol delete mode 100644 certora/munged/token/ERC721/utils/ERC721Holder.sol delete mode 100644 certora/munged/token/ERC777/ERC777.sol delete mode 100644 certora/munged/token/ERC777/IERC777.sol delete mode 100644 certora/munged/token/ERC777/IERC777Recipient.sol delete mode 100644 certora/munged/token/ERC777/IERC777Sender.sol delete mode 100644 certora/munged/token/ERC777/README.adoc delete mode 100644 certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol delete mode 100644 certora/munged/utils/Address.sol delete mode 100644 certora/munged/utils/Arrays.sol delete mode 100644 certora/munged/utils/Context.sol delete mode 100644 certora/munged/utils/Counters.sol delete mode 100644 certora/munged/utils/Create2.sol delete mode 100644 certora/munged/utils/Multicall.sol delete mode 100644 certora/munged/utils/README.adoc delete mode 100644 certora/munged/utils/StorageSlot.sol delete mode 100644 certora/munged/utils/Strings.sol delete mode 100644 certora/munged/utils/Timers.sol delete mode 100644 certora/munged/utils/cryptography/ECDSA.sol delete mode 100644 certora/munged/utils/cryptography/MerkleProof.sol delete mode 100644 certora/munged/utils/cryptography/SignatureChecker.sol delete mode 100644 certora/munged/utils/cryptography/draft-EIP712.sol delete mode 100644 certora/munged/utils/escrow/ConditionalEscrow.sol delete mode 100644 certora/munged/utils/escrow/Escrow.sol delete mode 100644 certora/munged/utils/escrow/RefundEscrow.sol delete mode 100644 certora/munged/utils/introspection/ERC165.sol delete mode 100644 certora/munged/utils/introspection/ERC165Checker.sol delete mode 100644 certora/munged/utils/introspection/ERC165Storage.sol delete mode 100644 certora/munged/utils/introspection/ERC1820Implementer.sol delete mode 100644 certora/munged/utils/introspection/IERC165.sol delete mode 100644 certora/munged/utils/introspection/IERC1820Implementer.sol delete mode 100644 certora/munged/utils/introspection/IERC1820Registry.sol delete mode 100644 certora/munged/utils/math/Math.sol delete mode 100644 certora/munged/utils/math/SafeCast.sol delete mode 100644 certora/munged/utils/math/SafeMath.sol delete mode 100644 certora/munged/utils/math/SignedSafeMath.sol delete mode 100644 certora/munged/utils/structs/BitMaps.sol delete mode 100644 certora/munged/utils/structs/EnumerableMap.sol delete mode 100644 certora/munged/utils/structs/EnumerableSet.sol diff --git a/certora/Makefile b/certora/Makefile new file mode 100644 index 000000000..bbbddbcab --- /dev/null +++ b/certora/Makefile @@ -0,0 +1,24 @@ +default: help + +PATCH = applyHarness.patch +CONTRACTS_DIR = ../contracts +MUNGED_DIR = munged + +help: + @echo "usage:" + @echo " make clean: remove all generated files (those ignored by git)" + @echo " make $(MUNGED_DIR): create $(MUNGED_DIR) directory by applying the patch file to $(CONTRACTS_DIR)" + @echo " make record: record a new patch file capturing the differences between $(CONTRACTS_DIR) and $(MUNGED_DIR)" + +munged: $(wildcard $(CONTRACTS_DIR)/*.sol) $(PATCH) + rm -rf $@ + cp -r $(CONTRACTS_DIR) $@ + patch -p0 -d $@ < $(PATCH) + +record: + diff -ruN $(CONTRACTS_DIR) $(MUNGED_DIR) | sed 's+../contracts/++g' | sed 's+munged/++g' > $(PATCH) + +clean: + git clean -fdX + touch $(PATCH) + diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch new file mode 100644 index 000000000..317f22e73 --- /dev/null +++ b/certora/applyHarness.patch @@ -0,0 +1,121 @@ +diff -ruN .gitignore .gitignore +--- .gitignore 1969-12-31 19:00:00.000000000 -0500 ++++ .gitignore 2021-12-01 10:05:19.757088138 -0500 +@@ -0,0 +1,2 @@ ++* ++!.gitignore +diff -ruN governance/compatibility/GovernorCompatibilityBravo.sol governance/compatibility/GovernorCompatibilityBravo.sol +--- governance/compatibility/GovernorCompatibilityBravo.sol 2021-12-01 10:02:39.909936316 -0500 ++++ governance/compatibility/GovernorCompatibilityBravo.sol 2021-12-01 10:00:48.002627620 -0500 +@@ -245,7 +245,7 @@ + /** + * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. + */ +- function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { ++ function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal + ProposalDetails storage details = _proposalDetails[proposalId]; + return quorum(proposalSnapshot(proposalId)) < details.forVotes; + } +@@ -253,7 +253,7 @@ + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be scritly over the againstVotes. + */ +- function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { ++ function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal + ProposalDetails storage details = _proposalDetails[proposalId]; + return details.forVotes > details.againstVotes; + } +diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol +--- governance/extensions/GovernorCountingSimple.sol 2021-12-01 10:02:39.909936316 -0500 ++++ governance/extensions/GovernorCountingSimple.sol 2021-12-01 10:00:48.002627620 -0500 +@@ -64,7 +64,7 @@ + /** + * @dev See {Governor-_quorumReached}. + */ +- function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { ++ function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + + return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes; +@@ -73,7 +73,7 @@ + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. + */ +- function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { ++ function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + + return proposalvote.forVotes > proposalvote.againstVotes; +diff -ruN governance/extensions/GovernorTimelockControl.sol governance/extensions/GovernorTimelockControl.sol +--- governance/extensions/GovernorTimelockControl.sol 2021-12-01 10:02:39.909936316 -0500 ++++ governance/extensions/GovernorTimelockControl.sol 2021-12-01 10:00:48.002627620 -0500 +@@ -109,7 +109,7 @@ + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override { +- _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); ++ _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); + } + + /** +diff -ruN governance/Governor.sol governance/Governor.sol +--- governance/Governor.sol 2021-12-01 10:02:39.909936316 -0500 ++++ governance/Governor.sol 2021-12-01 10:00:48.002627620 -0500 +@@ -38,8 +38,8 @@ + + string private _name; + +- mapping(uint256 => ProposalCore) private _proposals; +- ++ mapping(uint256 => ProposalCore) public _proposals; ++ + /** + * @dev Restrict access to governor executing address. Some module might override the _executor function to make + * sure this modifier is consistant with the execution model. +@@ -154,12 +154,12 @@ + /** + * @dev Amount of votes already cast passes the threshold limit. + */ +- function _quorumReached(uint256 proposalId) internal view virtual returns (bool); ++ function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal + + /** + * @dev Is the proposal successful or not. + */ +- function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool); ++ function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal + + /** + * @dev Register a vote with a given support and voting weight. +@@ -320,7 +320,7 @@ + v, + r, + s +- ); ++ ); // mention that we assume that hashing works correctly + return _castVote(proposalId, voter, support, ""); + } + +diff -ruN governance/TimelockController.sol governance/TimelockController.sol +--- governance/TimelockController.sol 2021-12-01 10:02:39.909936316 -0500 ++++ governance/TimelockController.sol 2021-12-01 10:00:48.002627620 -0500 +@@ -299,6 +299,7 @@ + _call(id, i, targets[i], values[i], datas[i]); + } + _afterCall(id); ++ // ASSUME THAT THERE IS NO REENTRANCY IN WIZARDHARNESS1 + } + + /** +diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol +--- token/ERC20/extensions/ERC20Votes.sol 2021-12-01 10:02:39.909936316 -0500 ++++ token/ERC20/extensions/ERC20Votes.sol 2021-12-01 10:00:48.018627515 -0500 +@@ -84,7 +84,7 @@ + * + * - `blockNumber` must have been already mined + */ +- function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) { ++ function getPastVotes(address account, uint256 blockNumber) public view virtual returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_checkpoints[account], blockNumber); + } diff --git a/certora/munged/.gitignore b/certora/munged/.gitignore new file mode 100644 index 000000000..d6b7ef32c --- /dev/null +++ b/certora/munged/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/certora/munged/access/AccessControl.sol b/certora/munged/access/AccessControl.sol deleted file mode 100644 index dae9f7077..000000000 --- a/certora/munged/access/AccessControl.sol +++ /dev/null @@ -1,223 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (access/AccessControl.sol) - -pragma solidity ^0.8.0; - -import "./IAccessControl.sol"; -import "../utils/Context.sol"; -import "../utils/Strings.sol"; -import "../utils/introspection/ERC165.sol"; - -/** - * @dev Contract module that allows children to implement role-based access - * control mechanisms. This is a lightweight version that doesn't allow enumerating role - * members except through off-chain means by accessing the contract event logs. Some - * applications may benefit from on-chain enumerability, for those cases see - * {AccessControlEnumerable}. - * - * Roles are referred to by their `bytes32` identifier. These should be exposed - * in the external API and be unique. The best way to achieve this is by - * using `public constant` hash digests: - * - * ``` - * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); - * ``` - * - * Roles can be used to represent a set of permissions. To restrict access to a - * function call, use {hasRole}: - * - * ``` - * function foo() public { - * require(hasRole(MY_ROLE, msg.sender)); - * ... - * } - * ``` - * - * Roles can be granted and revoked dynamically via the {grantRole} and - * {revokeRole} functions. Each role has an associated admin role, and only - * accounts that have a role's admin role can call {grantRole} and {revokeRole}. - * - * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means - * that only accounts with this role will be able to grant or revoke other - * roles. More complex role relationships can be created by using - * {_setRoleAdmin}. - * - * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to - * grant and revoke this role. Extra precautions should be taken to secure - * accounts that have been granted it. - */ -abstract contract AccessControl is Context, IAccessControl, ERC165 { - struct RoleData { - mapping(address => bool) members; - bytes32 adminRole; - } - - mapping(bytes32 => RoleData) private _roles; - - bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; - - /** - * @dev Modifier that checks that an account has a specific role. Reverts - * with a standardized message including the required role. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - * - * _Available since v4.1._ - */ - modifier onlyRole(bytes32 role) { - _checkRole(role, _msgSender()); - _; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) public view override returns (bool) { - return _roles[role].members[account]; - } - - /** - * @dev Revert with a standard message if `account` is missing `role`. - * - * The format of the revert reason is given by the following regular expression: - * - * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ - */ - function _checkRole(bytes32 role, address account) internal view { - if (!hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(account), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - } - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) public view override returns (bytes32) { - return _roles[role].adminRole; - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _grantRole(role, account); - } - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { - _revokeRole(role, account); - } - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been revoked `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) public virtual override { - require(account == _msgSender(), "AccessControl: can only renounce roles for self"); - - _revokeRole(role, account); - } - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. Note that unlike {grantRole}, this function doesn't perform any - * checks on the calling account. - * - * [WARNING] - * ==== - * This function should only be called from the constructor when setting - * up the initial roles for the system. - * - * Using this function in any other way is effectively circumventing the admin - * system imposed by {AccessControl}. - * ==== - * - * NOTE: This function is deprecated in favor of {_grantRole}. - */ - function _setupRole(bytes32 role, address account) internal virtual { - _grantRole(role, account); - } - - /** - * @dev Sets `adminRole` as ``role``'s admin role. - * - * Emits a {RoleAdminChanged} event. - */ - function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { - bytes32 previousAdminRole = getRoleAdmin(role); - _roles[role].adminRole = adminRole; - emit RoleAdminChanged(role, previousAdminRole, adminRole); - } - - /** - * @dev Grants `role` to `account`. - * - * Internal function without access restriction. - */ - function _grantRole(bytes32 role, address account) internal virtual { - if (!hasRole(role, account)) { - _roles[role].members[account] = true; - emit RoleGranted(role, account, _msgSender()); - } - } - - /** - * @dev Revokes `role` from `account`. - * - * Internal function without access restriction. - */ - function _revokeRole(bytes32 role, address account) internal virtual { - if (hasRole(role, account)) { - _roles[role].members[account] = false; - emit RoleRevoked(role, account, _msgSender()); - } - } -} diff --git a/certora/munged/access/AccessControlEnumerable.sol b/certora/munged/access/AccessControlEnumerable.sol deleted file mode 100644 index 655195d9a..000000000 --- a/certora/munged/access/AccessControlEnumerable.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (access/AccessControlEnumerable.sol) - -pragma solidity ^0.8.0; - -import "./IAccessControlEnumerable.sol"; -import "./AccessControl.sol"; -import "../utils/structs/EnumerableSet.sol"; - -/** - * @dev Extension of {AccessControl} that allows enumerating the members of each role. - */ -abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { - using EnumerableSet for EnumerableSet.AddressSet; - - mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) public view override returns (address) { - return _roleMembers[role].at(index); - } - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) public view override returns (uint256) { - return _roleMembers[role].length(); - } - - /** - * @dev Overload {_grantRole} to track enumerable memberships - */ - function _grantRole(bytes32 role, address account) internal virtual override { - super._grantRole(role, account); - _roleMembers[role].add(account); - } - - /** - * @dev Overload {_revokeRole} to track enumerable memberships - */ - function _revokeRole(bytes32 role, address account) internal virtual override { - super._revokeRole(role, account); - _roleMembers[role].remove(account); - } -} diff --git a/certora/munged/access/IAccessControl.sol b/certora/munged/access/IAccessControl.sol deleted file mode 100644 index ac606aabc..000000000 --- a/certora/munged/access/IAccessControl.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (access/IAccessControl.sol) - -pragma solidity ^0.8.0; - -/** - * @dev External interface of AccessControl declared to support ERC165 detection. - */ -interface IAccessControl { - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - /** - * @dev Returns `true` if `account` has been granted `role`. - */ - function hasRole(bytes32 role, address account) external view returns (bool); - - /** - * @dev Returns the admin role that controls `role`. See {grantRole} and - * {revokeRole}. - * - * To change a role's admin, use {AccessControl-_setRoleAdmin}. - */ - function getRoleAdmin(bytes32 role) external view returns (bytes32); - - /** - * @dev Grants `role` to `account`. - * - * If `account` had not been already granted `role`, emits a {RoleGranted} - * event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function grantRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from `account`. - * - * If `account` had been granted `role`, emits a {RoleRevoked} event. - * - * Requirements: - * - * - the caller must have ``role``'s admin role. - */ - function revokeRole(bytes32 role, address account) external; - - /** - * @dev Revokes `role` from the calling account. - * - * Roles are often managed via {grantRole} and {revokeRole}: this function's - * purpose is to provide a mechanism for accounts to lose their privileges - * if they are compromised (such as when a trusted device is misplaced). - * - * If the calling account had been granted `role`, emits a {RoleRevoked} - * event. - * - * Requirements: - * - * - the caller must be `account`. - */ - function renounceRole(bytes32 role, address account) external; -} diff --git a/certora/munged/access/IAccessControlEnumerable.sol b/certora/munged/access/IAccessControlEnumerable.sol deleted file mode 100644 index 3af4d10af..000000000 --- a/certora/munged/access/IAccessControlEnumerable.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (access/IAccessControlEnumerable.sol) - -pragma solidity ^0.8.0; - -import "./IAccessControl.sol"; - -/** - * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. - */ -interface IAccessControlEnumerable is IAccessControl { - /** - * @dev Returns one of the accounts that have `role`. `index` must be a - * value between 0 and {getRoleMemberCount}, non-inclusive. - * - * Role bearers are not sorted in any particular way, and their ordering may - * change at any point. - * - * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure - * you perform all queries on the same block. See the following - * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] - * for more information. - */ - function getRoleMember(bytes32 role, uint256 index) external view returns (address); - - /** - * @dev Returns the number of accounts that have `role`. Can be used - * together with {getRoleMember} to enumerate all bearers of a role. - */ - function getRoleMemberCount(bytes32 role) external view returns (uint256); -} diff --git a/certora/munged/access/Ownable.sol b/certora/munged/access/Ownable.sol deleted file mode 100644 index 2ea238caa..000000000 --- a/certora/munged/access/Ownable.sol +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (access/Ownable.sol) - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * By default, the owner account will be the one that deploys the contract. This - * can later be changed with {transferOwnership}. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -abstract contract Ownable is Context { - address private _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the deployer as the initial owner. - */ - constructor() { - _transferOwnership(_msgSender()); - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view virtual returns (address) { - return _owner; - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - require(owner() == _msgSender(), "Ownable: caller is not the owner"); - _; - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions anymore. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public virtual onlyOwner { - _transferOwnership(address(0)); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public virtual onlyOwner { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - _transferOwnership(newOwner); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Internal function without access restriction. - */ - function _transferOwnership(address newOwner) internal virtual { - address oldOwner = _owner; - _owner = newOwner; - emit OwnershipTransferred(oldOwner, newOwner); - } -} diff --git a/certora/munged/access/README.adoc b/certora/munged/access/README.adoc deleted file mode 100644 index 2e84c09ad..000000000 --- a/certora/munged/access/README.adoc +++ /dev/null @@ -1,21 +0,0 @@ -= Access Control - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/access - -This directory provides ways to restrict who can access the functions of a contract or when they can do it. - -- {AccessControl} provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts. -- {Ownable} is a simpler mechanism with a single owner "role" that can be assigned to a single account. This simpler mechanism can be useful for quick tests but projects with production concerns are likely to outgrow it. - -== Authorization - -{{Ownable}} - -{{IAccessControl}} - -{{AccessControl}} - -{{IAccessControlEnumerable}} - -{{AccessControlEnumerable}} diff --git a/certora/munged/finance/PaymentSplitter.sol b/certora/munged/finance/PaymentSplitter.sol deleted file mode 100644 index df9345d77..000000000 --- a/certora/munged/finance/PaymentSplitter.sol +++ /dev/null @@ -1,189 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (finance/PaymentSplitter.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC20/utils/SafeERC20.sol"; -import "../utils/Address.sol"; -import "../utils/Context.sol"; - -/** - * @title PaymentSplitter - * @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware - * that the Ether will be split in this way, since it is handled transparently by the contract. - * - * The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each - * account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim - * an amount proportional to the percentage of total shares they were assigned. - * - * `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the - * accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release} - * function. - * - * NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and - * tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you - * to run tests before sending real value to this contract. - */ -contract PaymentSplitter is Context { - event PayeeAdded(address account, uint256 shares); - event PaymentReleased(address to, uint256 amount); - event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount); - event PaymentReceived(address from, uint256 amount); - - uint256 private _totalShares; - uint256 private _totalReleased; - - mapping(address => uint256) private _shares; - mapping(address => uint256) private _released; - address[] private _payees; - - mapping(IERC20 => uint256) private _erc20TotalReleased; - mapping(IERC20 => mapping(address => uint256)) private _erc20Released; - - /** - * @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at - * the matching position in the `shares` array. - * - * All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no - * duplicates in `payees`. - */ - constructor(address[] memory payees, uint256[] memory shares_) payable { - require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch"); - require(payees.length > 0, "PaymentSplitter: no payees"); - - for (uint256 i = 0; i < payees.length; i++) { - _addPayee(payees[i], shares_[i]); - } - } - - /** - * @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully - * reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the - * reliability of the events, and not the actual splitting of Ether. - * - * To learn more about this see the Solidity documentation for - * https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback - * functions]. - */ - receive() external payable virtual { - emit PaymentReceived(_msgSender(), msg.value); - } - - /** - * @dev Getter for the total shares held by payees. - */ - function totalShares() public view returns (uint256) { - return _totalShares; - } - - /** - * @dev Getter for the total amount of Ether already released. - */ - function totalReleased() public view returns (uint256) { - return _totalReleased; - } - - /** - * @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20 - * contract. - */ - function totalReleased(IERC20 token) public view returns (uint256) { - return _erc20TotalReleased[token]; - } - - /** - * @dev Getter for the amount of shares held by an account. - */ - function shares(address account) public view returns (uint256) { - return _shares[account]; - } - - /** - * @dev Getter for the amount of Ether already released to a payee. - */ - function released(address account) public view returns (uint256) { - return _released[account]; - } - - /** - * @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an - * IERC20 contract. - */ - function released(IERC20 token, address account) public view returns (uint256) { - return _erc20Released[token][account]; - } - - /** - * @dev Getter for the address of the payee number `index`. - */ - function payee(uint256 index) public view returns (address) { - return _payees[index]; - } - - /** - * @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the - * total shares and their previous withdrawals. - */ - function release(address payable account) public virtual { - require(_shares[account] > 0, "PaymentSplitter: account has no shares"); - - uint256 totalReceived = address(this).balance + totalReleased(); - uint256 payment = _pendingPayment(account, totalReceived, released(account)); - - require(payment != 0, "PaymentSplitter: account is not due payment"); - - _released[account] += payment; - _totalReleased += payment; - - Address.sendValue(account, payment); - emit PaymentReleased(account, payment); - } - - /** - * @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their - * percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20 - * contract. - */ - function release(IERC20 token, address account) public virtual { - require(_shares[account] > 0, "PaymentSplitter: account has no shares"); - - uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token); - uint256 payment = _pendingPayment(account, totalReceived, released(token, account)); - - require(payment != 0, "PaymentSplitter: account is not due payment"); - - _erc20Released[token][account] += payment; - _erc20TotalReleased[token] += payment; - - SafeERC20.safeTransfer(token, account, payment); - emit ERC20PaymentReleased(token, account, payment); - } - - /** - * @dev internal logic for computing the pending payment of an `account` given the token historical balances and - * already released amounts. - */ - function _pendingPayment( - address account, - uint256 totalReceived, - uint256 alreadyReleased - ) private view returns (uint256) { - return (totalReceived * _shares[account]) / _totalShares - alreadyReleased; - } - - /** - * @dev Add a new payee to the contract. - * @param account The address of the payee to add. - * @param shares_ The number of shares owned by the payee. - */ - function _addPayee(address account, uint256 shares_) private { - require(account != address(0), "PaymentSplitter: account is the zero address"); - require(shares_ > 0, "PaymentSplitter: shares are 0"); - require(_shares[account] == 0, "PaymentSplitter: account already has shares"); - - _payees.push(account); - _shares[account] = shares_; - _totalShares = _totalShares + shares_; - emit PayeeAdded(account, shares_); - } -} diff --git a/certora/munged/finance/README.adoc b/certora/munged/finance/README.adoc deleted file mode 100644 index b64af3125..000000000 --- a/certora/munged/finance/README.adoc +++ /dev/null @@ -1,20 +0,0 @@ -= Finance - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/finance - -This directory includes primitives for financial systems: - -- {PaymentSplitter} allows to split Ether and ERC20 payments among a group of accounts. The sender does not need to be - aware that the assets will be split in this way, since it is handled transparently by the contract. The split can be - in equal parts or in any other arbitrary proportion. - -- {VestingWallet} handles the vesting of Ether and ERC20 tokens for a given beneficiary. Custody of multiple tokens can - be given to this contract, which will release the token to the beneficiary following a given, customizable, vesting - schedule. - -== Contracts - -{{PaymentSplitter}} - -{{VestingWallet}} diff --git a/certora/munged/finance/VestingWallet.sol b/certora/munged/finance/VestingWallet.sol deleted file mode 100644 index f0513bdc6..000000000 --- a/certora/munged/finance/VestingWallet.sol +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (finance/VestingWallet.sol) -pragma solidity ^0.8.0; - -import "../token/ERC20/utils/SafeERC20.sol"; -import "../utils/Address.sol"; -import "../utils/Context.sol"; -import "../utils/math/Math.sol"; - -/** - * @title VestingWallet - * @dev This contract handles the vesting of Eth and ERC20 tokens for a given beneficiary. Custody of multiple tokens - * can be given to this contract, which will release the token to the beneficiary following a given vesting schedule. - * The vesting schedule is customizable through the {vestedAmount} function. - * - * Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning. - * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly) - * be immediately releasable. - */ -contract VestingWallet is Context { - event EtherReleased(uint256 amount); - event ERC20Released(address token, uint256 amount); - - uint256 private _released; - mapping(address => uint256) private _erc20Released; - address private immutable _beneficiary; - uint64 private immutable _start; - uint64 private immutable _duration; - - /** - * @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet. - */ - constructor( - address beneficiaryAddress, - uint64 startTimestamp, - uint64 durationSeconds - ) { - require(beneficiaryAddress != address(0), "VestingWallet: beneficiary is zero address"); - _beneficiary = beneficiaryAddress; - _start = startTimestamp; - _duration = durationSeconds; - } - - /** - * @dev The contract should be able to receive Eth. - */ - receive() external payable virtual {} - - /** - * @dev Getter for the beneficiary address. - */ - function beneficiary() public view virtual returns (address) { - return _beneficiary; - } - - /** - * @dev Getter for the start timestamp. - */ - function start() public view virtual returns (uint256) { - return _start; - } - - /** - * @dev Getter for the vesting duration. - */ - function duration() public view virtual returns (uint256) { - return _duration; - } - - /** - * @dev Amount of eth already released - */ - function released() public view virtual returns (uint256) { - return _released; - } - - /** - * @dev Amount of token already released - */ - function released(address token) public view virtual returns (uint256) { - return _erc20Released[token]; - } - - /** - * @dev Release the native token (ether) that have already vested. - * - * Emits a {TokensReleased} event. - */ - function release() public virtual { - uint256 releasable = vestedAmount(uint64(block.timestamp)) - released(); - _released += releasable; - emit EtherReleased(releasable); - Address.sendValue(payable(beneficiary()), releasable); - } - - /** - * @dev Release the tokens that have already vested. - * - * Emits a {TokensReleased} event. - */ - function release(address token) public virtual { - uint256 releasable = vestedAmount(token, uint64(block.timestamp)) - released(token); - _erc20Released[token] += releasable; - emit ERC20Released(token, releasable); - SafeERC20.safeTransfer(IERC20(token), beneficiary(), releasable); - } - - /** - * @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve. - */ - function vestedAmount(uint64 timestamp) public view virtual returns (uint256) { - return _vestingSchedule(address(this).balance + released(), timestamp); - } - - /** - * @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve. - */ - function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) { - return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp); - } - - /** - * @dev Virtual implementation of the vesting formula. This returns the amout vested, as a function of time, for - * an asset given its total historical allocation. - */ - function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) { - if (timestamp < start()) { - return 0; - } else if (timestamp > start() + duration()) { - return totalAllocation; - } else { - return (totalAllocation * (timestamp - start())) / duration(); - } - } -} diff --git a/certora/munged/governance/Governor.sol b/certora/munged/governance/Governor.sol deleted file mode 100644 index e8d369452..000000000 --- a/certora/munged/governance/Governor.sol +++ /dev/null @@ -1,357 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/Governor.sol) - -pragma solidity ^0.8.0; - -import "../utils/cryptography/ECDSA.sol"; -import "../utils/cryptography/draft-EIP712.sol"; -import "../utils/introspection/ERC165.sol"; -import "../utils/math/SafeCast.sol"; -import "../utils/Address.sol"; -import "../utils/Context.sol"; -import "../utils/Timers.sol"; -import "./IGovernor.sol"; - -/** - * @dev Core of the governance system, designed to be extended though various modules. - * - * This contract is abstract and requires several function to be implemented in various modules: - * - * - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote} - * - A voting module must implement {getVotes} - * - Additionanly, the {votingPeriod} must also be implemented - * - * _Available since v4.3._ - */ -abstract contract Governor is Context, ERC165, EIP712, IGovernor { - using SafeCast for uint256; - using Timers for Timers.BlockNumber; - - bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); - - struct ProposalCore { - Timers.BlockNumber voteStart; - Timers.BlockNumber voteEnd; - bool executed; - bool canceled; - } - - string private _name; - - mapping(uint256 => ProposalCore) public _proposals; - - /** - * @dev Restrict access to governor executing address. Some module might override the _executor function to make - * sure this modifier is consistant with the execution model. - */ - modifier onlyGovernance() { - require(_msgSender() == _executor(), "Governor: onlyGovernance"); - _; - } - - /** - * @dev Sets the value for {name} and {version} - */ - constructor(string memory name_) EIP712(name_, version()) { - _name = name_; - } - - /** - * @dev Function to receive ETH that will be handled by the governor (disabled if executor is a third party contract) - */ - receive() external payable virtual { - require(_executor() == address(this)); - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { - return interfaceId == type(IGovernor).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev See {IGovernor-name}. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev See {IGovernor-version}. - */ - function version() public view virtual override returns (string memory) { - return "1"; - } - - /** - * @dev See {IGovernor-hashProposal}. - * - * The proposal id is produced by hashing the RLC encoded `targets` array, the `values` array, the `calldatas` array - * and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id - * can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in - * advance, before the proposal is submitted. - * - * Note that the chainId and the governor address are not part of the proposal id computation. Consequently, the - * same proposal (with same operation and same description) will have the same id if submitted on multiple governors - * accross multiple networks. This also means that in order to execute the same operation twice (on the same - * governor) the proposer will have to change the description in order to avoid proposal id conflicts. - */ - function hashProposal( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public pure virtual override returns (uint256) { - return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash))); - } - - /** - * @dev See {IGovernor-state}. - */ - function state(uint256 proposalId) public view virtual override returns (ProposalState) { - ProposalCore memory proposal = _proposals[proposalId]; - - if (proposal.executed) { - return ProposalState.Executed; - } else if (proposal.canceled) { - return ProposalState.Canceled; - } else if (proposal.voteStart.getDeadline() >= block.number) { - return ProposalState.Pending; - } else if (proposal.voteEnd.getDeadline() >= block.number) { - return ProposalState.Active; - } else if (proposal.voteEnd.isExpired()) { - return - _quorumReached(proposalId) && _voteSucceeded(proposalId) - ? ProposalState.Succeeded - : ProposalState.Defeated; - } else { - revert("Governor: unknown proposal id"); - } - } - - /** - * @dev See {IGovernor-proposalSnapshot}. - */ - function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) { - return _proposals[proposalId].voteStart.getDeadline(); - } - - /** - * @dev See {IGovernor-proposalDeadline}. - */ - function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) { - return _proposals[proposalId].voteEnd.getDeadline(); - } - - /** - * @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_. - */ - function proposalThreshold() public view virtual returns (uint256) { - return 0; - } - - /** - * @dev Amount of votes already cast passes the threshold limit. - */ - function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal - - /** - * @dev Is the proposal successful or not. - */ - function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal - - /** - * @dev Register a vote with a given support and voting weight. - * - * Note: Support is generic and can represent various things depending on the voting system used. - */ - function _countVote( - uint256 proposalId, - address account, - uint8 support, - uint256 weight - ) internal virtual; - - /** - * @dev See {IGovernor-propose}. - */ - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public virtual override returns (uint256) { - require( - getVotes(msg.sender, block.number - 1) >= proposalThreshold(), - "GovernorCompatibilityBravo: proposer votes below proposal threshold" - ); - - uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description))); - - require(targets.length == values.length, "Governor: invalid proposal length"); - require(targets.length == calldatas.length, "Governor: invalid proposal length"); - require(targets.length > 0, "Governor: empty proposal"); - - ProposalCore storage proposal = _proposals[proposalId]; - require(proposal.voteStart.isUnset(), "Governor: proposal already exists"); - - uint64 snapshot = block.number.toUint64() + votingDelay().toUint64(); - uint64 deadline = snapshot + votingPeriod().toUint64(); - - proposal.voteStart.setDeadline(snapshot); - proposal.voteEnd.setDeadline(deadline); - - emit ProposalCreated( - proposalId, - _msgSender(), - targets, - values, - new string[](targets.length), - calldatas, - snapshot, - deadline, - description - ); - - return proposalId; - } - - /** - * @dev See {IGovernor-execute}. - */ - function execute( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public payable virtual override returns (uint256) { - uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); - - ProposalState status = state(proposalId); - require( - status == ProposalState.Succeeded || status == ProposalState.Queued, - "Governor: proposal not successful" - ); - _proposals[proposalId].executed = true; - - emit ProposalExecuted(proposalId); - - _execute(proposalId, targets, values, calldatas, descriptionHash); - - return proposalId; - } - - /** - * @dev Internal execution mechanism. Can be overriden to implement different execution mechanism - */ - function _execute( - uint256, /* proposalId */ - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 /*descriptionHash*/ - ) internal virtual { - string memory errorMessage = "Governor: call reverted without message"; - for (uint256 i = 0; i < targets.length; ++i) { - (bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]); - Address.verifyCallResult(success, returndata, errorMessage); - } - } - - /** - * @dev Internal cancel mechanism: locks up the proposal timer, preventing it from being re-submitted. Marks it as - * canceled to allow distinguishing it from executed proposals. - * - * Emits a {IGovernor-ProposalCanceled} event. - */ - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal virtual returns (uint256) { - uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); - ProposalState status = state(proposalId); - - require( - status != ProposalState.Canceled && status != ProposalState.Expired && status != ProposalState.Executed, - "Governor: proposal not active" - ); - _proposals[proposalId].canceled = true; - - emit ProposalCanceled(proposalId); - - return proposalId; - } - - /** - * @dev See {IGovernor-castVote}. - */ - function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { - address voter = _msgSender(); - return _castVote(proposalId, voter, support, ""); - } - - /** - * @dev See {IGovernor-castVoteWithReason}. - */ - function castVoteWithReason( - uint256 proposalId, - uint8 support, - string calldata reason - ) public virtual override returns (uint256) { - address voter = _msgSender(); - return _castVote(proposalId, voter, support, reason); - } - - /** - * @dev See {IGovernor-castVoteBySig}. - */ - function castVoteBySig( - uint256 proposalId, - uint8 support, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual override returns (uint256) { - address voter = ECDSA.recover( - _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))), - v, - r, - s - ); // mention that we assume that hashing works correctly - return _castVote(proposalId, voter, support, ""); - } - - /** - * @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve - * voting weight using {IGovernor-getVotes} and call the {_countVote} internal function. - * - * Emits a {IGovernor-VoteCast} event. - */ - function _castVote( - uint256 proposalId, - address account, - uint8 support, - string memory reason - ) internal virtual returns (uint256) { - ProposalCore storage proposal = _proposals[proposalId]; - require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active"); - - uint256 weight = getVotes(account, proposal.voteStart.getDeadline()); - _countVote(proposalId, account, support, weight); - - emit VoteCast(account, proposalId, support, weight, reason); - - return weight; - } - - /** - * @dev Address through which the governor executes action. Will be overloaded by module that execute actions - * through another contract such as a timelock. - */ - function _executor() internal view virtual returns (address) { - return address(this); - } -} diff --git a/certora/munged/governance/IGovernor.sol b/certora/munged/governance/IGovernor.sol deleted file mode 100644 index b30a2aa0e..000000000 --- a/certora/munged/governance/IGovernor.sol +++ /dev/null @@ -1,218 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/IGovernor.sol) - -pragma solidity ^0.8.0; - -import "../utils/introspection/ERC165.sol"; - -/** - * @dev Interface of the {Governor} core. - * - * _Available since v4.3._ - */ -abstract contract IGovernor is IERC165 { - enum ProposalState { - Pending, - Active, - Canceled, - Defeated, - Succeeded, - Queued, - Expired, - Executed - } - - /** - * @dev Emitted when a proposal is created. - */ - event ProposalCreated( - uint256 proposalId, - address proposer, - address[] targets, - uint256[] values, - string[] signatures, - bytes[] calldatas, - uint256 startBlock, - uint256 endBlock, - string description - ); - - /** - * @dev Emitted when a proposal is canceled. - */ - event ProposalCanceled(uint256 proposalId); - - /** - * @dev Emitted when a proposal is executed. - */ - event ProposalExecuted(uint256 proposalId); - - /** - * @dev Emitted when a vote is cast. - * - * Note: `support` values should be seen as buckets. There interpretation depends on the voting module used. - */ - event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason); - - /** - * @notice module:core - * @dev Name of the governor instance (used in building the ERC712 domain separator). - */ - function name() public view virtual returns (string memory); - - /** - * @notice module:core - * @dev Version of the governor instance (used in building the ERC712 domain separator). Default: "1" - */ - function version() public view virtual returns (string memory); - - /** - * @notice module:voting - * @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to - * be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of - * key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`. - * - * There are 2 standard keys: `support` and `quorum`. - * - * - `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`. - * - `quorum=bravo` means that only For votes are counted towards quorum. - * - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum. - * - * NOTE: The string can be decoded by the standard - * https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`] - * JavaScript class. - */ - // solhint-disable-next-line func-name-mixedcase - function COUNTING_MODE() public pure virtual returns (string memory); - - /** - * @notice module:core - * @dev Hashing function used to (re)build the proposal id from the proposal details.. - */ - function hashProposal( - address[] calldata targets, - uint256[] calldata values, - bytes[] calldata calldatas, - bytes32 descriptionHash - ) public pure virtual returns (uint256); - - /** - * @notice module:core - * @dev Current state of a proposal, following Compound's convention - */ - function state(uint256 proposalId) public view virtual returns (ProposalState); - - /** - * @notice module:core - * @dev Block number used to retrieve user's votes and quorum. As per Compound's Comp and OpenZeppelin's - * ERC20Votes, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the - * beginning of the following block. - */ - function proposalSnapshot(uint256 proposalId) public view virtual returns (uint256); - - /** - * @notice module:core - * @dev Block number at which votes close. Votes close at the end of this block, so it is possible to cast a vote - * during this block. - */ - function proposalDeadline(uint256 proposalId) public view virtual returns (uint256); - - /** - * @notice module:user-config - * @dev Delay, in number of block, between the proposal is created and the vote starts. This can be increassed to - * leave time for users to buy voting power, of delegate it, before the voting of a proposal starts. - */ - function votingDelay() public view virtual returns (uint256); - - /** - * @notice module:user-config - * @dev Delay, in number of blocks, between the vote start and vote ends. - * - * NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting - * duration compared to the voting delay. - */ - function votingPeriod() public view virtual returns (uint256); - - /** - * @notice module:user-config - * @dev Minimum number of cast voted required for a proposal to be successful. - * - * Note: The `blockNumber` parameter corresponds to the snaphot used for counting vote. This allows to scale the - * quroum depending on values such as the totalSupply of a token at this block (see {ERC20Votes}). - */ - function quorum(uint256 blockNumber) public view virtual returns (uint256); - - /** - * @notice module:reputation - * @dev Voting power of an `account` at a specific `blockNumber`. - * - * Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or - * multiple), {ERC20Votes} tokens. - */ - function getVotes(address account, uint256 blockNumber) public view virtual returns (uint256); - - /** - * @notice module:voting - * @dev Returns weither `account` has cast a vote on `proposalId`. - */ - function hasVoted(uint256 proposalId, address account) public view virtual returns (bool); - - /** - * @dev Create a new proposal. Vote start {IGovernor-votingDelay} blocks after the proposal is created and ends - * {IGovernor-votingPeriod} blocks after the voting starts. - * - * Emits a {ProposalCreated} event. - */ - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public virtual returns (uint256 proposalId); - - /** - * @dev Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the - * deadline to be reached. - * - * Emits a {ProposalExecuted} event. - * - * Note: some module can modify the requirements for execution, for example by adding an additional timelock. - */ - function execute( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public payable virtual returns (uint256 proposalId); - - /** - * @dev Cast a vote - * - * Emits a {VoteCast} event. - */ - function castVote(uint256 proposalId, uint8 support) public virtual returns (uint256 balance); - - /** - * @dev Cast a with a reason - * - * Emits a {VoteCast} event. - */ - function castVoteWithReason( - uint256 proposalId, - uint8 support, - string calldata reason - ) public virtual returns (uint256 balance); - - /** - * @dev Cast a vote using the user cryptographic signature. - * - * Emits a {VoteCast} event. - */ - function castVoteBySig( - uint256 proposalId, - uint8 support, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual returns (uint256 balance); -} diff --git a/certora/munged/governance/README.adoc b/certora/munged/governance/README.adoc deleted file mode 100644 index d198c9f93..000000000 --- a/certora/munged/governance/README.adoc +++ /dev/null @@ -1,168 +0,0 @@ -= Governance - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance - -This directory includes primitives for on-chain governance. - -== Governor - -This modular system of Governor contracts allows the deployment on-chain voting protocols similar to https://compound.finance/docs/governance[Compound's Governor Alpha & Bravo] and beyond, through the ability to easily customize multiple aspects of the protocol. - -[TIP] -==== -For a guided experience, set up your Governor contract using https://wizard.openzeppelin.com/#governor[Contracts Wizard]. - -For a written walkthrough, check out our guide on xref:ROOT:governance.adoc[How to set up on-chain governance]. -==== - -* {Governor}: The core contract that contains all the logic and primitives. It is abstract and requires choosing one of each of the modules below, or custom ones. - -Votes modules determine the source of voting power, and sometimes quorum number. - -* {GovernorVotes}: Extracts voting weight from an {ERC20Votes} token. - -* {GovernorVotesComp}: Extracts voting weight from a COMP-like or {ERC20VotesComp} token. - -* {GovernorVotesQuorumFraction}: Combines with `GovernorVotes` to set the quorum as a fraction of the total token supply. - -Counting modules determine valid voting options. - -* {GovernorCountingSimple}: Simple voting mechanism with 3 voting options: Against, For and Abstain. - -Timelock extensions add a delay for governance decisions to be executed. The workflow is extended to require a `queue` step before execution. With these modules, proposals are executed by the external timelock contract, thus it is the timelock that has to hold the assets that are being governed. - -* {GovernorTimelockControl}: Connects with an instance of {TimelockController}. Allows multiple proposers and executors, in addition to the Governor itself. - -* {GovernorTimelockCompound}: Connects with an instance of Compound's https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol[`Timelock`] contract. - -Other extensions can customize the behavior or interface in multiple ways. - -* {GovernorCompatibilityBravo}: Extends the interface to be fully `GovernorBravo`-compatible. Note that events are compatible regardless of whether this extension is included or not. - -* {GovernorSettings}: Manages some of the settings (voting delay, voting period duration, and proposal threshold) in a way that can be updated through a governance proposal, without requiering an upgrade. - -In addition to modules and extensions, the core contract requires a few virtual functions to be implemented to your particular specifications: - -* <>: Delay (in number of blocks) since the proposal is submitted until voting power is fixed and voting starts. This can be used to enforce a delay after a proposal is published for users to buy tokens, or delegate their votes. -* <>: Delay (in number of blocks) since the proposal starts until voting ends. -* <>: Quorum required for a proposal to be successful. This function includes a `blockNumber` argument so the quorum can adapt through time, for example, to follow a token's `totalSupply`. - -NOTE: Functions of the `Governor` contract do not include access control. If you want to restrict access, you should add these checks by overloading the particular functions. Among these, {Governor-_cancel} is internal by default, and you will have to expose it (which the right access control mechanism) yourself if this function is needed. - -=== Core - -{{IGovernor}} - -{{Governor}} - -=== Modules - -{{GovernorCountingSimple}} - -{{GovernorVotes}} - -{{GovernorVotesQuorumFraction}} - -{{GovernorVotesComp}} - -=== Extensions - -{{GovernorTimelockControl}} - -{{GovernorTimelockCompound}} - -{{GovernorSettings}} - -{{GovernorCompatibilityBravo}} - -=== Deprecated - -{{GovernorProposalThreshold}} - -== Timelock - -In a governance system, the {TimelockController} contract is in carge of introducing a delay between a proposal and its execution. It can be used with or without a {Governor}. - -{{TimelockController}} - -[[timelock-terminology]] -==== Terminology - -* *Operation:* A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution (see xref:access-control.adoc#operation_lifecycle[operation lifecycle]). If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content. -* *Operation status:* -** *Unset:* An operation that is not part of the timelock mechanism. -** *Pending:* An operation that has been scheduled, before the timer expires. -** *Ready:* An operation that has been scheduled, after the timer expires. -** *Done:* An operation that has been executed. -* *Predecessor*: An (optional) dependency between operations. An operation can depend on another operation (its predecessor), forcing the execution order of these two operations. -* *Role*: -** *Admin:* An address (smart contract or EOA) that is in charge of granting the roles of Proposer and Executor. -** *Proposer:* An address (smart contract or EOA) that is in charge of scheduling (and cancelling) operations. -** *Executor:* An address (smart contract or EOA) that is in charge of executing operations once the timelock has expired. This role can be given to the zero address to allow anyone to execute operations. - -[[timelock-operation]] -==== Operation structure - -Operation executed by the xref:api:governance.adoc#TimelockController[`TimelockController`] can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations. - -Both operations contain: - -* *Target*, the address of the smart contract that the timelock should operate on. -* *Value*, in wei, that should be sent with the transaction. Most of the time this will be 0. Ether can be deposited before-end or passed along when executing the transaction. -* *Data*, containing the encoded function selector and parameters of the call. This can be produced using a number of tools. For example, a maintenance operation granting role `ROLE` to `ACCOUNT` can be encode using web3js as follows: - -```javascript -const data = timelock.contract.methods.grantRole(ROLE, ACCOUNT).encodeABI() -``` - -* *Predecessor*, that specifies a dependency between operations. This dependency is optional. Use `bytes32(0)` if the operation does not have any dependency. -* *Salt*, used to disambiguate two otherwise identical operations. This can be any random value. - -In the case of batched operations, `target`, `value` and `data` are specified as arrays, which must be of the same length. - -[[timelock-operation-lifecycle]] -==== Operation lifecycle - -Timelocked operations are identified by a unique id (their hash) and follow a specific lifecycle: - -`Unset` -> `Pending` -> `Pending` + `Ready` -> `Done` - -* By calling xref:api:governance.adoc#TimelockController-schedule-address-uint256-bytes-bytes32-bytes32-uint256-[`schedule`] (or xref:api:governance.adoc#TimelockController-scheduleBatch-address---uint256---bytes---bytes32-bytes32-uint256-[`scheduleBatch`]), a proposer moves the operation from the `Unset` to the `Pending` state. This starts a timer that must be longer than the minimum delay. The timer expires at a timestamp accessible through the xref:api:governance.adoc#TimelockController-getTimestamp-bytes32-[`getTimestamp`] method. -* Once the timer expires, the operation automatically gets the `Ready` state. At this point, it can be executed. -* By calling xref:api:governance.adoc#TimelockController-TimelockController-execute-address-uint256-bytes-bytes32-bytes32-[`execute`] (or xref:api:governance.adoc#TimelockController-executeBatch-address---uint256---bytes---bytes32-bytes32-[`executeBatch`]), an executor triggers the operation's underlying transactions and moves it to the `Done` state. If the operation has a predecessor, it has to be in the `Done` state for this transition to succeed. -* xref:api:governance.adoc#TimelockController-TimelockController-cancel-bytes32-[`cancel`] allows proposers to cancel any `Pending` operation. This resets the operation to the `Unset` state. It is thus possible for a proposer to re-schedule an operation that has been cancelled. In this case, the timer restarts when the operation is re-scheduled. - -Operations status can be queried using the functions: - -* xref:api:governance.adoc#TimelockController-isOperationPending-bytes32-[`isOperationPending(bytes32)`] -* xref:api:governance.adoc#TimelockController-isOperationReady-bytes32-[`isOperationReady(bytes32)`] -* xref:api:governance.adoc#TimelockController-isOperationDone-bytes32-[`isOperationDone(bytes32)`] - -[[timelock-roles]] -==== Roles - -[[timelock-admin]] -===== Admin - -The admins are in charge of managing proposers and executors. For the timelock to be self-governed, this role should only be given to the timelock itself. Upon deployment, both the timelock and the deployer have this role. After further configuration and testing, the deployer can renounce this role such that all further maintenance operations have to go through the timelock process. - -This role is identified by the *TIMELOCK_ADMIN_ROLE* value: `0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5` - -[[timelock-proposer]] -===== Proposer - -The proposers are in charge of scheduling (and cancelling) operations. This is a critical role, that should be given to governing entities. This could be an EOA, a multisig, or a DAO. - -WARNING: *Proposer fight:* Having multiple proposers, while providing redundancy in case one becomes unavailable, can be dangerous. As proposer have their say on all operations, they could cancel operations they disagree with, including operations to remove them for the proposers. - -This role is identified by the *PROPOSER_ROLE* value: `0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1` - -[[timelock-executor]] -===== Executor - -The executors are in charge of executing the operations scheduled by the proposers once the timelock expires. Logic dictates that multisig or DAO that are proposers should also be executors in order to guarantee operations that have been scheduled will eventually be executed. However, having additional executors can reduce the cost (the executing transaction does not require validation by the multisig or DAO that proposed it), while ensuring whoever is in charge of execution cannot trigger actions that have not been scheduled by the proposers. Alternatively, it is possible to allow _any_ address to execute a proposal once the timelock has expired by granting the executor role to the zero address. - -This role is identified by the *EXECUTOR_ROLE* value: `0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63` - -WARNING: A live contract without at least one proposer and one executor is locked. Make sure these roles are filled by reliable entities before the deployer renounces its administrative rights in favour of the timelock contract itself. See the {AccessControl} documentation to learn more about role management. diff --git a/certora/munged/governance/TimelockController.sol b/certora/munged/governance/TimelockController.sol deleted file mode 100644 index b3b551dde..000000000 --- a/certora/munged/governance/TimelockController.sol +++ /dev/null @@ -1,354 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/TimelockController.sol) - -pragma solidity ^0.8.0; - -import "../access/AccessControl.sol"; - -/** - * @dev Contract module which acts as a timelocked controller. When set as the - * owner of an `Ownable` smart contract, it enforces a timelock on all - * `onlyOwner` maintenance operations. This gives time for users of the - * controlled contract to exit before a potentially dangerous maintenance - * operation is applied. - * - * By default, this contract is self administered, meaning administration tasks - * have to go through the timelock process. The proposer (resp executor) role - * is in charge of proposing (resp executing) operations. A common use case is - * to position this {TimelockController} as the owner of a smart contract, with - * a multisig or a DAO as the sole proposer. - * - * _Available since v3.3._ - */ -contract TimelockController is AccessControl { - bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); - bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); - bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); - uint256 internal constant _DONE_TIMESTAMP = uint256(1); - - mapping(bytes32 => uint256) private _timestamps; - uint256 private _minDelay; - - /** - * @dev Emitted when a call is scheduled as part of operation `id`. - */ - event CallScheduled( - bytes32 indexed id, - uint256 indexed index, - address target, - uint256 value, - bytes data, - bytes32 predecessor, - uint256 delay - ); - - /** - * @dev Emitted when a call is performed as part of operation `id`. - */ - event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data); - - /** - * @dev Emitted when operation `id` is cancelled. - */ - event Cancelled(bytes32 indexed id); - - /** - * @dev Emitted when the minimum delay for future operations is modified. - */ - event MinDelayChange(uint256 oldDuration, uint256 newDuration); - - /** - * @dev Initializes the contract with a given `minDelay`. - */ - constructor( - uint256 minDelay, - address[] memory proposers, - address[] memory executors - ) { - _setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE); - _setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE); - _setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE); - - // deployer + self administration - _setupRole(TIMELOCK_ADMIN_ROLE, _msgSender()); - _setupRole(TIMELOCK_ADMIN_ROLE, address(this)); - - // register proposers - for (uint256 i = 0; i < proposers.length; ++i) { - _setupRole(PROPOSER_ROLE, proposers[i]); - } - - // register executors - for (uint256 i = 0; i < executors.length; ++i) { - _setupRole(EXECUTOR_ROLE, executors[i]); - } - - _minDelay = minDelay; - emit MinDelayChange(0, minDelay); - } - - /** - * @dev Modifier to make a function callable only by a certain role. In - * addition to checking the sender's role, `address(0)` 's role is also - * considered. Granting a role to `address(0)` is equivalent to enabling - * this role for everyone. - */ - modifier onlyRoleOrOpenRole(bytes32 role) { - if (!hasRole(role, address(0))) { - _checkRole(role, _msgSender()); - } - _; - } - - /** - * @dev Contract might receive/hold ETH as part of the maintenance process. - */ - receive() external payable {} - - /** - * @dev Returns whether an id correspond to a registered operation. This - * includes both Pending, Ready and Done operations. - */ - function isOperation(bytes32 id) public view virtual returns (bool pending) { - return getTimestamp(id) > 0; - } - - /** - * @dev Returns whether an operation is pending or not. - */ - function isOperationPending(bytes32 id) public view virtual returns (bool pending) { - return getTimestamp(id) > _DONE_TIMESTAMP; - } - - /** - * @dev Returns whether an operation is ready or not. - */ - function isOperationReady(bytes32 id) public view virtual returns (bool ready) { - uint256 timestamp = getTimestamp(id); - return timestamp > _DONE_TIMESTAMP && timestamp <= block.timestamp; - } - - /** - * @dev Returns whether an operation is done or not. - */ - function isOperationDone(bytes32 id) public view virtual returns (bool done) { - return getTimestamp(id) == _DONE_TIMESTAMP; - } - - /** - * @dev Returns the timestamp at with an operation becomes ready (0 for - * unset operations, 1 for done operations). - */ - function getTimestamp(bytes32 id) public view virtual returns (uint256 timestamp) { - return _timestamps[id]; - } - - /** - * @dev Returns the minimum delay for an operation to become valid. - * - * This value can be changed by executing an operation that calls `updateDelay`. - */ - function getMinDelay() public view virtual returns (uint256 duration) { - return _minDelay; - } - - /** - * @dev Returns the identifier of an operation containing a single - * transaction. - */ - function hashOperation( - address target, - uint256 value, - bytes calldata data, - bytes32 predecessor, - bytes32 salt - ) public pure virtual returns (bytes32 hash) { - return keccak256(abi.encode(target, value, data, predecessor, salt)); - } - - /** - * @dev Returns the identifier of an operation containing a batch of - * transactions. - */ - function hashOperationBatch( - address[] calldata targets, - uint256[] calldata values, - bytes[] calldata datas, - bytes32 predecessor, - bytes32 salt - ) public pure virtual returns (bytes32 hash) { - return keccak256(abi.encode(targets, values, datas, predecessor, salt)); - } - - /** - * @dev Schedule an operation containing a single transaction. - * - * Emits a {CallScheduled} event. - * - * Requirements: - * - * - the caller must have the 'proposer' role. - */ - function schedule( - address target, - uint256 value, - bytes calldata data, - bytes32 predecessor, - bytes32 salt, - uint256 delay - ) public virtual onlyRole(PROPOSER_ROLE) { - bytes32 id = hashOperation(target, value, data, predecessor, salt); - _schedule(id, delay); - emit CallScheduled(id, 0, target, value, data, predecessor, delay); - } - - /** - * @dev Schedule an operation containing a batch of transactions. - * - * Emits one {CallScheduled} event per transaction in the batch. - * - * Requirements: - * - * - the caller must have the 'proposer' role. - */ - function scheduleBatch( - address[] calldata targets, - uint256[] calldata values, - bytes[] calldata datas, - bytes32 predecessor, - bytes32 salt, - uint256 delay - ) public virtual onlyRole(PROPOSER_ROLE) { - require(targets.length == values.length, "TimelockController: length mismatch"); - require(targets.length == datas.length, "TimelockController: length mismatch"); - - bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt); - _schedule(id, delay); - for (uint256 i = 0; i < targets.length; ++i) { - emit CallScheduled(id, i, targets[i], values[i], datas[i], predecessor, delay); - } - } - - /** - * @dev Schedule an operation that is to becomes valid after a given delay. - */ - function _schedule(bytes32 id, uint256 delay) private { - require(!isOperation(id), "TimelockController: operation already scheduled"); - require(delay >= getMinDelay(), "TimelockController: insufficient delay"); - _timestamps[id] = block.timestamp + delay; - } - - /** - * @dev Cancel an operation. - * - * Requirements: - * - * - the caller must have the 'proposer' role. - */ - function cancel(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) { - require(isOperationPending(id), "TimelockController: operation cannot be cancelled"); - delete _timestamps[id]; - - emit Cancelled(id); - } - - /** - * @dev Execute an (ready) operation containing a single transaction. - * - * Emits a {CallExecuted} event. - * - * Requirements: - * - * - the caller must have the 'executor' role. - */ - function execute( - address target, - uint256 value, - bytes calldata data, - bytes32 predecessor, - bytes32 salt - ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) { - bytes32 id = hashOperation(target, value, data, predecessor, salt); - _beforeCall(id, predecessor); - _call(id, 0, target, value, data); - _afterCall(id); - } - - /** - * @dev Execute an (ready) operation containing a batch of transactions. - * - * Emits one {CallExecuted} event per transaction in the batch. - * - * Requirements: - * - * - the caller must have the 'executor' role. - */ - function executeBatch( - address[] calldata targets, - uint256[] calldata values, - bytes[] calldata datas, - bytes32 predecessor, - bytes32 salt - ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) { - require(targets.length == values.length, "TimelockController: length mismatch"); - require(targets.length == datas.length, "TimelockController: length mismatch"); - - bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt); - _beforeCall(id, predecessor); - for (uint256 i = 0; i < targets.length; ++i) { - _call(id, i, targets[i], values[i], datas[i]); - } - _afterCall(id); - // ASSUME THAT THERE IS NO REENTRANCY IN WIZARDHARNESS1 - } - - /** - * @dev Checks before execution of an operation's calls. - */ - function _beforeCall(bytes32 id, bytes32 predecessor) private view { - require(isOperationReady(id), "TimelockController: operation is not ready"); - require(predecessor == bytes32(0) || isOperationDone(predecessor), "TimelockController: missing dependency"); - } - - /** - * @dev Checks after execution of an operation's calls. - */ - function _afterCall(bytes32 id) private { - require(isOperationReady(id), "TimelockController: operation is not ready"); - _timestamps[id] = _DONE_TIMESTAMP; - } - - /** - * @dev Execute an operation's call. - * - * Emits a {CallExecuted} event. - */ - function _call( - bytes32 id, - uint256 index, - address target, - uint256 value, - bytes calldata data - ) private { - (bool success, ) = target.call{value: value}(data); - require(success, "TimelockController: underlying transaction reverted"); - - emit CallExecuted(id, index, target, value, data); - } - - /** - * @dev Changes the minimum timelock duration for future operations. - * - * Emits a {MinDelayChange} event. - * - * Requirements: - * - * - the caller must be the timelock itself. This can only be achieved by scheduling and later executing - * an operation where the timelock is the target and the data is the ABI-encoded call to this function. - */ - function updateDelay(uint256 newDelay) external virtual { - require(msg.sender == address(this), "TimelockController: caller must be timelock"); - emit MinDelayChange(_minDelay, newDelay); - _minDelay = newDelay; - } -} diff --git a/certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol b/certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol deleted file mode 100644 index 20e507b61..000000000 --- a/certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol +++ /dev/null @@ -1,288 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/compatibility/GovernorCompatibilityBravo.sol) - -pragma solidity ^0.8.0; - -import "../../utils/Counters.sol"; -import "../../utils/math/SafeCast.sol"; -import "../extensions/IGovernorTimelock.sol"; -import "../Governor.sol"; -import "./IGovernorCompatibilityBravo.sol"; - -/** - * @dev Compatibility layer that implements GovernorBravo compatibility on to of {Governor}. - * - * This compatibility layer includes a voting system and requires a {IGovernorTimelock} compatible module to be added - * through inheritance. It does not include token bindings, not does it include any variable upgrade patterns. - * - * NOTE: When using this module, you may need to enable the Solidity optimizer to avoid hitting the contract size limit. - * - * _Available since v4.3._ - */ -abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorCompatibilityBravo, Governor { - using Counters for Counters.Counter; - using Timers for Timers.BlockNumber; - - enum VoteType { - Against, - For, - Abstain - } - - struct ProposalDetails { - address proposer; - address[] targets; - uint256[] values; - string[] signatures; - bytes[] calldatas; - uint256 forVotes; - uint256 againstVotes; - uint256 abstainVotes; - mapping(address => Receipt) receipts; - bytes32 descriptionHash; - } - - mapping(uint256 => ProposalDetails) private _proposalDetails; - - // solhint-disable-next-line func-name-mixedcase - function COUNTING_MODE() public pure virtual override returns (string memory) { - return "support=bravo&quorum=bravo"; - } - - // ============================================== Proposal lifecycle ============================================== - /** - * @dev See {IGovernor-propose}. - */ - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public virtual override(IGovernor, Governor) returns (uint256) { - _storeProposal(_msgSender(), targets, values, new string[](calldatas.length), calldatas, description); - return super.propose(targets, values, calldatas, description); - } - - /** - * @dev See {IGovernorCompatibilityBravo-propose}. - */ - function propose( - address[] memory targets, - uint256[] memory values, - string[] memory signatures, - bytes[] memory calldatas, - string memory description - ) public virtual override returns (uint256) { - _storeProposal(_msgSender(), targets, values, signatures, calldatas, description); - return propose(targets, values, _encodeCalldata(signatures, calldatas), description); - } - - /** - * @dev See {IGovernorCompatibilityBravo-queue}. - */ - function queue(uint256 proposalId) public virtual override { - ProposalDetails storage details = _proposalDetails[proposalId]; - queue( - details.targets, - details.values, - _encodeCalldata(details.signatures, details.calldatas), - details.descriptionHash - ); - } - - /** - * @dev See {IGovernorCompatibilityBravo-execute}. - */ - function execute(uint256 proposalId) public payable virtual override { - ProposalDetails storage details = _proposalDetails[proposalId]; - execute( - details.targets, - details.values, - _encodeCalldata(details.signatures, details.calldatas), - details.descriptionHash - ); - } - - function cancel(uint256 proposalId) public virtual override { - ProposalDetails storage details = _proposalDetails[proposalId]; - - require( - _msgSender() == details.proposer || getVotes(details.proposer, block.number - 1) < proposalThreshold(), - "GovernorBravo: proposer above threshold" - ); - - _cancel( - details.targets, - details.values, - _encodeCalldata(details.signatures, details.calldatas), - details.descriptionHash - ); - } - - /** - * @dev Encodes calldatas with optional function signature. - */ - function _encodeCalldata(string[] memory signatures, bytes[] memory calldatas) - private - pure - returns (bytes[] memory) - { - bytes[] memory fullcalldatas = new bytes[](calldatas.length); - - for (uint256 i = 0; i < signatures.length; ++i) { - fullcalldatas[i] = bytes(signatures[i]).length == 0 - ? calldatas[i] - : abi.encodeWithSignature(signatures[i], calldatas[i]); - } - - return fullcalldatas; - } - - /** - * @dev Store proposal metadata for later lookup - */ - function _storeProposal( - address proposer, - address[] memory targets, - uint256[] memory values, - string[] memory signatures, - bytes[] memory calldatas, - string memory description - ) private { - bytes32 descriptionHash = keccak256(bytes(description)); - uint256 proposalId = hashProposal(targets, values, _encodeCalldata(signatures, calldatas), descriptionHash); - - ProposalDetails storage details = _proposalDetails[proposalId]; - if (details.descriptionHash == bytes32(0)) { - details.proposer = proposer; - details.targets = targets; - details.values = values; - details.signatures = signatures; - details.calldatas = calldatas; - details.descriptionHash = descriptionHash; - } - } - - // ==================================================== Views ===================================================== - /** - * @dev See {IGovernorCompatibilityBravo-proposals}. - */ - function proposals(uint256 proposalId) - public - view - virtual - override - returns ( - uint256 id, - address proposer, - uint256 eta, - uint256 startBlock, - uint256 endBlock, - uint256 forVotes, - uint256 againstVotes, - uint256 abstainVotes, - bool canceled, - bool executed - ) - { - id = proposalId; - eta = proposalEta(proposalId); - startBlock = proposalSnapshot(proposalId); - endBlock = proposalDeadline(proposalId); - - ProposalDetails storage details = _proposalDetails[proposalId]; - proposer = details.proposer; - forVotes = details.forVotes; - againstVotes = details.againstVotes; - abstainVotes = details.abstainVotes; - - ProposalState status = state(proposalId); - canceled = status == ProposalState.Canceled; - executed = status == ProposalState.Executed; - } - - /** - * @dev See {IGovernorCompatibilityBravo-getActions}. - */ - function getActions(uint256 proposalId) - public - view - virtual - override - returns ( - address[] memory targets, - uint256[] memory values, - string[] memory signatures, - bytes[] memory calldatas - ) - { - ProposalDetails storage details = _proposalDetails[proposalId]; - return (details.targets, details.values, details.signatures, details.calldatas); - } - - /** - * @dev See {IGovernorCompatibilityBravo-getReceipt}. - */ - function getReceipt(uint256 proposalId, address voter) public view virtual override returns (Receipt memory) { - return _proposalDetails[proposalId].receipts[voter]; - } - - /** - * @dev See {IGovernorCompatibilityBravo-quorumVotes}. - */ - function quorumVotes() public view virtual override returns (uint256) { - return quorum(block.number - 1); - } - - // ==================================================== Voting ==================================================== - /** - * @dev See {IGovernor-hasVoted}. - */ - function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) { - return _proposalDetails[proposalId].receipts[account].hasVoted; - } - - /** - * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. - */ - function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal - ProposalDetails storage details = _proposalDetails[proposalId]; - return quorum(proposalSnapshot(proposalId)) < details.forVotes; - } - - /** - * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be scritly over the againstVotes. - */ - function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal - ProposalDetails storage details = _proposalDetails[proposalId]; - return details.forVotes > details.againstVotes; - } - - /** - * @dev See {Governor-_countVote}. In this module, the support follows Governor Bravo. - */ - function _countVote( - uint256 proposalId, - address account, - uint8 support, - uint256 weight - ) internal virtual override { - ProposalDetails storage details = _proposalDetails[proposalId]; - Receipt storage receipt = details.receipts[account]; - - require(!receipt.hasVoted, "GovernorCompatibilityBravo: vote already cast"); - receipt.hasVoted = true; - receipt.support = support; - receipt.votes = SafeCast.toUint96(weight); - - if (support == uint8(VoteType.Against)) { - details.againstVotes += weight; - } else if (support == uint8(VoteType.For)) { - details.forVotes += weight; - } else if (support == uint8(VoteType.Abstain)) { - details.abstainVotes += weight; - } else { - revert("GovernorCompatibilityBravo: invalid vote type"); - } - } -} diff --git a/certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol b/certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol deleted file mode 100644 index ae2cb7c39..000000000 --- a/certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol +++ /dev/null @@ -1,114 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/compatibility/IGovernorCompatibilityBravo.sol) - -pragma solidity ^0.8.0; - -import "../IGovernor.sol"; - -/** - * @dev Interface extension that adds missing functions to the {Governor} core to provide `GovernorBravo` compatibility. - * - * _Available since v4.3._ - */ -abstract contract IGovernorCompatibilityBravo is IGovernor { - /** - * @dev Proposal structure from Compound Governor Bravo. Not actually used by the compatibility layer, as - * {{proposal}} returns a very different structure. - */ - struct Proposal { - uint256 id; - address proposer; - uint256 eta; - address[] targets; - uint256[] values; - string[] signatures; - bytes[] calldatas; - uint256 startBlock; - uint256 endBlock; - uint256 forVotes; - uint256 againstVotes; - uint256 abstainVotes; - bool canceled; - bool executed; - mapping(address => Receipt) receipts; - } - - /** - * @dev Receipt structure from Compound Governor Bravo - */ - struct Receipt { - bool hasVoted; - uint8 support; - uint96 votes; - } - - /** - * @dev Part of the Governor Bravo's interface. - */ - function quorumVotes() public view virtual returns (uint256); - - /** - * @dev Part of the Governor Bravo's interface: _"The official record of all proposals ever proposed"_. - */ - function proposals(uint256) - public - view - virtual - returns ( - uint256 id, - address proposer, - uint256 eta, - uint256 startBlock, - uint256 endBlock, - uint256 forVotes, - uint256 againstVotes, - uint256 abstainVotes, - bool canceled, - bool executed - ); - - /** - * @dev Part of the Governor Bravo's interface: _"Function used to propose a new proposal"_. - */ - function propose( - address[] memory targets, - uint256[] memory values, - string[] memory signatures, - bytes[] memory calldatas, - string memory description - ) public virtual returns (uint256); - - /** - * @dev Part of the Governor Bravo's interface: _"Queues a proposal of state succeeded"_. - */ - function queue(uint256 proposalId) public virtual; - - /** - * @dev Part of the Governor Bravo's interface: _"Executes a queued proposal if eta has passed"_. - */ - function execute(uint256 proposalId) public payable virtual; - - /** - * @dev Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold. - */ - function cancel(uint256 proposalId) public virtual; - - /** - * @dev Part of the Governor Bravo's interface: _"Gets actions of a proposal"_. - */ - function getActions(uint256 proposalId) - public - view - virtual - returns ( - address[] memory targets, - uint256[] memory values, - string[] memory signatures, - bytes[] memory calldatas - ); - - /** - * @dev Part of the Governor Bravo's interface: _"Gets the receipt for a voter on a given proposal"_. - */ - function getReceipt(uint256 proposalId, address voter) public view virtual returns (Receipt memory); -} diff --git a/certora/munged/governance/extensions/GovernorCountingSimple.sol b/certora/munged/governance/extensions/GovernorCountingSimple.sol deleted file mode 100644 index b8c72ed9e..000000000 --- a/certora/munged/governance/extensions/GovernorCountingSimple.sol +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorCountingSimple.sol) - -pragma solidity ^0.8.0; - -import "../Governor.sol"; - -/** - * @dev Extension of {Governor} for simple, 3 options, vote counting. - * - * _Available since v4.3._ - */ -abstract contract GovernorCountingSimple is Governor { - /** - * @dev Supported vote types. Matches Governor Bravo ordering. - */ - enum VoteType { - Against, - For, - Abstain - } - - struct ProposalVote { - uint256 againstVotes; - uint256 forVotes; - uint256 abstainVotes; - mapping(address => bool) hasVoted; - } - - mapping(uint256 => ProposalVote) private _proposalVotes; - - /** - * @dev See {IGovernor-COUNTING_MODE}. - */ - // solhint-disable-next-line func-name-mixedcase - function COUNTING_MODE() public pure virtual override returns (string memory) { - return "support=bravo&quorum=for,abstain"; - } - - /** - * @dev See {IGovernor-hasVoted}. - */ - function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) { - return _proposalVotes[proposalId].hasVoted[account]; - } - - /** - * @dev Accessor to the internal vote counts. - */ - function proposalVotes(uint256 proposalId) - public - view - virtual - returns ( - uint256 againstVotes, - uint256 forVotes, - uint256 abstainVotes - ) - { - ProposalVote storage proposalvote = _proposalVotes[proposalId]; - return (proposalvote.againstVotes, proposalvote.forVotes, proposalvote.abstainVotes); - } - - /** - * @dev See {Governor-_quorumReached}. - */ - function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { - ProposalVote storage proposalvote = _proposalVotes[proposalId]; - - return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes; - } - - /** - * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. - */ - function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { - ProposalVote storage proposalvote = _proposalVotes[proposalId]; - - return proposalvote.forVotes > proposalvote.againstVotes; - } - - /** - * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo). - */ - function _countVote( - uint256 proposalId, - address account, - uint8 support, - uint256 weight - ) internal virtual override { - ProposalVote storage proposalvote = _proposalVotes[proposalId]; - - require(!proposalvote.hasVoted[account], "GovernorVotingSimple: vote already cast"); - proposalvote.hasVoted[account] = true; - - if (support == uint8(VoteType.Against)) { - proposalvote.againstVotes += weight; - } else if (support == uint8(VoteType.For)) { - proposalvote.forVotes += weight; - } else if (support == uint8(VoteType.Abstain)) { - proposalvote.abstainVotes += weight; - } else { - revert("GovernorVotingSimple: invalid value for enum VoteType"); - } - } -} diff --git a/certora/munged/governance/extensions/GovernorProposalThreshold.sol b/certora/munged/governance/extensions/GovernorProposalThreshold.sol deleted file mode 100644 index d9623f200..000000000 --- a/certora/munged/governance/extensions/GovernorProposalThreshold.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorProposalThreshold.sol) - -pragma solidity ^0.8.0; - -import "../Governor.sol"; - -/** - * @dev Extension of {Governor} for proposal restriction to token holders with a minimum balance. - * - * _Available since v4.3._ - * _Deprecated since v4.4._ - */ -abstract contract GovernorProposalThreshold is Governor { - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public virtual override returns (uint256) { - return super.propose(targets, values, calldatas, description); - } -} diff --git a/certora/munged/governance/extensions/GovernorSettings.sol b/certora/munged/governance/extensions/GovernorSettings.sol deleted file mode 100644 index 9b68f3cf6..000000000 --- a/certora/munged/governance/extensions/GovernorSettings.sol +++ /dev/null @@ -1,114 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorSettings.sol) - -pragma solidity ^0.8.0; - -import "../Governor.sol"; - -/** - * @dev Extension of {Governor} for settings updatable through governance. - * - * _Available since v4.4._ - */ -abstract contract GovernorSettings is Governor { - uint256 private _votingDelay; - uint256 private _votingPeriod; - uint256 private _proposalThreshold; - - event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay); - event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod); - event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold); - - /** - * @dev Initialize the governance parameters. - */ - constructor( - uint256 initialVotingDelay, - uint256 initialVotingPeriod, - uint256 initialProposalThreshold - ) { - _setVotingDelay(initialVotingDelay); - _setVotingPeriod(initialVotingPeriod); - _setProposalThreshold(initialProposalThreshold); - } - - /** - * @dev See {IGovernor-votingDelay}. - */ - function votingDelay() public view virtual override returns (uint256) { - return _votingDelay; - } - - /** - * @dev See {IGovernor-votingPeriod}. - */ - function votingPeriod() public view virtual override returns (uint256) { - return _votingPeriod; - } - - /** - * @dev See {Governor-proposalThreshold}. - */ - function proposalThreshold() public view virtual override returns (uint256) { - return _proposalThreshold; - } - - /** - * @dev Update the voting delay. This operation can only be performed through a governance proposal. - * - * Emits a {VotingDelaySet} event. - */ - function setVotingDelay(uint256 newVotingDelay) public onlyGovernance { - _setVotingDelay(newVotingDelay); - } - - /** - * @dev Update the voting period. This operation can only be performed through a governance proposal. - * - * Emits a {VotingPeriodSet} event. - */ - function setVotingPeriod(uint256 newVotingPeriod) public onlyGovernance { - _setVotingPeriod(newVotingPeriod); - } - - /** - * @dev Update the proposal threshold. This operation can only be performed through a governance proposal. - * - * Emits a {ProposalThresholdSet} event. - */ - function setProposalThreshold(uint256 newProposalThreshold) public onlyGovernance { - _setProposalThreshold(newProposalThreshold); - } - - /** - * @dev Internal setter for the voting delay. - * - * Emits a {VotingDelaySet} event. - */ - function _setVotingDelay(uint256 newVotingDelay) internal virtual { - emit VotingDelaySet(_votingDelay, newVotingDelay); - _votingDelay = newVotingDelay; - } - - /** - * @dev Internal setter for the voting period. - * - * Emits a {VotingPeriodSet} event. - */ - function _setVotingPeriod(uint256 newVotingPeriod) internal virtual { - // voting period must be at least one block long - require(newVotingPeriod > 0, "GovernorSettings: voting period too low"); - emit VotingPeriodSet(_votingPeriod, newVotingPeriod); - _votingPeriod = newVotingPeriod; - } - - /** - * @dev Internal setter for the proposal threshold. - * - * Emits a {ProposalThresholdSet} event. - */ - function _setProposalThreshold(uint256 newProposalThreshold) internal virtual { - emit ProposalThresholdSet(_proposalThreshold, newProposalThreshold); - _proposalThreshold = newProposalThreshold; - } -} diff --git a/certora/munged/governance/extensions/GovernorTimelockCompound.sol b/certora/munged/governance/extensions/GovernorTimelockCompound.sol deleted file mode 100644 index f9685b6e0..000000000 --- a/certora/munged/governance/extensions/GovernorTimelockCompound.sol +++ /dev/null @@ -1,244 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorTimelockCompound.sol) - -pragma solidity ^0.8.0; - -import "./IGovernorTimelock.sol"; -import "../Governor.sol"; -import "../../utils/math/SafeCast.sol"; - -/** - * https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol[Compound's timelock] interface - */ -interface ICompoundTimelock { - receive() external payable; - - // solhint-disable-next-line func-name-mixedcase - function GRACE_PERIOD() external view returns (uint256); - - // solhint-disable-next-line func-name-mixedcase - function MINIMUM_DELAY() external view returns (uint256); - - // solhint-disable-next-line func-name-mixedcase - function MAXIMUM_DELAY() external view returns (uint256); - - function admin() external view returns (address); - - function pendingAdmin() external view returns (address); - - function delay() external view returns (uint256); - - function queuedTransactions(bytes32) external view returns (bool); - - function setDelay(uint256) external; - - function acceptAdmin() external; - - function setPendingAdmin(address) external; - - function queueTransaction( - address target, - uint256 value, - string memory signature, - bytes memory data, - uint256 eta - ) external returns (bytes32); - - function cancelTransaction( - address target, - uint256 value, - string memory signature, - bytes memory data, - uint256 eta - ) external; - - function executeTransaction( - address target, - uint256 value, - string memory signature, - bytes memory data, - uint256 eta - ) external payable returns (bytes memory); -} - -/** - * @dev Extension of {Governor} that binds the execution process to a Compound Timelock. This adds a delay, enforced by - * the external timelock to all successful proposal (in addition to the voting duration). The {Governor} needs to be - * the admin of the timelock for any operation to be performed. A public, unrestricted, - * {GovernorTimelockCompound-__acceptAdmin} is available to accept ownership of the timelock. - * - * Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus, - * the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be - * inaccessible. - * - * _Available since v4.3._ - */ -abstract contract GovernorTimelockCompound is IGovernorTimelock, Governor { - using SafeCast for uint256; - using Timers for Timers.Timestamp; - - struct ProposalTimelock { - Timers.Timestamp timer; - } - - ICompoundTimelock private _timelock; - - mapping(uint256 => ProposalTimelock) private _proposalTimelocks; - - /** - * @dev Emitted when the timelock controller used for proposal execution is modified. - */ - event TimelockChange(address oldTimelock, address newTimelock); - - /** - * @dev Set the timelock. - */ - constructor(ICompoundTimelock timelockAddress) { - _updateTimelock(timelockAddress); - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, Governor) returns (bool) { - return interfaceId == type(IGovernorTimelock).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Overriden version of the {Governor-state} function with added support for the `Queued` and `Expired` status. - */ - function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) { - ProposalState status = super.state(proposalId); - - if (status != ProposalState.Succeeded) { - return status; - } - - uint256 eta = proposalEta(proposalId); - if (eta == 0) { - return status; - } else if (block.timestamp >= eta + _timelock.GRACE_PERIOD()) { - return ProposalState.Expired; - } else { - return ProposalState.Queued; - } - } - - /** - * @dev Public accessor to check the address of the timelock - */ - function timelock() public view virtual override returns (address) { - return address(_timelock); - } - - /** - * @dev Public accessor to check the eta of a queued proposal - */ - function proposalEta(uint256 proposalId) public view virtual override returns (uint256) { - return _proposalTimelocks[proposalId].timer.getDeadline(); - } - - /** - * @dev Function to queue a proposal to the timelock. - */ - function queue( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public virtual override returns (uint256) { - uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); - - require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful"); - - uint256 eta = block.timestamp + _timelock.delay(); - _proposalTimelocks[proposalId].timer.setDeadline(eta.toUint64()); - for (uint256 i = 0; i < targets.length; ++i) { - require( - !_timelock.queuedTransactions(keccak256(abi.encode(targets[i], values[i], "", calldatas[i], eta))), - "GovernorTimelockCompound: identical proposal action already queued" - ); - _timelock.queueTransaction(targets[i], values[i], "", calldatas[i], eta); - } - - emit ProposalQueued(proposalId, eta); - - return proposalId; - } - - /** - * @dev Overriden execute function that run the already queued proposal through the timelock. - */ - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 /*descriptionHash*/ - ) internal virtual override { - uint256 eta = proposalEta(proposalId); - require(eta > 0, "GovernorTimelockCompound: proposal not yet queued"); - Address.sendValue(payable(_timelock), msg.value); - for (uint256 i = 0; i < targets.length; ++i) { - _timelock.executeTransaction(targets[i], values[i], "", calldatas[i], eta); - } - } - - /** - * @dev Overriden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already - * been queued. - */ - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal virtual override returns (uint256) { - uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash); - - uint256 eta = proposalEta(proposalId); - if (eta > 0) { - for (uint256 i = 0; i < targets.length; ++i) { - _timelock.cancelTransaction(targets[i], values[i], "", calldatas[i], eta); - } - _proposalTimelocks[proposalId].timer.reset(); - } - - return proposalId; - } - - /** - * @dev Address through which the governor executes action. In this case, the timelock. - */ - function _executor() internal view virtual override returns (address) { - return address(_timelock); - } - - /** - * @dev Accept admin right over the timelock. - */ - // solhint-disable-next-line private-vars-leading-underscore - function __acceptAdmin() public { - _timelock.acceptAdmin(); - } - - /** - * @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates - * must be proposed, scheduled and executed using the {Governor} workflow. - * - * For security reason, the timelock must be handed over to another admin before setting up a new one. The two - * operations (hand over the timelock) and do the update can be batched in a single proposal. - * - * Note that if the timelock admin has been handed over in a previous operation, we refuse updates made through the - * timelock if admin of the timelock has already been accepted and the operation is executed outside the scope of - * governance. - */ - function updateTimelock(ICompoundTimelock newTimelock) external virtual onlyGovernance { - _updateTimelock(newTimelock); - } - - function _updateTimelock(ICompoundTimelock newTimelock) private { - emit TimelockChange(address(_timelock), address(newTimelock)); - _timelock = newTimelock; - } -} diff --git a/certora/munged/governance/extensions/GovernorTimelockControl.sol b/certora/munged/governance/extensions/GovernorTimelockControl.sol deleted file mode 100644 index 892ec3a55..000000000 --- a/certora/munged/governance/extensions/GovernorTimelockControl.sol +++ /dev/null @@ -1,154 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorTimelockControl.sol) - -pragma solidity ^0.8.0; - -import "./IGovernorTimelock.sol"; -import "../Governor.sol"; -import "../TimelockController.sol"; - -/** - * @dev Extension of {Governor} that binds the execution process to an instance of {TimelockController}. This adds a - * delay, enforced by the {TimelockController} to all successful proposal (in addition to the voting duration). The - * {Governor} needs the proposer (an ideally the executor) roles for the {Governor} to work properly. - * - * Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus, - * the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be - * inaccessible. - * - * _Available since v4.3._ - */ -abstract contract GovernorTimelockControl is IGovernorTimelock, Governor { - TimelockController private _timelock; - mapping(uint256 => bytes32) private _timelockIds; - - /** - * @dev Emitted when the timelock controller used for proposal execution is modified. - */ - event TimelockChange(address oldTimelock, address newTimelock); - - /** - * @dev Set the timelock. - */ - constructor(TimelockController timelockAddress) { - _updateTimelock(timelockAddress); - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, Governor) returns (bool) { - return interfaceId == type(IGovernorTimelock).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Overriden version of the {Governor-state} function with added support for the `Queued` status. - */ - function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) { - ProposalState status = super.state(proposalId); - - if (status != ProposalState.Succeeded) { - return status; - } - - // core tracks execution, so we just have to check if successful proposal have been queued. - bytes32 queueid = _timelockIds[proposalId]; - if (queueid == bytes32(0)) { - return status; - } else if (_timelock.isOperationDone(queueid)) { - return ProposalState.Executed; - } else { - return ProposalState.Queued; - } - } - - /** - * @dev Public accessor to check the address of the timelock - */ - function timelock() public view virtual override returns (address) { - return address(_timelock); - } - - /** - * @dev Public accessor to check the eta of a queued proposal - */ - function proposalEta(uint256 proposalId) public view virtual override returns (uint256) { - uint256 eta = _timelock.getTimestamp(_timelockIds[proposalId]); - return eta == 1 ? 0 : eta; // _DONE_TIMESTAMP (1) should be replaced with a 0 value - } - - /** - * @dev Function to queue a proposal to the timelock. - */ - function queue( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public virtual override returns (uint256) { - uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); - - require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful"); - - uint256 delay = _timelock.getMinDelay(); - _timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash); - _timelock.scheduleBatch(targets, values, calldatas, 0, descriptionHash, delay); - - emit ProposalQueued(proposalId, block.timestamp + delay); - - return proposalId; - } - - /** - * @dev Overriden execute function that run the already queued proposal through the timelock. - */ - function _execute( - uint256, /* proposalId */ - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal virtual override { - _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); - } - - /** - * @dev Overriden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already - * been queued. - */ - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal virtual override returns (uint256) { - uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash); - - if (_timelockIds[proposalId] != 0) { - _timelock.cancel(_timelockIds[proposalId]); - delete _timelockIds[proposalId]; - } - - return proposalId; - } - - /** - * @dev Address through which the governor executes action. In this case, the timelock. - */ - function _executor() internal view virtual override returns (address) { - return address(_timelock); - } - - /** - * @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates - * must be proposed, scheduled and executed using the {Governor} workflow. - */ - function updateTimelock(TimelockController newTimelock) external virtual onlyGovernance { - _updateTimelock(newTimelock); - } - - function _updateTimelock(TimelockController newTimelock) private { - emit TimelockChange(address(_timelock), address(newTimelock)); - _timelock = newTimelock; - } -} diff --git a/certora/munged/governance/extensions/GovernorVotes.sol b/certora/munged/governance/extensions/GovernorVotes.sol deleted file mode 100644 index a1172e614..000000000 --- a/certora/munged/governance/extensions/GovernorVotes.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorVotes.sol) - -pragma solidity ^0.8.0; - -import "../Governor.sol"; -import "../../token/ERC20/extensions/ERC20Votes.sol"; -import "../../utils/math/Math.sol"; - -/** - * @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token. - * - * _Available since v4.3._ - */ -abstract contract GovernorVotes is Governor { - ERC20Votes public immutable token; - - constructor(ERC20Votes tokenAddress) { - token = tokenAddress; - } - - /** - * Read the voting weight from the token's built in snapshot mechanism (see {IGovernor-getVotes}). - */ - function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { - return token.getPastVotes(account, blockNumber); - } -} diff --git a/certora/munged/governance/extensions/GovernorVotesComp.sol b/certora/munged/governance/extensions/GovernorVotesComp.sol deleted file mode 100644 index bb6b09a01..000000000 --- a/certora/munged/governance/extensions/GovernorVotesComp.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorVotesComp.sol) - -pragma solidity ^0.8.0; - -import "../Governor.sol"; -import "../../token/ERC20/extensions/ERC20VotesComp.sol"; - -/** - * @dev Extension of {Governor} for voting weight extraction from a Comp token. - * - * _Available since v4.3._ - */ -abstract contract GovernorVotesComp is Governor { - ERC20VotesComp public immutable token; - - constructor(ERC20VotesComp token_) { - token = token_; - } - - /** - * Read the voting weight from the token's built in snapshot mechanism (see {IGovernor-getVotes}). - */ - function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { - return token.getPriorVotes(account, blockNumber); - } -} diff --git a/certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol b/certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol deleted file mode 100644 index 5bac4e597..000000000 --- a/certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/extensions/GovernorVotesQuorumFraction.sol) - -pragma solidity ^0.8.0; - -import "./GovernorVotes.sol"; - -/** - * @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token and a quorum expressed as a - * fraction of the total supply. - * - * _Available since v4.3._ - */ -abstract contract GovernorVotesQuorumFraction is GovernorVotes { - uint256 private _quorumNumerator; - - event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator); - - constructor(uint256 quorumNumeratorValue) { - _updateQuorumNumerator(quorumNumeratorValue); - } - - function quorumNumerator() public view virtual returns (uint256) { - return _quorumNumerator; - } - - function quorumDenominator() public view virtual returns (uint256) { - return 100; - } - - function quorum(uint256 blockNumber) public view virtual override returns (uint256) { - return (token.getPastTotalSupply(blockNumber) * quorumNumerator()) / quorumDenominator(); - } - - function updateQuorumNumerator(uint256 newQuorumNumerator) external virtual onlyGovernance { - _updateQuorumNumerator(newQuorumNumerator); - } - - function _updateQuorumNumerator(uint256 newQuorumNumerator) internal virtual { - require( - newQuorumNumerator <= quorumDenominator(), - "GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator" - ); - - uint256 oldQuorumNumerator = _quorumNumerator; - _quorumNumerator = newQuorumNumerator; - - emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator); - } -} diff --git a/certora/munged/governance/extensions/IGovernorTimelock.sol b/certora/munged/governance/extensions/IGovernorTimelock.sol deleted file mode 100644 index 910135a37..000000000 --- a/certora/munged/governance/extensions/IGovernorTimelock.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (governance/extensions/IGovernorTimelock.sol) - -pragma solidity ^0.8.0; - -import "../IGovernor.sol"; - -/** - * @dev Extension of the {IGovernor} for timelock supporting modules. - * - * _Available since v4.3._ - */ -abstract contract IGovernorTimelock is IGovernor { - event ProposalQueued(uint256 proposalId, uint256 eta); - - function timelock() public view virtual returns (address); - - function proposalEta(uint256 proposalId) public view virtual returns (uint256); - - function queue( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public virtual returns (uint256 proposalId); -} diff --git a/certora/munged/interfaces/IERC1155.sol b/certora/munged/interfaces/IERC1155.sol deleted file mode 100644 index 995aa876c..000000000 --- a/certora/munged/interfaces/IERC1155.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1155.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC1155/IERC1155.sol"; diff --git a/certora/munged/interfaces/IERC1155MetadataURI.sol b/certora/munged/interfaces/IERC1155MetadataURI.sol deleted file mode 100644 index 6f7af0440..000000000 --- a/certora/munged/interfaces/IERC1155MetadataURI.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1155MetadataURI.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC1155/extensions/IERC1155MetadataURI.sol"; diff --git a/certora/munged/interfaces/IERC1155Receiver.sol b/certora/munged/interfaces/IERC1155Receiver.sol deleted file mode 100644 index cd947ddf1..000000000 --- a/certora/munged/interfaces/IERC1155Receiver.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1155Receiver.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC1155/IERC1155Receiver.sol"; diff --git a/certora/munged/interfaces/IERC1271.sol b/certora/munged/interfaces/IERC1271.sol deleted file mode 100644 index ee89e252b..000000000 --- a/certora/munged/interfaces/IERC1271.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1271.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC1271 standard signature validation method for - * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. - * - * _Available since v4.1._ - */ -interface IERC1271 { - /** - * @dev Should return whether the signature provided is valid for the provided data - * @param hash Hash of the data to be signed - * @param signature Signature byte array associated with _data - */ - function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); -} diff --git a/certora/munged/interfaces/IERC1363.sol b/certora/munged/interfaces/IERC1363.sol deleted file mode 100644 index 6d36befda..000000000 --- a/certora/munged/interfaces/IERC1363.sol +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1363.sol) - -pragma solidity ^0.8.0; - -import "./IERC20.sol"; -import "./IERC165.sol"; - -interface IERC1363 is IERC165, IERC20 { - /* - * Note: the ERC-165 identifier for this interface is 0x4bbee2df. - * 0x4bbee2df === - * bytes4(keccak256('transferAndCall(address,uint256)')) ^ - * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ - * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ - * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) - */ - - /* - * Note: the ERC-165 identifier for this interface is 0xfb9ec8ce. - * 0xfb9ec8ce === - * bytes4(keccak256('approveAndCall(address,uint256)')) ^ - * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) - */ - - /** - * @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver - * @param to address The address which you want to transfer to - * @param value uint256 The amount of tokens to be transferred - * @return true unless throwing - */ - function transferAndCall(address to, uint256 value) external returns (bool); - - /** - * @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver - * @param to address The address which you want to transfer to - * @param value uint256 The amount of tokens to be transferred - * @param data bytes Additional data with no specified format, sent in call to `to` - * @return true unless throwing - */ - function transferAndCall( - address to, - uint256 value, - bytes memory data - ) external returns (bool); - - /** - * @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver - * @param from address The address which you want to send tokens from - * @param to address The address which you want to transfer to - * @param value uint256 The amount of tokens to be transferred - * @return true unless throwing - */ - function transferFromAndCall( - address from, - address to, - uint256 value - ) external returns (bool); - - /** - * @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver - * @param from address The address which you want to send tokens from - * @param to address The address which you want to transfer to - * @param value uint256 The amount of tokens to be transferred - * @param data bytes Additional data with no specified format, sent in call to `to` - * @return true unless throwing - */ - function transferFromAndCall( - address from, - address to, - uint256 value, - bytes memory data - ) external returns (bool); - - /** - * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender - * and then call `onApprovalReceived` on spender. - * @param spender address The address which will spend the funds - * @param value uint256 The amount of tokens to be spent - */ - function approveAndCall(address spender, uint256 value) external returns (bool); - - /** - * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender - * and then call `onApprovalReceived` on spender. - * @param spender address The address which will spend the funds - * @param value uint256 The amount of tokens to be spent - * @param data bytes Additional data with no specified format, sent in call to `spender` - */ - function approveAndCall( - address spender, - uint256 value, - bytes memory data - ) external returns (bool); -} diff --git a/certora/munged/interfaces/IERC1363Receiver.sol b/certora/munged/interfaces/IERC1363Receiver.sol deleted file mode 100644 index ae6e10ce9..000000000 --- a/certora/munged/interfaces/IERC1363Receiver.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1363Receiver.sol) - -pragma solidity ^0.8.0; - -interface IERC1363Receiver { - /* - * Note: the ERC-165 identifier for this interface is 0x88a7ca5c. - * 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)")) - */ - - /** - * @notice Handle the receipt of ERC1363 tokens - * @dev Any ERC1363 smart contract calls this function on the recipient - * after a `transfer` or a `transferFrom`. This function MAY throw to revert and reject the - * transfer. Return of other than the magic value MUST result in the - * transaction being reverted. - * Note: the token contract address is always the message sender. - * @param operator address The address which called `transferAndCall` or `transferFromAndCall` function - * @param from address The address which are token transferred from - * @param value uint256 The amount of tokens transferred - * @param data bytes Additional data with no specified format - * @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))` - * unless throwing - */ - function onTransferReceived( - address operator, - address from, - uint256 value, - bytes memory data - ) external returns (bytes4); -} diff --git a/certora/munged/interfaces/IERC1363Spender.sol b/certora/munged/interfaces/IERC1363Spender.sol deleted file mode 100644 index 782b20fce..000000000 --- a/certora/munged/interfaces/IERC1363Spender.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1363Spender.sol) - -pragma solidity ^0.8.0; - -interface IERC1363Spender { - /* - * Note: the ERC-165 identifier for this interface is 0x7b04a2d0. - * 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)")) - */ - - /** - * @notice Handle the approval of ERC1363 tokens - * @dev Any ERC1363 smart contract calls this function on the recipient - * after an `approve`. This function MAY throw to revert and reject the - * approval. Return of other than the magic value MUST result in the - * transaction being reverted. - * Note: the token contract address is always the message sender. - * @param owner address The address which called `approveAndCall` function - * @param value uint256 The amount of tokens to be spent - * @param data bytes Additional data with no specified format - * @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))` - * unless throwing - */ - function onApprovalReceived( - address owner, - uint256 value, - bytes memory data - ) external returns (bytes4); -} diff --git a/certora/munged/interfaces/IERC165.sol b/certora/munged/interfaces/IERC165.sol deleted file mode 100644 index e1d54e698..000000000 --- a/certora/munged/interfaces/IERC165.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC165.sol) - -pragma solidity ^0.8.0; - -import "../utils/introspection/IERC165.sol"; diff --git a/certora/munged/interfaces/IERC1820Implementer.sol b/certora/munged/interfaces/IERC1820Implementer.sol deleted file mode 100644 index 7ce0a79d7..000000000 --- a/certora/munged/interfaces/IERC1820Implementer.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1820Implementer.sol) - -pragma solidity ^0.8.0; - -import "../utils/introspection/IERC1820Implementer.sol"; diff --git a/certora/munged/interfaces/IERC1820Registry.sol b/certora/munged/interfaces/IERC1820Registry.sol deleted file mode 100644 index aea318470..000000000 --- a/certora/munged/interfaces/IERC1820Registry.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC1820Registry.sol) - -pragma solidity ^0.8.0; - -import "../utils/introspection/IERC1820Registry.sol"; diff --git a/certora/munged/interfaces/IERC20.sol b/certora/munged/interfaces/IERC20.sol deleted file mode 100644 index ee6091660..000000000 --- a/certora/munged/interfaces/IERC20.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC20.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC20/IERC20.sol"; diff --git a/certora/munged/interfaces/IERC20Metadata.sol b/certora/munged/interfaces/IERC20Metadata.sol deleted file mode 100644 index 4752b50a3..000000000 --- a/certora/munged/interfaces/IERC20Metadata.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC20Metadata.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/IERC20Metadata.sol"; diff --git a/certora/munged/interfaces/IERC2981.sol b/certora/munged/interfaces/IERC2981.sol deleted file mode 100644 index 3ef94b0e7..000000000 --- a/certora/munged/interfaces/IERC2981.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC2981.sol) - -pragma solidity ^0.8.0; - -import "./IERC165.sol"; - -/** - * @dev Interface for the NFT Royalty Standard - */ -interface IERC2981 is IERC165 { - /** - * @dev Called with the sale price to determine how much royalty is owed and to whom. - * @param tokenId - the NFT asset queried for royalty information - * @param salePrice - the sale price of the NFT asset specified by `tokenId` - * @return receiver - address of who should be sent the royalty payment - * @return royaltyAmount - the royalty payment amount for `salePrice` - */ - function royaltyInfo(uint256 tokenId, uint256 salePrice) - external - view - returns (address receiver, uint256 royaltyAmount); -} diff --git a/certora/munged/interfaces/IERC3156.sol b/certora/munged/interfaces/IERC3156.sol deleted file mode 100644 index f24e30142..000000000 --- a/certora/munged/interfaces/IERC3156.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC3156.sol) - -pragma solidity ^0.8.0; - -import "./IERC3156FlashBorrower.sol"; -import "./IERC3156FlashLender.sol"; diff --git a/certora/munged/interfaces/IERC3156FlashBorrower.sol b/certora/munged/interfaces/IERC3156FlashBorrower.sol deleted file mode 100644 index e956c2a6b..000000000 --- a/certora/munged/interfaces/IERC3156FlashBorrower.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC3156FlashBorrower.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC3156 FlashBorrower, as defined in - * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. - * - * _Available since v4.1._ - */ -interface IERC3156FlashBorrower { - /** - * @dev Receive a flash loan. - * @param initiator The initiator of the loan. - * @param token The loan currency. - * @param amount The amount of tokens lent. - * @param fee The additional amount of tokens to repay. - * @param data Arbitrary data structure, intended to contain user-defined parameters. - * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" - */ - function onFlashLoan( - address initiator, - address token, - uint256 amount, - uint256 fee, - bytes calldata data - ) external returns (bytes32); -} diff --git a/certora/munged/interfaces/IERC3156FlashLender.sol b/certora/munged/interfaces/IERC3156FlashLender.sol deleted file mode 100644 index 954f79bfc..000000000 --- a/certora/munged/interfaces/IERC3156FlashLender.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC3156FlashLender.sol) - -pragma solidity ^0.8.0; - -import "./IERC3156FlashBorrower.sol"; - -/** - * @dev Interface of the ERC3156 FlashLender, as defined in - * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. - * - * _Available since v4.1._ - */ -interface IERC3156FlashLender { - /** - * @dev The amount of currency available to be lended. - * @param token The loan currency. - * @return The amount of `token` that can be borrowed. - */ - function maxFlashLoan(address token) external view returns (uint256); - - /** - * @dev The fee to be charged for a given loan. - * @param token The loan currency. - * @param amount The amount of tokens lent. - * @return The amount of `token` to be charged for the loan, on top of the returned principal. - */ - function flashFee(address token, uint256 amount) external view returns (uint256); - - /** - * @dev Initiate a flash loan. - * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. - * @param token The loan currency. - * @param amount The amount of tokens lent. - * @param data Arbitrary data structure, intended to contain user-defined parameters. - */ - function flashLoan( - IERC3156FlashBorrower receiver, - address token, - uint256 amount, - bytes calldata data - ) external returns (bool); -} diff --git a/certora/munged/interfaces/IERC721.sol b/certora/munged/interfaces/IERC721.sol deleted file mode 100644 index 59a2e2f11..000000000 --- a/certora/munged/interfaces/IERC721.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC721.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC721/IERC721.sol"; diff --git a/certora/munged/interfaces/IERC721Enumerable.sol b/certora/munged/interfaces/IERC721Enumerable.sol deleted file mode 100644 index e3b17e7d0..000000000 --- a/certora/munged/interfaces/IERC721Enumerable.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC721Enumerable.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC721/extensions/IERC721Enumerable.sol"; diff --git a/certora/munged/interfaces/IERC721Metadata.sol b/certora/munged/interfaces/IERC721Metadata.sol deleted file mode 100644 index 1a7f1f8e6..000000000 --- a/certora/munged/interfaces/IERC721Metadata.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC721Metadata.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC721/extensions/IERC721Metadata.sol"; diff --git a/certora/munged/interfaces/IERC721Receiver.sol b/certora/munged/interfaces/IERC721Receiver.sol deleted file mode 100644 index 0e65c5e6c..000000000 --- a/certora/munged/interfaces/IERC721Receiver.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC721Receiver.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC721/IERC721Receiver.sol"; diff --git a/certora/munged/interfaces/IERC777.sol b/certora/munged/interfaces/IERC777.sol deleted file mode 100644 index 4d808007e..000000000 --- a/certora/munged/interfaces/IERC777.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC777.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC777/IERC777.sol"; diff --git a/certora/munged/interfaces/IERC777Recipient.sol b/certora/munged/interfaces/IERC777Recipient.sol deleted file mode 100644 index 36b58e51b..000000000 --- a/certora/munged/interfaces/IERC777Recipient.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC777Recipient.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC777/IERC777Recipient.sol"; diff --git a/certora/munged/interfaces/IERC777Sender.sol b/certora/munged/interfaces/IERC777Sender.sol deleted file mode 100644 index f9f564853..000000000 --- a/certora/munged/interfaces/IERC777Sender.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/IERC777Sender.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC777/IERC777Sender.sol"; diff --git a/certora/munged/interfaces/README.adoc b/certora/munged/interfaces/README.adoc deleted file mode 100644 index 31dd27c85..000000000 --- a/certora/munged/interfaces/README.adoc +++ /dev/null @@ -1,50 +0,0 @@ -= Interfaces - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/interfaces - -== List of standardized interfaces -These interfaces are available as `.sol` files, and also as compiler `.json` ABI files (through the npm package). These -are usefull to interract with third party contracts that implement them. - -- {IERC20} -- {IERC20Metadata} -- {IERC165} -- {IERC721} -- {IERC721Receiver} -- {IERC721Enumerable} -- {IERC721Metadata} -- {IERC777} -- {IERC777Recipient} -- {IERC777Sender} -- {IERC1155} -- {IERC1155Receiver} -- {IERC1155MetadataURI} -- {IERC1271} -- {IERC1363} -- {IERC1820Implementer} -- {IERC1820Registry} -- {IERC2612} -- {IERC2981} -- {IERC3156FlashLender} -- {IERC3156FlashBorrower} - -== Detailed ABI - -{{IERC1271}} - -{{IERC1363}} - -{{IERC1363Receiver}} - -{{IERC1820Implementer}} - -{{IERC1820Registry}} - -{{IERC2612}} - -{{IERC2981}} - -{{IERC3156FlashLender}} - -{{IERC3156FlashBorrower}} diff --git a/certora/munged/interfaces/draft-IERC2612.sol b/certora/munged/interfaces/draft-IERC2612.sol deleted file mode 100644 index 160a5ef61..000000000 --- a/certora/munged/interfaces/draft-IERC2612.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (interfaces/draft-IERC2612.sol) - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/draft-IERC20Permit.sol"; - -interface IERC2612 is IERC20Permit {} diff --git a/certora/munged/metatx/ERC2771Context.sol b/certora/munged/metatx/ERC2771Context.sol deleted file mode 100644 index e5c0674a7..000000000 --- a/certora/munged/metatx/ERC2771Context.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (metatx/ERC2771Context.sol) - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; - -/** - * @dev Context variant with ERC2771 support. - */ -abstract contract ERC2771Context is Context { - address private _trustedForwarder; - - constructor(address trustedForwarder) { - _trustedForwarder = trustedForwarder; - } - - function isTrustedForwarder(address forwarder) public view virtual returns (bool) { - return forwarder == _trustedForwarder; - } - - function _msgSender() internal view virtual override returns (address sender) { - if (isTrustedForwarder(msg.sender)) { - // The assembly code is more direct than the Solidity version using `abi.decode`. - assembly { - sender := shr(96, calldataload(sub(calldatasize(), 20))) - } - } else { - return super._msgSender(); - } - } - - function _msgData() internal view virtual override returns (bytes calldata) { - if (isTrustedForwarder(msg.sender)) { - return msg.data[:msg.data.length - 20]; - } else { - return super._msgData(); - } - } -} diff --git a/certora/munged/metatx/MinimalForwarder.sol b/certora/munged/metatx/MinimalForwarder.sol deleted file mode 100644 index b3326f3f4..000000000 --- a/certora/munged/metatx/MinimalForwarder.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (metatx/MinimalForwarder.sol) - -pragma solidity ^0.8.0; - -import "../utils/cryptography/ECDSA.sol"; -import "../utils/cryptography/draft-EIP712.sol"; - -/** - * @dev Simple minimal forwarder to be used together with an ERC2771 compatible contract. See {ERC2771Context}. - */ -contract MinimalForwarder is EIP712 { - using ECDSA for bytes32; - - struct ForwardRequest { - address from; - address to; - uint256 value; - uint256 gas; - uint256 nonce; - bytes data; - } - - bytes32 private constant _TYPEHASH = - keccak256("ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)"); - - mapping(address => uint256) private _nonces; - - constructor() EIP712("MinimalForwarder", "0.0.1") {} - - function getNonce(address from) public view returns (uint256) { - return _nonces[from]; - } - - function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) { - address signer = _hashTypedDataV4( - keccak256(abi.encode(_TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data))) - ).recover(signature); - return _nonces[req.from] == req.nonce && signer == req.from; - } - - function execute(ForwardRequest calldata req, bytes calldata signature) - public - payable - returns (bool, bytes memory) - { - require(verify(req, signature), "MinimalForwarder: signature does not match request"); - _nonces[req.from] = req.nonce + 1; - - (bool success, bytes memory returndata) = req.to.call{gas: req.gas, value: req.value}( - abi.encodePacked(req.data, req.from) - ); - // Validate that the relayer has sent enough gas for the call. - // See https://ronan.eth.link/blog/ethereum-gas-dangers/ - assert(gasleft() > req.gas / 63); - - return (success, returndata); - } -} diff --git a/certora/munged/metatx/README.adoc b/certora/munged/metatx/README.adoc deleted file mode 100644 index eccdeaf97..000000000 --- a/certora/munged/metatx/README.adoc +++ /dev/null @@ -1,12 +0,0 @@ -= Meta Transactions - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/metatx - -== Core - -{{ERC2771Context}} - -== Utils - -{{MinimalForwarder}} diff --git a/certora/munged/mocks/AccessControlEnumerableMock.sol b/certora/munged/mocks/AccessControlEnumerableMock.sol deleted file mode 100644 index 7b15e3602..000000000 --- a/certora/munged/mocks/AccessControlEnumerableMock.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../access/AccessControlEnumerable.sol"; - -contract AccessControlEnumerableMock is AccessControlEnumerable { - constructor() { - _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public { - _setRoleAdmin(roleId, adminRoleId); - } - - function senderProtected(bytes32 roleId) public onlyRole(roleId) {} -} diff --git a/certora/munged/mocks/AccessControlMock.sol b/certora/munged/mocks/AccessControlMock.sol deleted file mode 100644 index 86f51477e..000000000 --- a/certora/munged/mocks/AccessControlMock.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../access/AccessControl.sol"; - -contract AccessControlMock is AccessControl { - constructor() { - _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public { - _setRoleAdmin(roleId, adminRoleId); - } - - function senderProtected(bytes32 roleId) public onlyRole(roleId) {} -} diff --git a/certora/munged/mocks/AddressImpl.sol b/certora/munged/mocks/AddressImpl.sol deleted file mode 100644 index 702093c73..000000000 --- a/certora/munged/mocks/AddressImpl.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Address.sol"; - -contract AddressImpl { - string public sharedAnswer; - - event CallReturnValue(string data); - - function isContract(address account) external view returns (bool) { - return Address.isContract(account); - } - - function sendValue(address payable receiver, uint256 amount) external { - Address.sendValue(receiver, amount); - } - - function functionCall(address target, bytes calldata data) external { - bytes memory returnData = Address.functionCall(target, data); - emit CallReturnValue(abi.decode(returnData, (string))); - } - - function functionCallWithValue( - address target, - bytes calldata data, - uint256 value - ) external payable { - bytes memory returnData = Address.functionCallWithValue(target, data, value); - emit CallReturnValue(abi.decode(returnData, (string))); - } - - function functionStaticCall(address target, bytes calldata data) external { - bytes memory returnData = Address.functionStaticCall(target, data); - emit CallReturnValue(abi.decode(returnData, (string))); - } - - function functionDelegateCall(address target, bytes calldata data) external { - bytes memory returnData = Address.functionDelegateCall(target, data); - emit CallReturnValue(abi.decode(returnData, (string))); - } - - // sendValue's tests require the contract to hold Ether - receive() external payable {} -} diff --git a/certora/munged/mocks/ArraysImpl.sol b/certora/munged/mocks/ArraysImpl.sol deleted file mode 100644 index f720524b8..000000000 --- a/certora/munged/mocks/ArraysImpl.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Arrays.sol"; - -contract ArraysImpl { - using Arrays for uint256[]; - - uint256[] private _array; - - constructor(uint256[] memory array) { - _array = array; - } - - function findUpperBound(uint256 element) external view returns (uint256) { - return _array.findUpperBound(element); - } -} diff --git a/certora/munged/mocks/BadBeacon.sol b/certora/munged/mocks/BadBeacon.sol deleted file mode 100644 index bedcfed84..000000000 --- a/certora/munged/mocks/BadBeacon.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -contract BadBeaconNoImpl {} - -contract BadBeaconNotContract { - function implementation() external pure returns (address) { - return address(0x1); - } -} diff --git a/certora/munged/mocks/BitmapMock.sol b/certora/munged/mocks/BitmapMock.sol deleted file mode 100644 index ccf8486f5..000000000 --- a/certora/munged/mocks/BitmapMock.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/structs/BitMaps.sol"; - -contract BitMapMock { - using BitMaps for BitMaps.BitMap; - - BitMaps.BitMap private _bitmap; - - function get(uint256 index) public view returns (bool) { - return _bitmap.get(index); - } - - function setTo(uint256 index, bool value) public { - _bitmap.setTo(index, value); - } - - function set(uint256 index) public { - _bitmap.set(index); - } - - function unset(uint256 index) public { - _bitmap.unset(index); - } -} diff --git a/certora/munged/mocks/CallReceiverMock.sol b/certora/munged/mocks/CallReceiverMock.sol deleted file mode 100644 index 11d21b405..000000000 --- a/certora/munged/mocks/CallReceiverMock.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -contract CallReceiverMock { - string public sharedAnswer; - - event MockFunctionCalled(); - - uint256[] private _array; - - function mockFunction() public payable returns (string memory) { - emit MockFunctionCalled(); - - return "0x1234"; - } - - function mockFunctionNonPayable() public returns (string memory) { - emit MockFunctionCalled(); - - return "0x1234"; - } - - function mockStaticFunction() public pure returns (string memory) { - return "0x1234"; - } - - function mockFunctionRevertsNoReason() public payable { - revert(); - } - - function mockFunctionRevertsReason() public payable { - revert("CallReceiverMock: reverting"); - } - - function mockFunctionThrows() public payable { - assert(false); - } - - function mockFunctionOutOfGas() public payable { - for (uint256 i = 0; ; ++i) { - _array.push(i); - } - } - - function mockFunctionWritesStorage() public returns (string memory) { - sharedAnswer = "42"; - return "0x1234"; - } -} diff --git a/certora/munged/mocks/ClashingImplementation.sol b/certora/munged/mocks/ClashingImplementation.sol deleted file mode 100644 index 80aca0c29..000000000 --- a/certora/munged/mocks/ClashingImplementation.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/** - * @dev Implementation contract with an admin() function made to clash with - * @dev TransparentUpgradeableProxy's to test correct functioning of the - * @dev Transparent Proxy feature. - */ -contract ClashingImplementation { - function admin() external pure returns (address) { - return 0x0000000000000000000000000000000011111142; - } - - function delegatedFunction() external pure returns (bool) { - return true; - } -} diff --git a/certora/munged/mocks/ClonesMock.sol b/certora/munged/mocks/ClonesMock.sol deleted file mode 100644 index 3719b0a78..000000000 --- a/certora/munged/mocks/ClonesMock.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../proxy/Clones.sol"; -import "../utils/Address.sol"; - -contract ClonesMock { - using Address for address; - using Clones for address; - - event NewInstance(address instance); - - function clone(address implementation, bytes calldata initdata) public payable { - _initAndEmit(implementation.clone(), initdata); - } - - function cloneDeterministic( - address implementation, - bytes32 salt, - bytes calldata initdata - ) public payable { - _initAndEmit(implementation.cloneDeterministic(salt), initdata); - } - - function predictDeterministicAddress(address implementation, bytes32 salt) public view returns (address predicted) { - return implementation.predictDeterministicAddress(salt); - } - - function _initAndEmit(address instance, bytes memory initdata) private { - if (initdata.length > 0) { - instance.functionCallWithValue(initdata, msg.value); - } - emit NewInstance(instance); - } -} diff --git a/certora/munged/mocks/ConditionalEscrowMock.sol b/certora/munged/mocks/ConditionalEscrowMock.sol deleted file mode 100644 index ececf0521..000000000 --- a/certora/munged/mocks/ConditionalEscrowMock.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/escrow/ConditionalEscrow.sol"; - -// mock class using ConditionalEscrow -contract ConditionalEscrowMock is ConditionalEscrow { - mapping(address => bool) private _allowed; - - function setAllowed(address payee, bool allowed) public { - _allowed[payee] = allowed; - } - - function withdrawalAllowed(address payee) public view override returns (bool) { - return _allowed[payee]; - } -} diff --git a/certora/munged/mocks/ContextMock.sol b/certora/munged/mocks/ContextMock.sol deleted file mode 100644 index f17af38a4..000000000 --- a/certora/munged/mocks/ContextMock.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; - -contract ContextMock is Context { - event Sender(address sender); - - function msgSender() public { - emit Sender(_msgSender()); - } - - event Data(bytes data, uint256 integerValue, string stringValue); - - function msgData(uint256 integerValue, string memory stringValue) public { - emit Data(_msgData(), integerValue, stringValue); - } -} - -contract ContextMockCaller { - function callSender(ContextMock context) public { - context.msgSender(); - } - - function callData( - ContextMock context, - uint256 integerValue, - string memory stringValue - ) public { - context.msgData(integerValue, stringValue); - } -} diff --git a/certora/munged/mocks/CountersImpl.sol b/certora/munged/mocks/CountersImpl.sol deleted file mode 100644 index 651b50baf..000000000 --- a/certora/munged/mocks/CountersImpl.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Counters.sol"; - -contract CountersImpl { - using Counters for Counters.Counter; - - Counters.Counter private _counter; - - function current() public view returns (uint256) { - return _counter.current(); - } - - function increment() public { - _counter.increment(); - } - - function decrement() public { - _counter.decrement(); - } - - function reset() public { - _counter.reset(); - } -} diff --git a/certora/munged/mocks/Create2Impl.sol b/certora/munged/mocks/Create2Impl.sol deleted file mode 100644 index 070ad3671..000000000 --- a/certora/munged/mocks/Create2Impl.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Create2.sol"; -import "../utils/introspection/ERC1820Implementer.sol"; - -contract Create2Impl { - function deploy( - uint256 value, - bytes32 salt, - bytes memory code - ) public { - Create2.deploy(value, salt, code); - } - - function deployERC1820Implementer(uint256 value, bytes32 salt) public { - Create2.deploy(value, salt, type(ERC1820Implementer).creationCode); - } - - function computeAddress(bytes32 salt, bytes32 codeHash) public view returns (address) { - return Create2.computeAddress(salt, codeHash); - } - - function computeAddressWithDeployer( - bytes32 salt, - bytes32 codeHash, - address deployer - ) public pure returns (address) { - return Create2.computeAddress(salt, codeHash, deployer); - } - - receive() external payable {} -} diff --git a/certora/munged/mocks/DummyImplementation.sol b/certora/munged/mocks/DummyImplementation.sol deleted file mode 100644 index d8651340d..000000000 --- a/certora/munged/mocks/DummyImplementation.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -abstract contract Impl { - function version() public pure virtual returns (string memory); -} - -contract DummyImplementation { - uint256 public value; - string public text; - uint256[] public values; - - function initializeNonPayable() public { - value = 10; - } - - function initializePayable() public payable { - value = 100; - } - - function initializeNonPayableWithValue(uint256 _value) public { - value = _value; - } - - function initializePayableWithValue(uint256 _value) public payable { - value = _value; - } - - function initialize( - uint256 _value, - string memory _text, - uint256[] memory _values - ) public { - value = _value; - text = _text; - values = _values; - } - - function get() public pure returns (bool) { - return true; - } - - function version() public pure virtual returns (string memory) { - return "V1"; - } - - function reverts() public pure { - require(false, "DummyImplementation reverted"); - } -} - -contract DummyImplementationV2 is DummyImplementation { - function migrate(uint256 newVal) public payable { - value = newVal; - } - - function version() public pure override returns (string memory) { - return "V2"; - } -} diff --git a/certora/munged/mocks/ECDSAMock.sol b/certora/munged/mocks/ECDSAMock.sol deleted file mode 100644 index 97bd46669..000000000 --- a/certora/munged/mocks/ECDSAMock.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/cryptography/ECDSA.sol"; - -contract ECDSAMock { - using ECDSA for bytes32; - using ECDSA for bytes; - - function recover(bytes32 hash, bytes memory signature) public pure returns (address) { - return hash.recover(signature); - } - - // solhint-disable-next-line func-name-mixedcase - function recover_v_r_s( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) public pure returns (address) { - return hash.recover(v, r, s); - } - - // solhint-disable-next-line func-name-mixedcase - function recover_r_vs( - bytes32 hash, - bytes32 r, - bytes32 vs - ) public pure returns (address) { - return hash.recover(r, vs); - } - - function toEthSignedMessageHash(bytes32 hash) public pure returns (bytes32) { - return hash.toEthSignedMessageHash(); - } - - function toEthSignedMessageHash(bytes memory s) public pure returns (bytes32) { - return s.toEthSignedMessageHash(); - } -} diff --git a/certora/munged/mocks/EIP712External.sol b/certora/munged/mocks/EIP712External.sol deleted file mode 100644 index 6f2446900..000000000 --- a/certora/munged/mocks/EIP712External.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/cryptography/draft-EIP712.sol"; -import "../utils/cryptography/ECDSA.sol"; - -contract EIP712External is EIP712 { - constructor(string memory name, string memory version) EIP712(name, version) {} - - function domainSeparator() external view returns (bytes32) { - return _domainSeparatorV4(); - } - - function verify( - bytes memory signature, - address signer, - address mailTo, - string memory mailContents - ) external view { - bytes32 digest = _hashTypedDataV4( - keccak256(abi.encode(keccak256("Mail(address to,string contents)"), mailTo, keccak256(bytes(mailContents)))) - ); - address recoveredSigner = ECDSA.recover(digest, signature); - require(recoveredSigner == signer); - } - - function getChainId() external view returns (uint256) { - return block.chainid; - } -} diff --git a/certora/munged/mocks/ERC1155BurnableMock.sol b/certora/munged/mocks/ERC1155BurnableMock.sol deleted file mode 100644 index 62138f28d..000000000 --- a/certora/munged/mocks/ERC1155BurnableMock.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC1155/extensions/ERC1155Burnable.sol"; - -contract ERC1155BurnableMock is ERC1155Burnable { - constructor(string memory uri) ERC1155(uri) {} - - function mint( - address to, - uint256 id, - uint256 value, - bytes memory data - ) public { - _mint(to, id, value, data); - } -} diff --git a/certora/munged/mocks/ERC1155Mock.sol b/certora/munged/mocks/ERC1155Mock.sol deleted file mode 100644 index 0518ac26c..000000000 --- a/certora/munged/mocks/ERC1155Mock.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC1155/ERC1155.sol"; - -/** - * @title ERC1155Mock - * This mock just publicizes internal functions for testing purposes - */ -contract ERC1155Mock is ERC1155 { - constructor(string memory uri) ERC1155(uri) {} - - function setURI(string memory newuri) public { - _setURI(newuri); - } - - function mint( - address to, - uint256 id, - uint256 value, - bytes memory data - ) public { - _mint(to, id, value, data); - } - - function mintBatch( - address to, - uint256[] memory ids, - uint256[] memory values, - bytes memory data - ) public { - _mintBatch(to, ids, values, data); - } - - function burn( - address owner, - uint256 id, - uint256 value - ) public { - _burn(owner, id, value); - } - - function burnBatch( - address owner, - uint256[] memory ids, - uint256[] memory values - ) public { - _burnBatch(owner, ids, values); - } -} diff --git a/certora/munged/mocks/ERC1155PausableMock.sol b/certora/munged/mocks/ERC1155PausableMock.sol deleted file mode 100644 index b1a4a8e1e..000000000 --- a/certora/munged/mocks/ERC1155PausableMock.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./ERC1155Mock.sol"; -import "../token/ERC1155/extensions/ERC1155Pausable.sol"; - -contract ERC1155PausableMock is ERC1155Mock, ERC1155Pausable { - constructor(string memory uri) ERC1155Mock(uri) {} - - function pause() external { - _pause(); - } - - function unpause() external { - _unpause(); - } - - function _beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual override(ERC1155, ERC1155Pausable) { - super._beforeTokenTransfer(operator, from, to, ids, amounts, data); - } -} diff --git a/certora/munged/mocks/ERC1155ReceiverMock.sol b/certora/munged/mocks/ERC1155ReceiverMock.sol deleted file mode 100644 index 6443a56c7..000000000 --- a/certora/munged/mocks/ERC1155ReceiverMock.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC1155/IERC1155Receiver.sol"; -import "../utils/introspection/ERC165.sol"; - -contract ERC1155ReceiverMock is ERC165, IERC1155Receiver { - bytes4 private _recRetval; - bool private _recReverts; - bytes4 private _batRetval; - bool private _batReverts; - - event Received(address operator, address from, uint256 id, uint256 value, bytes data, uint256 gas); - event BatchReceived(address operator, address from, uint256[] ids, uint256[] values, bytes data, uint256 gas); - - constructor( - bytes4 recRetval, - bool recReverts, - bytes4 batRetval, - bool batReverts - ) { - _recRetval = recRetval; - _recReverts = recReverts; - _batRetval = batRetval; - _batReverts = batReverts; - } - - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes calldata data - ) external override returns (bytes4) { - require(!_recReverts, "ERC1155ReceiverMock: reverting on receive"); - emit Received(operator, from, id, value, data, gasleft()); - return _recRetval; - } - - function onERC1155BatchReceived( - address operator, - address from, - uint256[] calldata ids, - uint256[] calldata values, - bytes calldata data - ) external override returns (bytes4) { - require(!_batReverts, "ERC1155ReceiverMock: reverting on batch receive"); - emit BatchReceived(operator, from, ids, values, data, gasleft()); - return _batRetval; - } -} diff --git a/certora/munged/mocks/ERC1155SupplyMock.sol b/certora/munged/mocks/ERC1155SupplyMock.sol deleted file mode 100644 index 44b208007..000000000 --- a/certora/munged/mocks/ERC1155SupplyMock.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./ERC1155Mock.sol"; -import "../token/ERC1155/extensions/ERC1155Supply.sol"; - -contract ERC1155SupplyMock is ERC1155Mock, ERC1155Supply { - constructor(string memory uri) ERC1155Mock(uri) {} - - function _beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual override(ERC1155, ERC1155Supply) { - super._beforeTokenTransfer(operator, from, to, ids, amounts, data); - } -} diff --git a/certora/munged/mocks/ERC1271WalletMock.sol b/certora/munged/mocks/ERC1271WalletMock.sol deleted file mode 100644 index c92acdba6..000000000 --- a/certora/munged/mocks/ERC1271WalletMock.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../access/Ownable.sol"; -import "../interfaces/IERC1271.sol"; -import "../utils/cryptography/ECDSA.sol"; - -contract ERC1271WalletMock is Ownable, IERC1271 { - constructor(address originalOwner) { - transferOwnership(originalOwner); - } - - function isValidSignature(bytes32 hash, bytes memory signature) public view override returns (bytes4 magicValue) { - return ECDSA.recover(hash, signature) == owner() ? this.isValidSignature.selector : bytes4(0); - } -} diff --git a/certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol b/certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol deleted file mode 100644 index 7a5e5bc67..000000000 --- a/certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../../utils/introspection/IERC165.sol"; - -/** - * https://eips.ethereum.org/EIPS/eip-214#specification - * From the specification: - * > Any attempts to make state-changing operations inside an execution instance with STATIC set to true will instead - * throw an exception. - * > These operations include [...], LOG0, LOG1, LOG2, [...] - * - * therefore, because this contract is staticcall'd we need to not emit events (which is how solidity-coverage works) - * solidity-coverage ignores the /mocks folder, so we duplicate its implementation here to avoid instrumenting it - */ -contract SupportsInterfaceWithLookupMock is IERC165 { - /* - * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 - */ - bytes4 public constant INTERFACE_ID_ERC165 = 0x01ffc9a7; - - /** - * @dev A mapping of interface id to whether or not it's supported. - */ - mapping(bytes4 => bool) private _supportedInterfaces; - - /** - * @dev A contract implementing SupportsInterfaceWithLookup - * implement ERC165 itself. - */ - constructor() { - _registerInterface(INTERFACE_ID_ERC165); - } - - /** - * @dev Implement supportsInterface(bytes4) using a lookup table. - */ - function supportsInterface(bytes4 interfaceId) public view override returns (bool) { - return _supportedInterfaces[interfaceId]; - } - - /** - * @dev Private method for registering an interface. - */ - function _registerInterface(bytes4 interfaceId) internal { - require(interfaceId != 0xffffffff, "ERC165InterfacesSupported: invalid interface id"); - _supportedInterfaces[interfaceId] = true; - } -} - -contract ERC165InterfacesSupported is SupportsInterfaceWithLookupMock { - constructor(bytes4[] memory interfaceIds) { - for (uint256 i = 0; i < interfaceIds.length; i++) { - _registerInterface(interfaceIds[i]); - } - } -} diff --git a/certora/munged/mocks/ERC165/ERC165MissingData.sol b/certora/munged/mocks/ERC165/ERC165MissingData.sol deleted file mode 100644 index 59cd51ae6..000000000 --- a/certora/munged/mocks/ERC165/ERC165MissingData.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -contract ERC165MissingData { - function supportsInterface(bytes4 interfaceId) public view {} // missing return -} diff --git a/certora/munged/mocks/ERC165/ERC165NotSupported.sol b/certora/munged/mocks/ERC165/ERC165NotSupported.sol deleted file mode 100644 index 486c7f0a4..000000000 --- a/certora/munged/mocks/ERC165/ERC165NotSupported.sol +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -contract ERC165NotSupported {} diff --git a/certora/munged/mocks/ERC165CheckerMock.sol b/certora/munged/mocks/ERC165CheckerMock.sol deleted file mode 100644 index bda5cfc78..000000000 --- a/certora/munged/mocks/ERC165CheckerMock.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/introspection/ERC165Checker.sol"; - -contract ERC165CheckerMock { - using ERC165Checker for address; - - function supportsERC165(address account) public view returns (bool) { - return account.supportsERC165(); - } - - function supportsInterface(address account, bytes4 interfaceId) public view returns (bool) { - return account.supportsInterface(interfaceId); - } - - function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool) { - return account.supportsAllInterfaces(interfaceIds); - } - - function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool[] memory) { - return account.getSupportedInterfaces(interfaceIds); - } -} diff --git a/certora/munged/mocks/ERC165Mock.sol b/certora/munged/mocks/ERC165Mock.sol deleted file mode 100644 index c123d0ab2..000000000 --- a/certora/munged/mocks/ERC165Mock.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/introspection/ERC165.sol"; - -contract ERC165Mock is ERC165 {} diff --git a/certora/munged/mocks/ERC165StorageMock.sol b/certora/munged/mocks/ERC165StorageMock.sol deleted file mode 100644 index 4b0bae908..000000000 --- a/certora/munged/mocks/ERC165StorageMock.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/introspection/ERC165Storage.sol"; - -contract ERC165StorageMock is ERC165Storage { - function registerInterface(bytes4 interfaceId) public { - _registerInterface(interfaceId); - } -} diff --git a/certora/munged/mocks/ERC1820ImplementerMock.sol b/certora/munged/mocks/ERC1820ImplementerMock.sol deleted file mode 100644 index a6012d7ff..000000000 --- a/certora/munged/mocks/ERC1820ImplementerMock.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/introspection/ERC1820Implementer.sol"; - -contract ERC1820ImplementerMock is ERC1820Implementer { - function registerInterfaceForAddress(bytes32 interfaceHash, address account) public { - _registerInterfaceForAddress(interfaceHash, account); - } -} diff --git a/certora/munged/mocks/ERC20BurnableMock.sol b/certora/munged/mocks/ERC20BurnableMock.sol deleted file mode 100644 index 0ed6c0c98..000000000 --- a/certora/munged/mocks/ERC20BurnableMock.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/ERC20Burnable.sol"; - -contract ERC20BurnableMock is ERC20Burnable { - constructor( - string memory name, - string memory symbol, - address initialAccount, - uint256 initialBalance - ) ERC20(name, symbol) { - _mint(initialAccount, initialBalance); - } -} diff --git a/certora/munged/mocks/ERC20CappedMock.sol b/certora/munged/mocks/ERC20CappedMock.sol deleted file mode 100644 index edb36f205..000000000 --- a/certora/munged/mocks/ERC20CappedMock.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/ERC20Capped.sol"; - -contract ERC20CappedMock is ERC20Capped { - constructor( - string memory name, - string memory symbol, - uint256 cap - ) ERC20(name, symbol) ERC20Capped(cap) {} - - function mint(address to, uint256 tokenId) public { - _mint(to, tokenId); - } -} diff --git a/certora/munged/mocks/ERC20DecimalsMock.sol b/certora/munged/mocks/ERC20DecimalsMock.sol deleted file mode 100644 index 924c3af31..000000000 --- a/certora/munged/mocks/ERC20DecimalsMock.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/ERC20.sol"; - -contract ERC20DecimalsMock is ERC20 { - uint8 private immutable _decimals; - - constructor( - string memory name_, - string memory symbol_, - uint8 decimals_ - ) ERC20(name_, symbol_) { - _decimals = decimals_; - } - - function decimals() public view virtual override returns (uint8) { - return _decimals; - } -} diff --git a/certora/munged/mocks/ERC20FlashMintMock.sol b/certora/munged/mocks/ERC20FlashMintMock.sol deleted file mode 100644 index 0bb7871fc..000000000 --- a/certora/munged/mocks/ERC20FlashMintMock.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/ERC20FlashMint.sol"; - -contract ERC20FlashMintMock is ERC20FlashMint { - constructor( - string memory name, - string memory symbol, - address initialAccount, - uint256 initialBalance - ) ERC20(name, symbol) { - _mint(initialAccount, initialBalance); - } -} diff --git a/certora/munged/mocks/ERC20Mock.sol b/certora/munged/mocks/ERC20Mock.sol deleted file mode 100644 index fd7f991ba..000000000 --- a/certora/munged/mocks/ERC20Mock.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/ERC20.sol"; - -// mock class using ERC20 -contract ERC20Mock is ERC20 { - constructor( - string memory name, - string memory symbol, - address initialAccount, - uint256 initialBalance - ) payable ERC20(name, symbol) { - _mint(initialAccount, initialBalance); - } - - function mint(address account, uint256 amount) public { - _mint(account, amount); - } - - function burn(address account, uint256 amount) public { - _burn(account, amount); - } - - function transferInternal( - address from, - address to, - uint256 value - ) public { - _transfer(from, to, value); - } - - function approveInternal( - address owner, - address spender, - uint256 value - ) public { - _approve(owner, spender, value); - } -} diff --git a/certora/munged/mocks/ERC20PausableMock.sol b/certora/munged/mocks/ERC20PausableMock.sol deleted file mode 100644 index 19160ba6c..000000000 --- a/certora/munged/mocks/ERC20PausableMock.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/ERC20Pausable.sol"; - -// mock class using ERC20Pausable -contract ERC20PausableMock is ERC20Pausable { - constructor( - string memory name, - string memory symbol, - address initialAccount, - uint256 initialBalance - ) ERC20(name, symbol) { - _mint(initialAccount, initialBalance); - } - - function pause() external { - _pause(); - } - - function unpause() external { - _unpause(); - } - - function mint(address to, uint256 amount) public { - _mint(to, amount); - } - - function burn(address from, uint256 amount) public { - _burn(from, amount); - } -} diff --git a/certora/munged/mocks/ERC20PermitMock.sol b/certora/munged/mocks/ERC20PermitMock.sol deleted file mode 100644 index 20302bfa0..000000000 --- a/certora/munged/mocks/ERC20PermitMock.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/draft-ERC20Permit.sol"; - -contract ERC20PermitMock is ERC20Permit { - constructor( - string memory name, - string memory symbol, - address initialAccount, - uint256 initialBalance - ) payable ERC20(name, symbol) ERC20Permit(name) { - _mint(initialAccount, initialBalance); - } - - function getChainId() external view returns (uint256) { - return block.chainid; - } -} diff --git a/certora/munged/mocks/ERC20SnapshotMock.sol b/certora/munged/mocks/ERC20SnapshotMock.sol deleted file mode 100644 index cb3048322..000000000 --- a/certora/munged/mocks/ERC20SnapshotMock.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/ERC20Snapshot.sol"; - -contract ERC20SnapshotMock is ERC20Snapshot { - constructor( - string memory name, - string memory symbol, - address initialAccount, - uint256 initialBalance - ) ERC20(name, symbol) { - _mint(initialAccount, initialBalance); - } - - function snapshot() public { - _snapshot(); - } - - function mint(address account, uint256 amount) public { - _mint(account, amount); - } - - function burn(address account, uint256 amount) public { - _burn(account, amount); - } -} diff --git a/certora/munged/mocks/ERC20VotesCompMock.sol b/certora/munged/mocks/ERC20VotesCompMock.sol deleted file mode 100644 index 171071fd5..000000000 --- a/certora/munged/mocks/ERC20VotesCompMock.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/ERC20VotesComp.sol"; - -contract ERC20VotesCompMock is ERC20VotesComp { - constructor(string memory name, string memory symbol) ERC20(name, symbol) ERC20Permit(name) {} - - function mint(address account, uint256 amount) public { - _mint(account, amount); - } - - function burn(address account, uint256 amount) public { - _burn(account, amount); - } - - function getChainId() external view returns (uint256) { - return block.chainid; - } -} diff --git a/certora/munged/mocks/ERC20VotesMock.sol b/certora/munged/mocks/ERC20VotesMock.sol deleted file mode 100644 index 0975e8b9f..000000000 --- a/certora/munged/mocks/ERC20VotesMock.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/ERC20Votes.sol"; - -contract ERC20VotesMock is ERC20Votes { - constructor(string memory name, string memory symbol) ERC20(name, symbol) ERC20Permit(name) {} - - function mint(address account, uint256 amount) public { - _mint(account, amount); - } - - function burn(address account, uint256 amount) public { - _burn(account, amount); - } - - function getChainId() external view returns (uint256) { - return block.chainid; - } -} diff --git a/certora/munged/mocks/ERC20WrapperMock.sol b/certora/munged/mocks/ERC20WrapperMock.sol deleted file mode 100644 index cf34a7a52..000000000 --- a/certora/munged/mocks/ERC20WrapperMock.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/extensions/ERC20Wrapper.sol"; - -contract ERC20WrapperMock is ERC20Wrapper { - constructor( - IERC20 _underlyingToken, - string memory name, - string memory symbol - ) ERC20(name, symbol) ERC20Wrapper(_underlyingToken) {} - - function recover(address account) public returns (uint256) { - return _recover(account); - } -} diff --git a/certora/munged/mocks/ERC2771ContextMock.sol b/certora/munged/mocks/ERC2771ContextMock.sol deleted file mode 100644 index 7bc1c4538..000000000 --- a/certora/munged/mocks/ERC2771ContextMock.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./ContextMock.sol"; -import "../metatx/ERC2771Context.sol"; - -// By inheriting from ERC2771Context, Context's internal functions are overridden automatically -contract ERC2771ContextMock is ContextMock, ERC2771Context { - constructor(address trustedForwarder) ERC2771Context(trustedForwarder) {} - - function _msgSender() internal view virtual override(Context, ERC2771Context) returns (address) { - return ERC2771Context._msgSender(); - } - - function _msgData() internal view virtual override(Context, ERC2771Context) returns (bytes calldata) { - return ERC2771Context._msgData(); - } -} diff --git a/certora/munged/mocks/ERC3156FlashBorrowerMock.sol b/certora/munged/mocks/ERC3156FlashBorrowerMock.sol deleted file mode 100644 index 288a278fb..000000000 --- a/certora/munged/mocks/ERC3156FlashBorrowerMock.sol +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC20/IERC20.sol"; -import "../interfaces/IERC3156.sol"; -import "../utils/Address.sol"; - -/** - * @dev WARNING: this IERC3156FlashBorrower mock implementation is for testing purposes ONLY. - * Writing a secure flash lock borrower is not an easy task, and should be done with the utmost care. - * This is not an example of how it should be done, and no pattern present in this mock should be considered secure. - * Following best practices, always have your contract properly audited before using them to manipulate important funds on - * live networks. - */ -contract ERC3156FlashBorrowerMock is IERC3156FlashBorrower { - bytes32 internal constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); - - bool immutable _enableApprove; - bool immutable _enableReturn; - - event BalanceOf(address token, address account, uint256 value); - event TotalSupply(address token, uint256 value); - - constructor(bool enableReturn, bool enableApprove) { - _enableApprove = enableApprove; - _enableReturn = enableReturn; - } - - function onFlashLoan( - address, /*initiator*/ - address token, - uint256 amount, - uint256 fee, - bytes calldata data - ) public override returns (bytes32) { - require(msg.sender == token); - - emit BalanceOf(token, address(this), IERC20(token).balanceOf(address(this))); - emit TotalSupply(token, IERC20(token).totalSupply()); - - if (data.length > 0) { - // WARNING: This code is for testing purposes only! Do not use. - Address.functionCall(token, data); - } - - if (_enableApprove) { - IERC20(token).approve(token, amount + fee); - } - - return _enableReturn ? _RETURN_VALUE : bytes32(0); - } -} diff --git a/certora/munged/mocks/ERC721BurnableMock.sol b/certora/munged/mocks/ERC721BurnableMock.sol deleted file mode 100644 index b30dbf53d..000000000 --- a/certora/munged/mocks/ERC721BurnableMock.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC721/extensions/ERC721Burnable.sol"; - -contract ERC721BurnableMock is ERC721Burnable { - constructor(string memory name, string memory symbol) ERC721(name, symbol) {} - - function exists(uint256 tokenId) public view returns (bool) { - return _exists(tokenId); - } - - function mint(address to, uint256 tokenId) public { - _mint(to, tokenId); - } - - function safeMint(address to, uint256 tokenId) public { - _safeMint(to, tokenId); - } - - function safeMint( - address to, - uint256 tokenId, - bytes memory _data - ) public { - _safeMint(to, tokenId, _data); - } -} diff --git a/certora/munged/mocks/ERC721EnumerableMock.sol b/certora/munged/mocks/ERC721EnumerableMock.sol deleted file mode 100644 index 73aee9d04..000000000 --- a/certora/munged/mocks/ERC721EnumerableMock.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC721/extensions/ERC721Enumerable.sol"; - -/** - * @title ERC721Mock - * This mock just provides a public safeMint, mint, and burn functions for testing purposes - */ -contract ERC721EnumerableMock is ERC721Enumerable { - string private _baseTokenURI; - - constructor(string memory name, string memory symbol) ERC721(name, symbol) {} - - function _baseURI() internal view virtual override returns (string memory) { - return _baseTokenURI; - } - - function setBaseURI(string calldata newBaseTokenURI) public { - _baseTokenURI = newBaseTokenURI; - } - - function baseURI() public view returns (string memory) { - return _baseURI(); - } - - function exists(uint256 tokenId) public view returns (bool) { - return _exists(tokenId); - } - - function mint(address to, uint256 tokenId) public { - _mint(to, tokenId); - } - - function safeMint(address to, uint256 tokenId) public { - _safeMint(to, tokenId); - } - - function safeMint( - address to, - uint256 tokenId, - bytes memory _data - ) public { - _safeMint(to, tokenId, _data); - } - - function burn(uint256 tokenId) public { - _burn(tokenId); - } -} diff --git a/certora/munged/mocks/ERC721Mock.sol b/certora/munged/mocks/ERC721Mock.sol deleted file mode 100644 index 74a092334..000000000 --- a/certora/munged/mocks/ERC721Mock.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC721/ERC721.sol"; - -/** - * @title ERC721Mock - * This mock just provides a public safeMint, mint, and burn functions for testing purposes - */ -contract ERC721Mock is ERC721 { - constructor(string memory name, string memory symbol) ERC721(name, symbol) {} - - function baseURI() public view returns (string memory) { - return _baseURI(); - } - - function exists(uint256 tokenId) public view returns (bool) { - return _exists(tokenId); - } - - function mint(address to, uint256 tokenId) public { - _mint(to, tokenId); - } - - function safeMint(address to, uint256 tokenId) public { - _safeMint(to, tokenId); - } - - function safeMint( - address to, - uint256 tokenId, - bytes memory _data - ) public { - _safeMint(to, tokenId, _data); - } - - function burn(uint256 tokenId) public { - _burn(tokenId); - } -} diff --git a/certora/munged/mocks/ERC721PausableMock.sol b/certora/munged/mocks/ERC721PausableMock.sol deleted file mode 100644 index 8d8e818fb..000000000 --- a/certora/munged/mocks/ERC721PausableMock.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC721/extensions/ERC721Pausable.sol"; - -/** - * @title ERC721PausableMock - * This mock just provides a public mint, burn and exists functions for testing purposes - */ -contract ERC721PausableMock is ERC721Pausable { - constructor(string memory name, string memory symbol) ERC721(name, symbol) {} - - function pause() external { - _pause(); - } - - function unpause() external { - _unpause(); - } - - function exists(uint256 tokenId) public view returns (bool) { - return _exists(tokenId); - } - - function mint(address to, uint256 tokenId) public { - _mint(to, tokenId); - } - - function safeMint(address to, uint256 tokenId) public { - _safeMint(to, tokenId); - } - - function safeMint( - address to, - uint256 tokenId, - bytes memory _data - ) public { - _safeMint(to, tokenId, _data); - } - - function burn(uint256 tokenId) public { - _burn(tokenId); - } -} diff --git a/certora/munged/mocks/ERC721ReceiverMock.sol b/certora/munged/mocks/ERC721ReceiverMock.sol deleted file mode 100644 index a4923bfd5..000000000 --- a/certora/munged/mocks/ERC721ReceiverMock.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC721/IERC721Receiver.sol"; - -contract ERC721ReceiverMock is IERC721Receiver { - enum Error { - None, - RevertWithMessage, - RevertWithoutMessage, - Panic - } - - bytes4 private immutable _retval; - Error private immutable _error; - - event Received(address operator, address from, uint256 tokenId, bytes data, uint256 gas); - - constructor(bytes4 retval, Error error) { - _retval = retval; - _error = error; - } - - function onERC721Received( - address operator, - address from, - uint256 tokenId, - bytes memory data - ) public override returns (bytes4) { - if (_error == Error.RevertWithMessage) { - revert("ERC721ReceiverMock: reverting"); - } else if (_error == Error.RevertWithoutMessage) { - revert(); - } else if (_error == Error.Panic) { - uint256 a = uint256(0) / uint256(0); - a; - } - emit Received(operator, from, tokenId, data, gasleft()); - return _retval; - } -} diff --git a/certora/munged/mocks/ERC721URIStorageMock.sol b/certora/munged/mocks/ERC721URIStorageMock.sol deleted file mode 100644 index 9c3480f71..000000000 --- a/certora/munged/mocks/ERC721URIStorageMock.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC721/extensions/ERC721URIStorage.sol"; - -/** - * @title ERC721Mock - * This mock just provides a public safeMint, mint, and burn functions for testing purposes - */ -contract ERC721URIStorageMock is ERC721URIStorage { - string private _baseTokenURI; - - constructor(string memory name, string memory symbol) ERC721(name, symbol) {} - - function _baseURI() internal view virtual override returns (string memory) { - return _baseTokenURI; - } - - function setBaseURI(string calldata newBaseTokenURI) public { - _baseTokenURI = newBaseTokenURI; - } - - function baseURI() public view returns (string memory) { - return _baseURI(); - } - - function setTokenURI(uint256 tokenId, string memory _tokenURI) public { - _setTokenURI(tokenId, _tokenURI); - } - - function exists(uint256 tokenId) public view returns (bool) { - return _exists(tokenId); - } - - function mint(address to, uint256 tokenId) public { - _mint(to, tokenId); - } - - function safeMint(address to, uint256 tokenId) public { - _safeMint(to, tokenId); - } - - function safeMint( - address to, - uint256 tokenId, - bytes memory _data - ) public { - _safeMint(to, tokenId, _data); - } - - function burn(uint256 tokenId) public { - _burn(tokenId); - } -} diff --git a/certora/munged/mocks/ERC777Mock.sol b/certora/munged/mocks/ERC777Mock.sol deleted file mode 100644 index f8a3b6784..000000000 --- a/certora/munged/mocks/ERC777Mock.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; -import "../token/ERC777/ERC777.sol"; - -contract ERC777Mock is Context, ERC777 { - event BeforeTokenTransfer(); - - constructor( - address initialHolder, - uint256 initialBalance, - string memory name, - string memory symbol, - address[] memory defaultOperators - ) ERC777(name, symbol, defaultOperators) { - _mint(initialHolder, initialBalance, "", ""); - } - - function mintInternal( - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData - ) public { - _mint(to, amount, userData, operatorData); - } - - function mintInternalExtended( - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData, - bool requireReceptionAck - ) public { - _mint(to, amount, userData, operatorData, requireReceptionAck); - } - - function approveInternal( - address holder, - address spender, - uint256 value - ) public { - _approve(holder, spender, value); - } - - function _beforeTokenTransfer( - address, - address, - address, - uint256 - ) internal override { - emit BeforeTokenTransfer(); - } -} diff --git a/certora/munged/mocks/ERC777SenderRecipientMock.sol b/certora/munged/mocks/ERC777SenderRecipientMock.sol deleted file mode 100644 index 169912f69..000000000 --- a/certora/munged/mocks/ERC777SenderRecipientMock.sol +++ /dev/null @@ -1,161 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../token/ERC777/IERC777.sol"; -import "../token/ERC777/IERC777Sender.sol"; -import "../token/ERC777/IERC777Recipient.sol"; -import "../utils/Context.sol"; -import "../utils/introspection/IERC1820Registry.sol"; -import "../utils/introspection/ERC1820Implementer.sol"; - -contract ERC777SenderRecipientMock is Context, IERC777Sender, IERC777Recipient, ERC1820Implementer { - event TokensToSendCalled( - address operator, - address from, - address to, - uint256 amount, - bytes data, - bytes operatorData, - address token, - uint256 fromBalance, - uint256 toBalance - ); - - event TokensReceivedCalled( - address operator, - address from, - address to, - uint256 amount, - bytes data, - bytes operatorData, - address token, - uint256 fromBalance, - uint256 toBalance - ); - - // Emitted in ERC777Mock. Here for easier decoding - event BeforeTokenTransfer(); - - bool private _shouldRevertSend; - bool private _shouldRevertReceive; - - IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - - bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); - bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); - - function tokensToSend( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external override { - if (_shouldRevertSend) { - revert(); - } - - IERC777 token = IERC777(_msgSender()); - - uint256 fromBalance = token.balanceOf(from); - // when called due to burn, to will be the zero address, which will have a balance of 0 - uint256 toBalance = token.balanceOf(to); - - emit TokensToSendCalled( - operator, - from, - to, - amount, - userData, - operatorData, - address(token), - fromBalance, - toBalance - ); - } - - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external override { - if (_shouldRevertReceive) { - revert(); - } - - IERC777 token = IERC777(_msgSender()); - - uint256 fromBalance = token.balanceOf(from); - // when called due to burn, to will be the zero address, which will have a balance of 0 - uint256 toBalance = token.balanceOf(to); - - emit TokensReceivedCalled( - operator, - from, - to, - amount, - userData, - operatorData, - address(token), - fromBalance, - toBalance - ); - } - - function senderFor(address account) public { - _registerInterfaceForAddress(_TOKENS_SENDER_INTERFACE_HASH, account); - - address self = address(this); - if (account == self) { - registerSender(self); - } - } - - function registerSender(address sender) public { - _erc1820.setInterfaceImplementer(address(this), _TOKENS_SENDER_INTERFACE_HASH, sender); - } - - function recipientFor(address account) public { - _registerInterfaceForAddress(_TOKENS_RECIPIENT_INTERFACE_HASH, account); - - address self = address(this); - if (account == self) { - registerRecipient(self); - } - } - - function registerRecipient(address recipient) public { - _erc1820.setInterfaceImplementer(address(this), _TOKENS_RECIPIENT_INTERFACE_HASH, recipient); - } - - function setShouldRevertSend(bool shouldRevert) public { - _shouldRevertSend = shouldRevert; - } - - function setShouldRevertReceive(bool shouldRevert) public { - _shouldRevertReceive = shouldRevert; - } - - function send( - IERC777 token, - address to, - uint256 amount, - bytes memory data - ) public { - // This is 777's send function, not the Solidity send function - token.send(to, amount, data); // solhint-disable-line check-send-result - } - - function burn( - IERC777 token, - uint256 amount, - bytes memory data - ) public { - token.burn(amount, data); - } -} diff --git a/certora/munged/mocks/EnumerableMapMock.sol b/certora/munged/mocks/EnumerableMapMock.sol deleted file mode 100644 index 510647b58..000000000 --- a/certora/munged/mocks/EnumerableMapMock.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/structs/EnumerableMap.sol"; - -contract EnumerableMapMock { - using EnumerableMap for EnumerableMap.UintToAddressMap; - - event OperationResult(bool result); - - EnumerableMap.UintToAddressMap private _map; - - function contains(uint256 key) public view returns (bool) { - return _map.contains(key); - } - - function set(uint256 key, address value) public { - bool result = _map.set(key, value); - emit OperationResult(result); - } - - function remove(uint256 key) public { - bool result = _map.remove(key); - emit OperationResult(result); - } - - function length() public view returns (uint256) { - return _map.length(); - } - - function at(uint256 index) public view returns (uint256 key, address value) { - return _map.at(index); - } - - function tryGet(uint256 key) public view returns (bool, address) { - return _map.tryGet(key); - } - - function get(uint256 key) public view returns (address) { - return _map.get(key); - } - - function getWithMessage(uint256 key, string calldata errorMessage) public view returns (address) { - return _map.get(key, errorMessage); - } -} diff --git a/certora/munged/mocks/EnumerableSetMock.sol b/certora/munged/mocks/EnumerableSetMock.sol deleted file mode 100644 index 922ce46d2..000000000 --- a/certora/munged/mocks/EnumerableSetMock.sol +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/structs/EnumerableSet.sol"; - -// Bytes32Set -contract EnumerableBytes32SetMock { - using EnumerableSet for EnumerableSet.Bytes32Set; - - event OperationResult(bool result); - - EnumerableSet.Bytes32Set private _set; - - function contains(bytes32 value) public view returns (bool) { - return _set.contains(value); - } - - function add(bytes32 value) public { - bool result = _set.add(value); - emit OperationResult(result); - } - - function remove(bytes32 value) public { - bool result = _set.remove(value); - emit OperationResult(result); - } - - function length() public view returns (uint256) { - return _set.length(); - } - - function at(uint256 index) public view returns (bytes32) { - return _set.at(index); - } - - function values() public view returns (bytes32[] memory) { - return _set.values(); - } -} - -// AddressSet -contract EnumerableAddressSetMock { - using EnumerableSet for EnumerableSet.AddressSet; - - event OperationResult(bool result); - - EnumerableSet.AddressSet private _set; - - function contains(address value) public view returns (bool) { - return _set.contains(value); - } - - function add(address value) public { - bool result = _set.add(value); - emit OperationResult(result); - } - - function remove(address value) public { - bool result = _set.remove(value); - emit OperationResult(result); - } - - function length() public view returns (uint256) { - return _set.length(); - } - - function at(uint256 index) public view returns (address) { - return _set.at(index); - } - - function values() public view returns (address[] memory) { - return _set.values(); - } -} - -// UintSet -contract EnumerableUintSetMock { - using EnumerableSet for EnumerableSet.UintSet; - - event OperationResult(bool result); - - EnumerableSet.UintSet private _set; - - function contains(uint256 value) public view returns (bool) { - return _set.contains(value); - } - - function add(uint256 value) public { - bool result = _set.add(value); - emit OperationResult(result); - } - - function remove(uint256 value) public { - bool result = _set.remove(value); - emit OperationResult(result); - } - - function length() public view returns (uint256) { - return _set.length(); - } - - function at(uint256 index) public view returns (uint256) { - return _set.at(index); - } - - function values() public view returns (uint256[] memory) { - return _set.values(); - } -} diff --git a/certora/munged/mocks/EtherReceiverMock.sol b/certora/munged/mocks/EtherReceiverMock.sol deleted file mode 100644 index a11e646fb..000000000 --- a/certora/munged/mocks/EtherReceiverMock.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -contract EtherReceiverMock { - bool private _acceptEther; - - function setAcceptEther(bool acceptEther) public { - _acceptEther = acceptEther; - } - - receive() external payable { - if (!_acceptEther) { - revert(); - } - } -} diff --git a/certora/munged/mocks/GovernorCompMock.sol b/certora/munged/mocks/GovernorCompMock.sol deleted file mode 100644 index 9dcbc536d..000000000 --- a/certora/munged/mocks/GovernorCompMock.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../governance/extensions/GovernorCountingSimple.sol"; -import "../governance/extensions/GovernorVotesComp.sol"; - -contract GovernorCompMock is GovernorVotesComp, GovernorCountingSimple { - constructor(string memory name_, ERC20VotesComp token_) Governor(name_) GovernorVotesComp(token_) {} - - function quorum(uint256) public pure override returns (uint256) { - return 0; - } - - function votingDelay() public pure override returns (uint256) { - return 4; - } - - function votingPeriod() public pure override returns (uint256) { - return 16; - } - - function cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 salt - ) public returns (uint256 proposalId) { - return _cancel(targets, values, calldatas, salt); - } - - function getVotes(address account, uint256 blockNumber) - public - view - virtual - override(IGovernor, GovernorVotesComp) - returns (uint256) - { - return super.getVotes(account, blockNumber); - } -} diff --git a/certora/munged/mocks/GovernorCompatibilityBravoMock.sol b/certora/munged/mocks/GovernorCompatibilityBravoMock.sol deleted file mode 100644 index 60afbb918..000000000 --- a/certora/munged/mocks/GovernorCompatibilityBravoMock.sol +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../governance/compatibility/GovernorCompatibilityBravo.sol"; -import "../governance/extensions/GovernorTimelockCompound.sol"; -import "../governance/extensions/GovernorSettings.sol"; -import "../governance/extensions/GovernorVotesComp.sol"; - -contract GovernorCompatibilityBravoMock is - GovernorCompatibilityBravo, - GovernorSettings, - GovernorTimelockCompound, - GovernorVotesComp -{ - constructor( - string memory name_, - ERC20VotesComp token_, - uint256 votingDelay_, - uint256 votingPeriod_, - uint256 proposalThreshold_, - ICompoundTimelock timelock_ - ) - Governor(name_) - GovernorTimelockCompound(timelock_) - GovernorSettings(votingDelay_, votingPeriod_, proposalThreshold_) - GovernorVotesComp(token_) - {} - - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(IERC165, Governor, GovernorTimelockCompound) - returns (bool) - { - return super.supportsInterface(interfaceId); - } - - function quorum(uint256) public pure override returns (uint256) { - return 0; - } - - function state(uint256 proposalId) - public - view - virtual - override(IGovernor, Governor, GovernorTimelockCompound) - returns (ProposalState) - { - return super.state(proposalId); - } - - function proposalEta(uint256 proposalId) - public - view - virtual - override(IGovernorTimelock, GovernorTimelockCompound) - returns (uint256) - { - return super.proposalEta(proposalId); - } - - function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { - return super.proposalThreshold(); - } - - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public virtual override(IGovernor, Governor, GovernorCompatibilityBravo) returns (uint256) { - return super.propose(targets, values, calldatas, description); - } - - function queue( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 salt - ) public virtual override(IGovernorTimelock, GovernorTimelockCompound) returns (uint256) { - return super.queue(targets, values, calldatas, salt); - } - - function execute( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 salt - ) public payable virtual override(IGovernor, Governor) returns (uint256) { - return super.execute(targets, values, calldatas, salt); - } - - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal virtual override(Governor, GovernorTimelockCompound) { - super._execute(proposalId, targets, values, calldatas, descriptionHash); - } - - /** - * @notice WARNING: this is for mock purposes only. Ability to the _cancel function should be restricted for live - * deployments. - */ - function cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 salt - ) public returns (uint256 proposalId) { - return _cancel(targets, values, calldatas, salt); - } - - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 salt - ) internal virtual override(Governor, GovernorTimelockCompound) returns (uint256 proposalId) { - return super._cancel(targets, values, calldatas, salt); - } - - function getVotes(address account, uint256 blockNumber) - public - view - virtual - override(IGovernor, GovernorVotesComp) - returns (uint256) - { - return super.getVotes(account, blockNumber); - } - - function _executor() internal view virtual override(Governor, GovernorTimelockCompound) returns (address) { - return super._executor(); - } -} diff --git a/certora/munged/mocks/GovernorMock.sol b/certora/munged/mocks/GovernorMock.sol deleted file mode 100644 index cc96dcd27..000000000 --- a/certora/munged/mocks/GovernorMock.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../governance/extensions/GovernorProposalThreshold.sol"; -import "../governance/extensions/GovernorSettings.sol"; -import "../governance/extensions/GovernorCountingSimple.sol"; -import "../governance/extensions/GovernorVotesQuorumFraction.sol"; - -contract GovernorMock is - GovernorProposalThreshold, - GovernorSettings, - GovernorVotesQuorumFraction, - GovernorCountingSimple -{ - constructor( - string memory name_, - ERC20Votes token_, - uint256 votingDelay_, - uint256 votingPeriod_, - uint256 quorumNumerator_ - ) - Governor(name_) - GovernorSettings(votingDelay_, votingPeriod_, 0) - GovernorVotes(token_) - GovernorVotesQuorumFraction(quorumNumerator_) - {} - - function cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 salt - ) public returns (uint256 proposalId) { - return _cancel(targets, values, calldatas, salt); - } - - function getVotes(address account, uint256 blockNumber) - public - view - virtual - override(IGovernor, GovernorVotes) - returns (uint256) - { - return super.getVotes(account, blockNumber); - } - - function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { - return super.proposalThreshold(); - } - - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public virtual override(Governor, GovernorProposalThreshold) returns (uint256) { - return super.propose(targets, values, calldatas, description); - } -} diff --git a/certora/munged/mocks/GovernorTimelockCompoundMock.sol b/certora/munged/mocks/GovernorTimelockCompoundMock.sol deleted file mode 100644 index 848f4b409..000000000 --- a/certora/munged/mocks/GovernorTimelockCompoundMock.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../governance/extensions/GovernorTimelockCompound.sol"; -import "../governance/extensions/GovernorSettings.sol"; -import "../governance/extensions/GovernorCountingSimple.sol"; -import "../governance/extensions/GovernorVotesQuorumFraction.sol"; - -contract GovernorTimelockCompoundMock is - GovernorSettings, - GovernorTimelockCompound, - GovernorVotesQuorumFraction, - GovernorCountingSimple -{ - constructor( - string memory name_, - ERC20Votes token_, - uint256 votingDelay_, - uint256 votingPeriod_, - ICompoundTimelock timelock_, - uint256 quorumNumerator_ - ) - Governor(name_) - GovernorTimelockCompound(timelock_) - GovernorSettings(votingDelay_, votingPeriod_, 0) - GovernorVotes(token_) - GovernorVotesQuorumFraction(quorumNumerator_) - {} - - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(Governor, GovernorTimelockCompound) - returns (bool) - { - return super.supportsInterface(interfaceId); - } - - function quorum(uint256 blockNumber) - public - view - override(IGovernor, GovernorVotesQuorumFraction) - returns (uint256) - { - return super.quorum(blockNumber); - } - - function cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 salt - ) public returns (uint256 proposalId) { - return _cancel(targets, values, calldatas, salt); - } - - /** - * Overriding nightmare - */ - function state(uint256 proposalId) - public - view - virtual - override(Governor, GovernorTimelockCompound) - returns (ProposalState) - { - return super.state(proposalId); - } - - function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { - return super.proposalThreshold(); - } - - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal virtual override(Governor, GovernorTimelockCompound) { - super._execute(proposalId, targets, values, calldatas, descriptionHash); - } - - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 salt - ) internal virtual override(Governor, GovernorTimelockCompound) returns (uint256 proposalId) { - return super._cancel(targets, values, calldatas, salt); - } - - function getVotes(address account, uint256 blockNumber) - public - view - virtual - override(IGovernor, GovernorVotes) - returns (uint256) - { - return super.getVotes(account, blockNumber); - } - - function _executor() internal view virtual override(Governor, GovernorTimelockCompound) returns (address) { - return super._executor(); - } -} diff --git a/certora/munged/mocks/GovernorTimelockControlMock.sol b/certora/munged/mocks/GovernorTimelockControlMock.sol deleted file mode 100644 index 4d9e97fd5..000000000 --- a/certora/munged/mocks/GovernorTimelockControlMock.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../governance/extensions/GovernorTimelockControl.sol"; -import "../governance/extensions/GovernorSettings.sol"; -import "../governance/extensions/GovernorCountingSimple.sol"; -import "../governance/extensions/GovernorVotesQuorumFraction.sol"; - -contract GovernorTimelockControlMock is - GovernorSettings, - GovernorTimelockControl, - GovernorVotesQuorumFraction, - GovernorCountingSimple -{ - constructor( - string memory name_, - ERC20Votes token_, - uint256 votingDelay_, - uint256 votingPeriod_, - TimelockController timelock_, - uint256 quorumNumerator_ - ) - Governor(name_) - GovernorTimelockControl(timelock_) - GovernorSettings(votingDelay_, votingPeriod_, 0) - GovernorVotes(token_) - GovernorVotesQuorumFraction(quorumNumerator_) - {} - - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(Governor, GovernorTimelockControl) - returns (bool) - { - return super.supportsInterface(interfaceId); - } - - function quorum(uint256 blockNumber) - public - view - override(IGovernor, GovernorVotesQuorumFraction) - returns (uint256) - { - return super.quorum(blockNumber); - } - - function cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public returns (uint256 proposalId) { - return _cancel(targets, values, calldatas, descriptionHash); - } - - /** - * Overriding nightmare - */ - function state(uint256 proposalId) - public - view - virtual - override(Governor, GovernorTimelockControl) - returns (ProposalState) - { - return super.state(proposalId); - } - - function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { - return super.proposalThreshold(); - } - - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal virtual override(Governor, GovernorTimelockControl) { - super._execute(proposalId, targets, values, calldatas, descriptionHash); - } - - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal virtual override(Governor, GovernorTimelockControl) returns (uint256 proposalId) { - return super._cancel(targets, values, calldatas, descriptionHash); - } - - function getVotes(address account, uint256 blockNumber) - public - view - virtual - override(IGovernor, GovernorVotes) - returns (uint256) - { - return super.getVotes(account, blockNumber); - } - - function _executor() internal view virtual override(Governor, GovernorTimelockControl) returns (address) { - return super._executor(); - } -} diff --git a/certora/munged/mocks/InitializableMock.sol b/certora/munged/mocks/InitializableMock.sol deleted file mode 100644 index 0d3e77dfa..000000000 --- a/certora/munged/mocks/InitializableMock.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../proxy/utils/Initializable.sol"; - -/** - * @title InitializableMock - * @dev This contract is a mock to test initializable functionality - */ -contract InitializableMock is Initializable { - bool public initializerRan; - uint256 public x; - - function initialize() public initializer { - initializerRan = true; - } - - function initializeNested() public initializer { - initialize(); - } - - function initializeWithX(uint256 _x) public payable initializer { - x = _x; - } - - function nonInitializable(uint256 _x) public payable { - x = _x; - } - - function fail() public pure { - require(false, "InitializableMock forced failure"); - } -} diff --git a/certora/munged/mocks/MathMock.sol b/certora/munged/mocks/MathMock.sol deleted file mode 100644 index c651b6bb1..000000000 --- a/certora/munged/mocks/MathMock.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/math/Math.sol"; - -contract MathMock { - function max(uint256 a, uint256 b) public pure returns (uint256) { - return Math.max(a, b); - } - - function min(uint256 a, uint256 b) public pure returns (uint256) { - return Math.min(a, b); - } - - function average(uint256 a, uint256 b) public pure returns (uint256) { - return Math.average(a, b); - } - - function ceilDiv(uint256 a, uint256 b) public pure returns (uint256) { - return Math.ceilDiv(a, b); - } -} diff --git a/certora/munged/mocks/MerkleProofWrapper.sol b/certora/munged/mocks/MerkleProofWrapper.sol deleted file mode 100644 index 1e188df36..000000000 --- a/certora/munged/mocks/MerkleProofWrapper.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/cryptography/MerkleProof.sol"; - -contract MerkleProofWrapper { - function verify( - bytes32[] memory proof, - bytes32 root, - bytes32 leaf - ) public pure returns (bool) { - return MerkleProof.verify(proof, root, leaf); - } - - function processProof(bytes32[] memory proof, bytes32 leaf) public pure returns (bytes32) { - return MerkleProof.processProof(proof, leaf); - } -} diff --git a/certora/munged/mocks/MulticallTest.sol b/certora/munged/mocks/MulticallTest.sol deleted file mode 100644 index f1a3a9cfe..000000000 --- a/certora/munged/mocks/MulticallTest.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./MulticallTokenMock.sol"; - -contract MulticallTest { - function testReturnValues( - MulticallTokenMock multicallToken, - address[] calldata recipients, - uint256[] calldata amounts - ) external { - bytes[] memory calls = new bytes[](recipients.length); - for (uint256 i = 0; i < recipients.length; i++) { - calls[i] = abi.encodeWithSignature("transfer(address,uint256)", recipients[i], amounts[i]); - } - - bytes[] memory results = multicallToken.multicall(calls); - for (uint256 i = 0; i < results.length; i++) { - require(abi.decode(results[i], (bool))); - } - } -} diff --git a/certora/munged/mocks/MulticallTokenMock.sol b/certora/munged/mocks/MulticallTokenMock.sol deleted file mode 100644 index de379681b..000000000 --- a/certora/munged/mocks/MulticallTokenMock.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Multicall.sol"; -import "./ERC20Mock.sol"; - -contract MulticallTokenMock is ERC20Mock, Multicall { - constructor(uint256 initialBalance) ERC20Mock("MulticallToken", "BCT", msg.sender, initialBalance) {} -} diff --git a/certora/munged/mocks/MultipleInheritanceInitializableMocks.sol b/certora/munged/mocks/MultipleInheritanceInitializableMocks.sol deleted file mode 100644 index 1a008e8d8..000000000 --- a/certora/munged/mocks/MultipleInheritanceInitializableMocks.sol +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../proxy/utils/Initializable.sol"; - -// Sample contracts showing upgradeability with multiple inheritance. -// Child contract inherits from Father and Mother contracts, and Father extends from Gramps. -// -// Human -// / \ -// | Gramps -// | | -// Mother Father -// | | -// -- Child -- - -/** - * Sample base intializable contract that is a human - */ -contract SampleHuman is Initializable { - bool public isHuman; - - function initialize() public initializer { - isHuman = true; - } -} - -/** - * Sample base intializable contract that defines a field mother - */ -contract SampleMother is Initializable, SampleHuman { - uint256 public mother; - - function initialize(uint256 value) public virtual initializer { - SampleHuman.initialize(); - mother = value; - } -} - -/** - * Sample base intializable contract that defines a field gramps - */ -contract SampleGramps is Initializable, SampleHuman { - string public gramps; - - function initialize(string memory value) public virtual initializer { - SampleHuman.initialize(); - gramps = value; - } -} - -/** - * Sample base intializable contract that defines a field father and extends from gramps - */ -contract SampleFather is Initializable, SampleGramps { - uint256 public father; - - function initialize(string memory _gramps, uint256 _father) public initializer { - SampleGramps.initialize(_gramps); - father = _father; - } -} - -/** - * Child extends from mother, father (gramps) - */ -contract SampleChild is Initializable, SampleMother, SampleFather { - uint256 public child; - - function initialize( - uint256 _mother, - string memory _gramps, - uint256 _father, - uint256 _child - ) public initializer { - SampleMother.initialize(_mother); - SampleFather.initialize(_gramps, _father); - child = _child; - } -} diff --git a/certora/munged/mocks/OwnableMock.sol b/certora/munged/mocks/OwnableMock.sol deleted file mode 100644 index d60f1c40d..000000000 --- a/certora/munged/mocks/OwnableMock.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../access/Ownable.sol"; - -contract OwnableMock is Ownable {} diff --git a/certora/munged/mocks/PausableMock.sol b/certora/munged/mocks/PausableMock.sol deleted file mode 100644 index 98bcfd593..000000000 --- a/certora/munged/mocks/PausableMock.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../security/Pausable.sol"; - -contract PausableMock is Pausable { - bool public drasticMeasureTaken; - uint256 public count; - - constructor() { - drasticMeasureTaken = false; - count = 0; - } - - function normalProcess() external whenNotPaused { - count++; - } - - function drasticMeasure() external whenPaused { - drasticMeasureTaken = true; - } - - function pause() external { - _pause(); - } - - function unpause() external { - _unpause(); - } -} diff --git a/certora/munged/mocks/PullPaymentMock.sol b/certora/munged/mocks/PullPaymentMock.sol deleted file mode 100644 index 8a708e30c..000000000 --- a/certora/munged/mocks/PullPaymentMock.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../security/PullPayment.sol"; - -// mock class using PullPayment -contract PullPaymentMock is PullPayment { - constructor() payable {} - - // test helper function to call asyncTransfer - function callTransfer(address dest, uint256 amount) public { - _asyncTransfer(dest, amount); - } -} diff --git a/certora/munged/mocks/ReentrancyAttack.sol b/certora/munged/mocks/ReentrancyAttack.sol deleted file mode 100644 index 4de181205..000000000 --- a/certora/munged/mocks/ReentrancyAttack.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; - -contract ReentrancyAttack is Context { - function callSender(bytes4 data) public { - (bool success, ) = _msgSender().call(abi.encodeWithSelector(data)); - require(success, "ReentrancyAttack: failed call"); - } -} diff --git a/certora/munged/mocks/ReentrancyMock.sol b/certora/munged/mocks/ReentrancyMock.sol deleted file mode 100644 index 43425dd6e..000000000 --- a/certora/munged/mocks/ReentrancyMock.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../security/ReentrancyGuard.sol"; -import "./ReentrancyAttack.sol"; - -contract ReentrancyMock is ReentrancyGuard { - uint256 public counter; - - constructor() { - counter = 0; - } - - function callback() external nonReentrant { - _count(); - } - - function countLocalRecursive(uint256 n) public nonReentrant { - if (n > 0) { - _count(); - countLocalRecursive(n - 1); - } - } - - function countThisRecursive(uint256 n) public nonReentrant { - if (n > 0) { - _count(); - (bool success, ) = address(this).call(abi.encodeWithSignature("countThisRecursive(uint256)", n - 1)); - require(success, "ReentrancyMock: failed call"); - } - } - - function countAndCall(ReentrancyAttack attacker) public nonReentrant { - _count(); - bytes4 func = bytes4(keccak256("callback()")); - attacker.callSender(func); - } - - function _count() private { - counter += 1; - } -} diff --git a/certora/munged/mocks/RegressionImplementation.sol b/certora/munged/mocks/RegressionImplementation.sol deleted file mode 100644 index be6b501c1..000000000 --- a/certora/munged/mocks/RegressionImplementation.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../proxy/utils/Initializable.sol"; - -contract Implementation1 is Initializable { - uint256 internal _value; - - function initialize() public initializer {} - - function setValue(uint256 _number) public { - _value = _number; - } -} - -contract Implementation2 is Initializable { - uint256 internal _value; - - function initialize() public initializer {} - - function setValue(uint256 _number) public { - _value = _number; - } - - function getValue() public view returns (uint256) { - return _value; - } -} - -contract Implementation3 is Initializable { - uint256 internal _value; - - function initialize() public initializer {} - - function setValue(uint256 _number) public { - _value = _number; - } - - function getValue(uint256 _number) public view returns (uint256) { - return _value + _number; - } -} - -contract Implementation4 is Initializable { - uint256 internal _value; - - function initialize() public initializer {} - - function setValue(uint256 _number) public { - _value = _number; - } - - function getValue() public view returns (uint256) { - return _value; - } - - fallback() external { - _value = 1; - } -} diff --git a/certora/munged/mocks/SafeCastMock.sol b/certora/munged/mocks/SafeCastMock.sol deleted file mode 100644 index d1f1aaaba..000000000 --- a/certora/munged/mocks/SafeCastMock.sol +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/math/SafeCast.sol"; - -contract SafeCastMock { - using SafeCast for uint256; - using SafeCast for int256; - - function toUint256(int256 a) public pure returns (uint256) { - return a.toUint256(); - } - - function toUint224(uint256 a) public pure returns (uint224) { - return a.toUint224(); - } - - function toUint128(uint256 a) public pure returns (uint128) { - return a.toUint128(); - } - - function toUint96(uint256 a) public pure returns (uint96) { - return a.toUint96(); - } - - function toUint64(uint256 a) public pure returns (uint64) { - return a.toUint64(); - } - - function toUint32(uint256 a) public pure returns (uint32) { - return a.toUint32(); - } - - function toUint16(uint256 a) public pure returns (uint16) { - return a.toUint16(); - } - - function toUint8(uint256 a) public pure returns (uint8) { - return a.toUint8(); - } - - function toInt256(uint256 a) public pure returns (int256) { - return a.toInt256(); - } - - function toInt128(int256 a) public pure returns (int128) { - return a.toInt128(); - } - - function toInt64(int256 a) public pure returns (int64) { - return a.toInt64(); - } - - function toInt32(int256 a) public pure returns (int32) { - return a.toInt32(); - } - - function toInt16(int256 a) public pure returns (int16) { - return a.toInt16(); - } - - function toInt8(int256 a) public pure returns (int8) { - return a.toInt8(); - } -} diff --git a/certora/munged/mocks/SafeERC20Helper.sol b/certora/munged/mocks/SafeERC20Helper.sol deleted file mode 100644 index 9e3442b35..000000000 --- a/certora/munged/mocks/SafeERC20Helper.sol +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; -import "../token/ERC20/IERC20.sol"; -import "../token/ERC20/utils/SafeERC20.sol"; - -contract ERC20ReturnFalseMock is Context { - uint256 private _allowance; - - // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, - // we write to a dummy state variable. - uint256 private _dummy; - - function transfer(address, uint256) public returns (bool) { - _dummy = 0; - return false; - } - - function transferFrom( - address, - address, - uint256 - ) public returns (bool) { - _dummy = 0; - return false; - } - - function approve(address, uint256) public returns (bool) { - _dummy = 0; - return false; - } - - function allowance(address, address) public view returns (uint256) { - require(_dummy == 0); // Duummy read from a state variable so that the function is view - return 0; - } -} - -contract ERC20ReturnTrueMock is Context { - mapping(address => uint256) private _allowances; - - // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, - // we write to a dummy state variable. - uint256 private _dummy; - - function transfer(address, uint256) public returns (bool) { - _dummy = 0; - return true; - } - - function transferFrom( - address, - address, - uint256 - ) public returns (bool) { - _dummy = 0; - return true; - } - - function approve(address, uint256) public returns (bool) { - _dummy = 0; - return true; - } - - function setAllowance(uint256 allowance_) public { - _allowances[_msgSender()] = allowance_; - } - - function allowance(address owner, address) public view returns (uint256) { - return _allowances[owner]; - } -} - -contract ERC20NoReturnMock is Context { - mapping(address => uint256) private _allowances; - - // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, - // we write to a dummy state variable. - uint256 private _dummy; - - function transfer(address, uint256) public { - _dummy = 0; - } - - function transferFrom( - address, - address, - uint256 - ) public { - _dummy = 0; - } - - function approve(address, uint256) public { - _dummy = 0; - } - - function setAllowance(uint256 allowance_) public { - _allowances[_msgSender()] = allowance_; - } - - function allowance(address owner, address) public view returns (uint256) { - return _allowances[owner]; - } -} - -contract SafeERC20Wrapper is Context { - using SafeERC20 for IERC20; - - IERC20 private _token; - - constructor(IERC20 token) { - _token = token; - } - - function transfer() public { - _token.safeTransfer(address(0), 0); - } - - function transferFrom() public { - _token.safeTransferFrom(address(0), address(0), 0); - } - - function approve(uint256 amount) public { - _token.safeApprove(address(0), amount); - } - - function increaseAllowance(uint256 amount) public { - _token.safeIncreaseAllowance(address(0), amount); - } - - function decreaseAllowance(uint256 amount) public { - _token.safeDecreaseAllowance(address(0), amount); - } - - function setAllowance(uint256 allowance_) public { - ERC20ReturnTrueMock(address(_token)).setAllowance(allowance_); - } - - function allowance() public view returns (uint256) { - return _token.allowance(address(0), address(0)); - } -} diff --git a/certora/munged/mocks/SafeMathMock.sol b/certora/munged/mocks/SafeMathMock.sol deleted file mode 100644 index 3d1f4727e..000000000 --- a/certora/munged/mocks/SafeMathMock.sol +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/math/SafeMath.sol"; - -contract SafeMathMock { - function tryAdd(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { - return SafeMath.tryAdd(a, b); - } - - function trySub(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { - return SafeMath.trySub(a, b); - } - - function tryMul(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { - return SafeMath.tryMul(a, b); - } - - function tryDiv(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { - return SafeMath.tryDiv(a, b); - } - - function tryMod(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { - return SafeMath.tryMod(a, b); - } - - // using the do* naming convention to avoid warnings due to clashing opcode names - - function doAdd(uint256 a, uint256 b) public pure returns (uint256) { - return SafeMath.add(a, b); - } - - function doSub(uint256 a, uint256 b) public pure returns (uint256) { - return SafeMath.sub(a, b); - } - - function doMul(uint256 a, uint256 b) public pure returns (uint256) { - return SafeMath.mul(a, b); - } - - function doDiv(uint256 a, uint256 b) public pure returns (uint256) { - return SafeMath.div(a, b); - } - - function doMod(uint256 a, uint256 b) public pure returns (uint256) { - return SafeMath.mod(a, b); - } - - function subWithMessage( - uint256 a, - uint256 b, - string memory errorMessage - ) public pure returns (uint256) { - return SafeMath.sub(a, b, errorMessage); - } - - function divWithMessage( - uint256 a, - uint256 b, - string memory errorMessage - ) public pure returns (uint256) { - return SafeMath.div(a, b, errorMessage); - } - - function modWithMessage( - uint256 a, - uint256 b, - string memory errorMessage - ) public pure returns (uint256) { - return SafeMath.mod(a, b, errorMessage); - } - - function addMemoryCheck() public pure returns (uint256 mem) { - uint256 length = 32; - assembly { - mem := mload(0x40) - } - for (uint256 i = 0; i < length; ++i) { - SafeMath.add(1, 1); - } - assembly { - mem := sub(mload(0x40), mem) - } - } - - function subMemoryCheck() public pure returns (uint256 mem) { - uint256 length = 32; - assembly { - mem := mload(0x40) - } - for (uint256 i = 0; i < length; ++i) { - SafeMath.sub(1, 1); - } - assembly { - mem := sub(mload(0x40), mem) - } - } - - function mulMemoryCheck() public pure returns (uint256 mem) { - uint256 length = 32; - assembly { - mem := mload(0x40) - } - for (uint256 i = 0; i < length; ++i) { - SafeMath.mul(1, 1); - } - assembly { - mem := sub(mload(0x40), mem) - } - } - - function divMemoryCheck() public pure returns (uint256 mem) { - uint256 length = 32; - assembly { - mem := mload(0x40) - } - for (uint256 i = 0; i < length; ++i) { - SafeMath.div(1, 1); - } - assembly { - mem := sub(mload(0x40), mem) - } - } - - function modMemoryCheck() public pure returns (uint256 mem) { - uint256 length = 32; - assembly { - mem := mload(0x40) - } - for (uint256 i = 0; i < length; ++i) { - SafeMath.mod(1, 1); - } - assembly { - mem := sub(mload(0x40), mem) - } - } -} diff --git a/certora/munged/mocks/SignatureCheckerMock.sol b/certora/munged/mocks/SignatureCheckerMock.sol deleted file mode 100644 index 3b399c1ae..000000000 --- a/certora/munged/mocks/SignatureCheckerMock.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/cryptography/SignatureChecker.sol"; - -contract SignatureCheckerMock { - using SignatureChecker for address; - - function isValidSignatureNow( - address signer, - bytes32 hash, - bytes memory signature - ) public view returns (bool) { - return signer.isValidSignatureNow(hash, signature); - } -} diff --git a/certora/munged/mocks/SignedSafeMathMock.sol b/certora/munged/mocks/SignedSafeMathMock.sol deleted file mode 100644 index 8d1021798..000000000 --- a/certora/munged/mocks/SignedSafeMathMock.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/math/SignedSafeMath.sol"; - -contract SignedSafeMathMock { - function mul(int256 a, int256 b) public pure returns (int256) { - return SignedSafeMath.mul(a, b); - } - - function div(int256 a, int256 b) public pure returns (int256) { - return SignedSafeMath.div(a, b); - } - - function sub(int256 a, int256 b) public pure returns (int256) { - return SignedSafeMath.sub(a, b); - } - - function add(int256 a, int256 b) public pure returns (int256) { - return SignedSafeMath.add(a, b); - } -} diff --git a/certora/munged/mocks/SingleInheritanceInitializableMocks.sol b/certora/munged/mocks/SingleInheritanceInitializableMocks.sol deleted file mode 100644 index 6c82dd20c..000000000 --- a/certora/munged/mocks/SingleInheritanceInitializableMocks.sol +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../proxy/utils/Initializable.sol"; - -/** - * @title MigratableMockV1 - * @dev This contract is a mock to test initializable functionality through migrations - */ -contract MigratableMockV1 is Initializable { - uint256 public x; - - function initialize(uint256 value) public payable initializer { - x = value; - } -} - -/** - * @title MigratableMockV2 - * @dev This contract is a mock to test migratable functionality with params - */ -contract MigratableMockV2 is MigratableMockV1 { - bool internal _migratedV2; - uint256 public y; - - function migrate(uint256 value, uint256 anotherValue) public payable { - require(!_migratedV2); - x = value; - y = anotherValue; - _migratedV2 = true; - } -} - -/** - * @title MigratableMockV3 - * @dev This contract is a mock to test migratable functionality without params - */ -contract MigratableMockV3 is MigratableMockV2 { - bool internal _migratedV3; - - function migrate() public payable { - require(!_migratedV3); - uint256 oldX = x; - x = y; - y = oldX; - _migratedV3 = true; - } -} diff --git a/certora/munged/mocks/StorageSlotMock.sol b/certora/munged/mocks/StorageSlotMock.sol deleted file mode 100644 index 5d099fca8..000000000 --- a/certora/munged/mocks/StorageSlotMock.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/StorageSlot.sol"; - -contract StorageSlotMock { - using StorageSlot for bytes32; - - function setBoolean(bytes32 slot, bool value) public { - slot.getBooleanSlot().value = value; - } - - function setAddress(bytes32 slot, address value) public { - slot.getAddressSlot().value = value; - } - - function setBytes32(bytes32 slot, bytes32 value) public { - slot.getBytes32Slot().value = value; - } - - function setUint256(bytes32 slot, uint256 value) public { - slot.getUint256Slot().value = value; - } - - function getBoolean(bytes32 slot) public view returns (bool) { - return slot.getBooleanSlot().value; - } - - function getAddress(bytes32 slot) public view returns (address) { - return slot.getAddressSlot().value; - } - - function getBytes32(bytes32 slot) public view returns (bytes32) { - return slot.getBytes32Slot().value; - } - - function getUint256(bytes32 slot) public view returns (uint256) { - return slot.getUint256Slot().value; - } -} diff --git a/certora/munged/mocks/StringsMock.sol b/certora/munged/mocks/StringsMock.sol deleted file mode 100644 index f257734e7..000000000 --- a/certora/munged/mocks/StringsMock.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Strings.sol"; - -contract StringsMock { - function fromUint256(uint256 value) public pure returns (string memory) { - return Strings.toString(value); - } - - function fromUint256Hex(uint256 value) public pure returns (string memory) { - return Strings.toHexString(value); - } - - function fromUint256HexFixed(uint256 value, uint256 length) public pure returns (string memory) { - return Strings.toHexString(value, length); - } -} diff --git a/certora/munged/mocks/TimersBlockNumberImpl.sol b/certora/munged/mocks/TimersBlockNumberImpl.sol deleted file mode 100644 index 84633e6f8..000000000 --- a/certora/munged/mocks/TimersBlockNumberImpl.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Timers.sol"; - -contract TimersBlockNumberImpl { - using Timers for Timers.BlockNumber; - - Timers.BlockNumber private _timer; - - function getDeadline() public view returns (uint64) { - return _timer.getDeadline(); - } - - function setDeadline(uint64 timestamp) public { - _timer.setDeadline(timestamp); - } - - function reset() public { - _timer.reset(); - } - - function isUnset() public view returns (bool) { - return _timer.isUnset(); - } - - function isStarted() public view returns (bool) { - return _timer.isStarted(); - } - - function isPending() public view returns (bool) { - return _timer.isPending(); - } - - function isExpired() public view returns (bool) { - return _timer.isExpired(); - } -} diff --git a/certora/munged/mocks/TimersTimestampImpl.sol b/certora/munged/mocks/TimersTimestampImpl.sol deleted file mode 100644 index 07f9a1b3f..000000000 --- a/certora/munged/mocks/TimersTimestampImpl.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Timers.sol"; - -contract TimersTimestampImpl { - using Timers for Timers.Timestamp; - - Timers.Timestamp private _timer; - - function getDeadline() public view returns (uint64) { - return _timer.getDeadline(); - } - - function setDeadline(uint64 timestamp) public { - _timer.setDeadline(timestamp); - } - - function reset() public { - _timer.reset(); - } - - function isUnset() public view returns (bool) { - return _timer.isUnset(); - } - - function isStarted() public view returns (bool) { - return _timer.isStarted(); - } - - function isPending() public view returns (bool) { - return _timer.isPending(); - } - - function isExpired() public view returns (bool) { - return _timer.isExpired(); - } -} diff --git a/certora/munged/mocks/UUPS/TestInProd.sol b/certora/munged/mocks/UUPS/TestInProd.sol deleted file mode 100644 index bbb610300..000000000 --- a/certora/munged/mocks/UUPS/TestInProd.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../CountersImpl.sol"; -import "../../proxy/utils/UUPSUpgradeable.sol"; - -contract UUPSUpgradeableMock is CountersImpl, UUPSUpgradeable { - // Not having any checks in this function is dangerous! Do not do this outside tests! - function _authorizeUpgrade(address) internal virtual override {} -} - -contract UUPSUpgradeableUnsafeMock is UUPSUpgradeableMock { - function upgradeTo(address newImplementation) external virtual override { - ERC1967Upgrade._upgradeToAndCall(newImplementation, bytes(""), false); - } - - function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual override { - ERC1967Upgrade._upgradeToAndCall(newImplementation, data, false); - } -} - -contract UUPSUpgradeableBrokenMock is UUPSUpgradeableMock { - function upgradeTo(address) external virtual override { - // pass - } - - function upgradeToAndCall(address, bytes memory) external payable virtual override { - // pass - } -} diff --git a/certora/munged/mocks/compound/CompTimelock.sol b/certora/munged/mocks/compound/CompTimelock.sol deleted file mode 100644 index 49ffa4b77..000000000 --- a/certora/munged/mocks/compound/CompTimelock.sol +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// solhint-disable private-vars-leading-underscore -/** - * Copyright 2020 Compound Labs, Inc. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the - * following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following - * disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -pragma solidity ^0.8.0; - -contract CompTimelock { - event NewAdmin(address indexed newAdmin); - event NewPendingAdmin(address indexed newPendingAdmin); - event NewDelay(uint256 indexed newDelay); - event CancelTransaction( - bytes32 indexed txHash, - address indexed target, - uint256 value, - string signature, - bytes data, - uint256 eta - ); - event ExecuteTransaction( - bytes32 indexed txHash, - address indexed target, - uint256 value, - string signature, - bytes data, - uint256 eta - ); - event QueueTransaction( - bytes32 indexed txHash, - address indexed target, - uint256 value, - string signature, - bytes data, - uint256 eta - ); - - uint256 public constant GRACE_PERIOD = 14 days; - uint256 public constant MINIMUM_DELAY = 2 days; - uint256 public constant MAXIMUM_DELAY = 30 days; - - address public admin; - address public pendingAdmin; - uint256 public delay; - - mapping(bytes32 => bool) public queuedTransactions; - - constructor(address admin_, uint256 delay_) { - require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); - require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); - - admin = admin_; - delay = delay_; - } - - receive() external payable {} - - function setDelay(uint256 delay_) public { - require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); - require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); - require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); - delay = delay_; - - emit NewDelay(delay); - } - - function acceptAdmin() public { - require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); - admin = msg.sender; - pendingAdmin = address(0); - - emit NewAdmin(admin); - } - - function setPendingAdmin(address pendingAdmin_) public { - require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); - pendingAdmin = pendingAdmin_; - - emit NewPendingAdmin(pendingAdmin); - } - - function queueTransaction( - address target, - uint256 value, - string memory signature, - bytes memory data, - uint256 eta - ) public returns (bytes32) { - require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); - require( - eta >= getBlockTimestamp() + delay, - "Timelock::queueTransaction: Estimated execution block must satisfy delay." - ); - - bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); - queuedTransactions[txHash] = true; - - emit QueueTransaction(txHash, target, value, signature, data, eta); - return txHash; - } - - function cancelTransaction( - address target, - uint256 value, - string memory signature, - bytes memory data, - uint256 eta - ) public { - require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); - - bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); - queuedTransactions[txHash] = false; - - emit CancelTransaction(txHash, target, value, signature, data, eta); - } - - function executeTransaction( - address target, - uint256 value, - string memory signature, - bytes memory data, - uint256 eta - ) public payable returns (bytes memory) { - require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); - - bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); - require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); - require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); - require(getBlockTimestamp() <= eta + GRACE_PERIOD, "Timelock::executeTransaction: Transaction is stale."); - - queuedTransactions[txHash] = false; - - bytes memory callData; - - if (bytes(signature).length == 0) { - callData = data; - } else { - callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); - } - - // solium-disable-next-line security/no-call-value - (bool success, bytes memory returnData) = target.call{value: value}(callData); - require(success, "Timelock::executeTransaction: Transaction execution reverted."); - - emit ExecuteTransaction(txHash, target, value, signature, data, eta); - - return returnData; - } - - function getBlockTimestamp() internal view returns (uint256) { - // solium-disable-next-line security/no-block-members - return block.timestamp; - } -} diff --git a/certora/munged/mocks/wizard/MyGovernor1.sol b/certora/munged/mocks/wizard/MyGovernor1.sol deleted file mode 100644 index 72b486aa7..000000000 --- a/certora/munged/mocks/wizard/MyGovernor1.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.2; - -import "../../governance/Governor.sol"; -import "../../governance/extensions/GovernorCountingSimple.sol"; -import "../../governance/extensions/GovernorVotes.sol"; -import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; -import "../../governance/extensions/GovernorTimelockControl.sol"; - -contract MyGovernor1 is - Governor, - GovernorTimelockControl, - GovernorVotes, - GovernorVotesQuorumFraction, - GovernorCountingSimple -{ - constructor(ERC20Votes _token, TimelockController _timelock) - Governor("MyGovernor") - GovernorVotes(_token) - GovernorVotesQuorumFraction(4) - GovernorTimelockControl(_timelock) - {} - - function votingDelay() public pure override returns (uint256) { - return 1; // 1 block - } - - function votingPeriod() public pure override returns (uint256) { - return 45818; // 1 week - } - - // The following functions are overrides required by Solidity. - - function quorum(uint256 blockNumber) - public - view - override(IGovernor, GovernorVotesQuorumFraction) - returns (uint256) - { - return super.quorum(blockNumber); - } - - function getVotes(address account, uint256 blockNumber) - public - view - override(IGovernor, GovernorVotes) - returns (uint256) - { - return super.getVotes(account, blockNumber); - } - - function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { - return super.state(proposalId); - } - - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public override(Governor, IGovernor) returns (uint256) { - return super.propose(targets, values, calldatas, description); - } - - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) { - super._execute(proposalId, targets, values, calldatas, descriptionHash); - } - - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) returns (uint256) { - return super._cancel(targets, values, calldatas, descriptionHash); - } - - function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { - return super._executor(); - } - - function supportsInterface(bytes4 interfaceId) - public - view - override(Governor, GovernorTimelockControl) - returns (bool) - { - return super.supportsInterface(interfaceId); - } -} diff --git a/certora/munged/mocks/wizard/MyGovernor2.sol b/certora/munged/mocks/wizard/MyGovernor2.sol deleted file mode 100644 index 3f25b91bf..000000000 --- a/certora/munged/mocks/wizard/MyGovernor2.sol +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.2; - -import "../../governance/Governor.sol"; -import "../../governance/extensions/GovernorProposalThreshold.sol"; -import "../../governance/extensions/GovernorCountingSimple.sol"; -import "../../governance/extensions/GovernorVotes.sol"; -import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; -import "../../governance/extensions/GovernorTimelockControl.sol"; - -contract MyGovernor2 is - Governor, - GovernorTimelockControl, - GovernorProposalThreshold, - GovernorVotes, - GovernorVotesQuorumFraction, - GovernorCountingSimple -{ - constructor(ERC20Votes _token, TimelockController _timelock) - Governor("MyGovernor") - GovernorVotes(_token) - GovernorVotesQuorumFraction(4) - GovernorTimelockControl(_timelock) - {} - - function votingDelay() public pure override returns (uint256) { - return 1; // 1 block - } - - function votingPeriod() public pure override returns (uint256) { - return 45818; // 1 week - } - - function proposalThreshold() public pure override returns (uint256) { - return 1000e18; - } - - // The following functions are overrides required by Solidity. - - function quorum(uint256 blockNumber) - public - view - override(IGovernor, GovernorVotesQuorumFraction) - returns (uint256) - { - return super.quorum(blockNumber); - } - - function getVotes(address account, uint256 blockNumber) - public - view - override(IGovernor, GovernorVotes) - returns (uint256) - { - return super.getVotes(account, blockNumber); - } - - function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { - return super.state(proposalId); - } - - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public override(Governor, GovernorProposalThreshold, IGovernor) returns (uint256) { - return super.propose(targets, values, calldatas, description); - } - - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) { - super._execute(proposalId, targets, values, calldatas, descriptionHash); - } - - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) returns (uint256) { - return super._cancel(targets, values, calldatas, descriptionHash); - } - - function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { - return super._executor(); - } - - function supportsInterface(bytes4 interfaceId) - public - view - override(Governor, GovernorTimelockControl) - returns (bool) - { - return super.supportsInterface(interfaceId); - } -} diff --git a/certora/munged/mocks/wizard/MyGovernor3.sol b/certora/munged/mocks/wizard/MyGovernor3.sol deleted file mode 100644 index c2465751a..000000000 --- a/certora/munged/mocks/wizard/MyGovernor3.sol +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.2; - -import "../../governance/Governor.sol"; -import "../../governance/compatibility/GovernorCompatibilityBravo.sol"; -import "../../governance/extensions/GovernorVotes.sol"; -import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; -import "../../governance/extensions/GovernorTimelockControl.sol"; - -contract MyGovernor is - Governor, - GovernorTimelockControl, - GovernorCompatibilityBravo, - GovernorVotes, - GovernorVotesQuorumFraction -{ - constructor(ERC20Votes _token, TimelockController _timelock) - Governor("MyGovernor") - GovernorVotes(_token) - GovernorVotesQuorumFraction(4) - GovernorTimelockControl(_timelock) - {} - - function votingDelay() public pure override returns (uint256) { - return 1; // 1 block - } - - function votingPeriod() public pure override returns (uint256) { - return 45818; // 1 week - } - - function proposalThreshold() public pure override returns (uint256) { - return 1000e18; - } - - // The following functions are overrides required by Solidity. - - function quorum(uint256 blockNumber) - public - view - override(IGovernor, GovernorVotesQuorumFraction) - returns (uint256) - { - return super.quorum(blockNumber); - } - - function getVotes(address account, uint256 blockNumber) - public - view - override(IGovernor, GovernorVotes) - returns (uint256) - { - return super.getVotes(account, blockNumber); - } - - function state(uint256 proposalId) - public - view - override(Governor, IGovernor, GovernorTimelockControl) - returns (ProposalState) - { - return super.state(proposalId); - } - - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) { - return super.propose(targets, values, calldatas, description); - } - - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) { - super._execute(proposalId, targets, values, calldatas, descriptionHash); - } - - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) returns (uint256) { - return super._cancel(targets, values, calldatas, descriptionHash); - } - - function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { - return super._executor(); - } - - function supportsInterface(bytes4 interfaceId) - public - view - override(Governor, IERC165, GovernorTimelockControl) - returns (bool) - { - return super.supportsInterface(interfaceId); - } -} diff --git a/certora/munged/package.json b/certora/munged/package.json deleted file mode 100644 index c8705f0e5..000000000 --- a/certora/munged/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "@openzeppelin/contracts", - "description": "Secure Smart Contract library for Solidity", - "version": "4.3.2", - "files": [ - "**/*.sol", - "/build/contracts/*.json", - "!/mocks/**/*" - ], - "scripts": { - "prepare": "bash ../scripts/prepare-contracts-package.sh", - "prepare-docs": "cd ..; npm run prepare-docs" - }, - "repository": { - "type": "git", - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts.git" - }, - "keywords": [ - "solidity", - "ethereum", - "smart", - "contracts", - "security", - "zeppelin" - ], - "author": "OpenZeppelin Community ", - "license": "MIT", - "bugs": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts/issues" - }, - "homepage": "https://openzeppelin.com/contracts/" -} diff --git a/certora/munged/proxy/Clones.sol b/certora/munged/proxy/Clones.sol deleted file mode 100644 index bd661b10b..000000000 --- a/certora/munged/proxy/Clones.sol +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/Clones.sol) - -pragma solidity ^0.8.0; - -/** - * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for - * deploying minimal proxy contracts, also known as "clones". - * - * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies - * > a minimal bytecode implementation that delegates all calls to a known, fixed address. - * - * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` - * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the - * deterministic method. - * - * _Available since v3.4._ - */ -library Clones { - /** - * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. - * - * This function uses the create opcode, which should never revert. - */ - function clone(address implementation) internal returns (address instance) { - assembly { - let ptr := mload(0x40) - mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) - mstore(add(ptr, 0x14), shl(0x60, implementation)) - mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) - instance := create(0, ptr, 0x37) - } - require(instance != address(0), "ERC1167: create failed"); - } - - /** - * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. - * - * This function uses the create2 opcode and a `salt` to deterministically deploy - * the clone. Using the same `implementation` and `salt` multiple time will revert, since - * the clones cannot be deployed twice at the same address. - */ - function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { - assembly { - let ptr := mload(0x40) - mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) - mstore(add(ptr, 0x14), shl(0x60, implementation)) - mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) - instance := create2(0, ptr, 0x37, salt) - } - require(instance != address(0), "ERC1167: create2 failed"); - } - - /** - * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. - */ - function predictDeterministicAddress( - address implementation, - bytes32 salt, - address deployer - ) internal pure returns (address predicted) { - assembly { - let ptr := mload(0x40) - mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) - mstore(add(ptr, 0x14), shl(0x60, implementation)) - mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000) - mstore(add(ptr, 0x38), shl(0x60, deployer)) - mstore(add(ptr, 0x4c), salt) - mstore(add(ptr, 0x6c), keccak256(ptr, 0x37)) - predicted := keccak256(add(ptr, 0x37), 0x55) - } - } - - /** - * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. - */ - function predictDeterministicAddress(address implementation, bytes32 salt) - internal - view - returns (address predicted) - { - return predictDeterministicAddress(implementation, salt, address(this)); - } -} diff --git a/certora/munged/proxy/ERC1967/ERC1967Proxy.sol b/certora/munged/proxy/ERC1967/ERC1967Proxy.sol deleted file mode 100644 index dbc655ecb..000000000 --- a/certora/munged/proxy/ERC1967/ERC1967Proxy.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/ERC1967/ERC1967Proxy.sol) - -pragma solidity ^0.8.0; - -import "../Proxy.sol"; -import "./ERC1967Upgrade.sol"; - -/** - * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an - * implementation address that can be changed. This address is stored in storage in the location specified by - * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the - * implementation behind the proxy. - */ -contract ERC1967Proxy is Proxy, ERC1967Upgrade { - /** - * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. - * - * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded - * function call, and allows initializating the storage of the proxy like a Solidity constructor. - */ - constructor(address _logic, bytes memory _data) payable { - assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); - _upgradeToAndCall(_logic, _data, false); - } - - /** - * @dev Returns the current implementation address. - */ - function _implementation() internal view virtual override returns (address impl) { - return ERC1967Upgrade._getImplementation(); - } -} diff --git a/certora/munged/proxy/ERC1967/ERC1967Upgrade.sol b/certora/munged/proxy/ERC1967/ERC1967Upgrade.sol deleted file mode 100644 index 053b5c124..000000000 --- a/certora/munged/proxy/ERC1967/ERC1967Upgrade.sol +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/ERC1967/ERC1967Upgrade.sol) - -pragma solidity ^0.8.2; - -import "../beacon/IBeacon.sol"; -import "../../utils/Address.sol"; -import "../../utils/StorageSlot.sol"; - -/** - * @dev This abstract contract provides getters and event emitting update functions for - * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. - * - * _Available since v4.1._ - * - * @custom:oz-upgrades-unsafe-allow delegatecall - */ -abstract contract ERC1967Upgrade { - // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 - bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; - - /** - * @dev Storage slot with the address of the current implementation. - * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /** - * @dev Emitted when the implementation is upgraded. - */ - event Upgraded(address indexed implementation); - - /** - * @dev Returns the current implementation address. - */ - function _getImplementation() internal view returns (address) { - return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; - } - - /** - * @dev Stores a new address in the EIP1967 implementation slot. - */ - function _setImplementation(address newImplementation) private { - require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); - StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; - } - - /** - * @dev Perform implementation upgrade - * - * Emits an {Upgraded} event. - */ - function _upgradeTo(address newImplementation) internal { - _setImplementation(newImplementation); - emit Upgraded(newImplementation); - } - - /** - * @dev Perform implementation upgrade with additional setup call. - * - * Emits an {Upgraded} event. - */ - function _upgradeToAndCall( - address newImplementation, - bytes memory data, - bool forceCall - ) internal { - _upgradeTo(newImplementation); - if (data.length > 0 || forceCall) { - Address.functionDelegateCall(newImplementation, data); - } - } - - /** - * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. - * - * Emits an {Upgraded} event. - */ - function _upgradeToAndCallSecure( - address newImplementation, - bytes memory data, - bool forceCall - ) internal { - address oldImplementation = _getImplementation(); - - // Initial upgrade and setup call - _setImplementation(newImplementation); - if (data.length > 0 || forceCall) { - Address.functionDelegateCall(newImplementation, data); - } - - // Perform rollback test if not already in progress - StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT); - if (!rollbackTesting.value) { - // Trigger rollback using upgradeTo from the new implementation - rollbackTesting.value = true; - Address.functionDelegateCall( - newImplementation, - abi.encodeWithSignature("upgradeTo(address)", oldImplementation) - ); - rollbackTesting.value = false; - // Check rollback was effective - require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades"); - // Finally reset to the new implementation and log the upgrade - _upgradeTo(newImplementation); - } - } - - /** - * @dev Storage slot with the admin of the contract. - * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; - - /** - * @dev Emitted when the admin account has changed. - */ - event AdminChanged(address previousAdmin, address newAdmin); - - /** - * @dev Returns the current admin. - */ - function _getAdmin() internal view returns (address) { - return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; - } - - /** - * @dev Stores a new address in the EIP1967 admin slot. - */ - function _setAdmin(address newAdmin) private { - require(newAdmin != address(0), "ERC1967: new admin is the zero address"); - StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; - } - - /** - * @dev Changes the admin of the proxy. - * - * Emits an {AdminChanged} event. - */ - function _changeAdmin(address newAdmin) internal { - emit AdminChanged(_getAdmin(), newAdmin); - _setAdmin(newAdmin); - } - - /** - * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. - * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. - */ - bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; - - /** - * @dev Emitted when the beacon is upgraded. - */ - event BeaconUpgraded(address indexed beacon); - - /** - * @dev Returns the current beacon. - */ - function _getBeacon() internal view returns (address) { - return StorageSlot.getAddressSlot(_BEACON_SLOT).value; - } - - /** - * @dev Stores a new beacon in the EIP1967 beacon slot. - */ - function _setBeacon(address newBeacon) private { - require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); - require( - Address.isContract(IBeacon(newBeacon).implementation()), - "ERC1967: beacon implementation is not a contract" - ); - StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; - } - - /** - * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does - * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). - * - * Emits a {BeaconUpgraded} event. - */ - function _upgradeBeaconToAndCall( - address newBeacon, - bytes memory data, - bool forceCall - ) internal { - _setBeacon(newBeacon); - emit BeaconUpgraded(newBeacon); - if (data.length > 0 || forceCall) { - Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); - } - } -} diff --git a/certora/munged/proxy/Proxy.sol b/certora/munged/proxy/Proxy.sol deleted file mode 100644 index 84d9fde04..000000000 --- a/certora/munged/proxy/Proxy.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/Proxy.sol) - -pragma solidity ^0.8.0; - -/** - * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM - * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to - * be specified by overriding the virtual {_implementation} function. - * - * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a - * different contract through the {_delegate} function. - * - * The success and return data of the delegated call will be returned back to the caller of the proxy. - */ -abstract contract Proxy { - /** - * @dev Delegates the current call to `implementation`. - * - * This function does not return to its internall call site, it will return directly to the external caller. - */ - function _delegate(address implementation) internal virtual { - assembly { - // Copy msg.data. We take full control of memory in this inline assembly - // block because it will not return to Solidity code. We overwrite the - // Solidity scratch pad at memory position 0. - calldatacopy(0, 0, calldatasize()) - - // Call the implementation. - // out and outsize are 0 because we don't know the size yet. - let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) - - // Copy the returned data. - returndatacopy(0, 0, returndatasize()) - - switch result - // delegatecall returns 0 on error. - case 0 { - revert(0, returndatasize()) - } - default { - return(0, returndatasize()) - } - } - } - - /** - * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function - * and {_fallback} should delegate. - */ - function _implementation() internal view virtual returns (address); - - /** - * @dev Delegates the current call to the address returned by `_implementation()`. - * - * This function does not return to its internall call site, it will return directly to the external caller. - */ - function _fallback() internal virtual { - _beforeFallback(); - _delegate(_implementation()); - } - - /** - * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other - * function in the contract matches the call data. - */ - fallback() external payable virtual { - _fallback(); - } - - /** - * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data - * is empty. - */ - receive() external payable virtual { - _fallback(); - } - - /** - * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` - * call, or as part of the Solidity `fallback` or `receive` functions. - * - * If overriden should call `super._beforeFallback()`. - */ - function _beforeFallback() internal virtual {} -} diff --git a/certora/munged/proxy/README.adoc b/certora/munged/proxy/README.adoc deleted file mode 100644 index ae278b083..000000000 --- a/certora/munged/proxy/README.adoc +++ /dev/null @@ -1,83 +0,0 @@ -= Proxies - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/proxy - -This is a low-level set of contracts implementing different proxy patterns with and without upgradeability. For an in-depth overview of this pattern check out the xref:upgrades-plugins::proxies.adoc[Proxy Upgrade Pattern] page. - -Most of the proxies below are built on an abstract base contract. - -- {Proxy}: Abstract contract implementing the core delegation functionality. - -In order to avoid clashes with the storage variables of the implementation contract behind a proxy, we use https://eips.ethereum.org/EIPS/eip-1967[EIP1967] storage slots. - -- {ERC1967Upgrade}: Internal functions to get and set the storage slots defined in EIP1967. -- {ERC1967Proxy}: A proxy using EIP1967 storage slots. Not upgradeable by default. - -There are two alternative ways to add upgradeability to an ERC1967 proxy. Their differences are explained below in <>. - -- {TransparentUpgradeableProxy}: A proxy with a built in admin and upgrade interface. -- {UUPSUpgradeable}: An upgradeability mechanism to be included in the implementation for an ERC1967 proxy. - -CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Truffle and Hardhat. - -A different family of proxies are beacon proxies. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. - -- {BeaconProxy}: A proxy that retreives its implementation from a beacon contract. -- {UpgradeableBeacon}: A beacon contract that can be upgraded. - -In this pattern, the proxy contract doesn't hold the implementation address in storage like an ERC1967 proxy, instead the address is stored in a separate beacon contract. The `upgrade` operations that are sent to the beacon instead of to the proxy contract, and all proxies that follow that beacon are automatically upgraded. - -Outside the realm of upgradeability, proxies can also be useful to make cheap contract clones, such as those created by an on-chain factory contract that creates many instances of the same contract. These instances are designed to be both cheap to deploy, and cheap to call. - -- {Clones}: A library that can deploy cheap minimal non-upgradeable proxies. - -[[transparent-vs-uups]] -== Transparent vs UUPS Proxies - -The original proxies included in OpenZeppelin followed the https://blog.openzeppelin.com/the-transparent-proxy-pattern/[Transparent Proxy Pattern]. While this pattern is still provided, our recommendation is now shifting towards UUPS proxies, which are both lightweight and versatile. The name UUPS comes from https://eips.ethereum.org/EIPS/eip-1822[EIP1822], which first documented the pattern. - -While both of these share the same interface for upgrades, in UUPS proxies the upgrade is handled by the implementation, and can eventually be removed. Transparent proxies, on the other hand, include the upgrade and admin logic in the proxy itself. This means {TransparentUpgradeableProxy} is more expensive to deploy than what is possible with UUPS proxies. - -UUPS proxies are implemented using an {ERC1967Proxy}. Note that this proxy is not by itself upgradeable. It is the role of the implementation to include, alongside the contract's logic, all the code necessary to update the implementation's address that is stored at a specific slot in the proxy's storage space. This is where the {UUPSUpgradeable} contract comes in. Inheriting from it (and overriding the {xref-UUPSUpgradeable-_authorizeUpgrade-address-}[`_authorizeUpgrade`] function with the relevant access control mechanism) will turn your contract into a UUPS compliant implementation. - -Note that since both proxies use the same storage slot for the implementation address, using a UUPS compliant implementation with a {TransparentUpgradeableProxy} might allow non-admins to perform upgrade operations. - -By default, the upgrade functionality included in {UUPSUpgradeable} contains a security mechanism that will prevent any upgrades to a non UUPS compliant implementation. This prevents upgrades to an implementation contract that wouldn't contain the necessary upgrade mechanism, as it would lock the upgradeability of the proxy forever. This security mechanism can be bypassed by either of: - -- Adding a flag mechanism in the implementation that will disable the upgrade function when triggered. -- Upgrading to an implementation that features an upgrade mechanism without the additional security check, and then upgrading again to another implementation without the upgrade mechanism. - -== Core - -{{Proxy}} - -== ERC1967 - -{{ERC1967Proxy}} - -{{ERC1967Upgrade}} - -== Transparent Proxy - -{{TransparentUpgradeableProxy}} - -{{ProxyAdmin}} - -== Beacon - -{{BeaconProxy}} - -{{IBeacon}} - -{{UpgradeableBeacon}} - -== Minimal Clones - -{{Clones}} - -== Utils - -{{Initializable}} - -{{UUPSUpgradeable}} diff --git a/certora/munged/proxy/beacon/BeaconProxy.sol b/certora/munged/proxy/beacon/BeaconProxy.sol deleted file mode 100644 index ff8970509..000000000 --- a/certora/munged/proxy/beacon/BeaconProxy.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/beacon/BeaconProxy.sol) - -pragma solidity ^0.8.0; - -import "./IBeacon.sol"; -import "../Proxy.sol"; -import "../ERC1967/ERC1967Upgrade.sol"; - -/** - * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}. - * - * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't - * conflict with the storage layout of the implementation behind the proxy. - * - * _Available since v3.4._ - */ -contract BeaconProxy is Proxy, ERC1967Upgrade { - /** - * @dev Initializes the proxy with `beacon`. - * - * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This - * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity - * constructor. - * - * Requirements: - * - * - `beacon` must be a contract with the interface {IBeacon}. - */ - constructor(address beacon, bytes memory data) payable { - assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1)); - _upgradeBeaconToAndCall(beacon, data, false); - } - - /** - * @dev Returns the current beacon address. - */ - function _beacon() internal view virtual returns (address) { - return _getBeacon(); - } - - /** - * @dev Returns the current implementation address of the associated beacon. - */ - function _implementation() internal view virtual override returns (address) { - return IBeacon(_getBeacon()).implementation(); - } - - /** - * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}. - * - * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. - * - * Requirements: - * - * - `beacon` must be a contract. - * - The implementation returned by `beacon` must be a contract. - */ - function _setBeacon(address beacon, bytes memory data) internal virtual { - _upgradeBeaconToAndCall(beacon, data, false); - } -} diff --git a/certora/munged/proxy/beacon/IBeacon.sol b/certora/munged/proxy/beacon/IBeacon.sol deleted file mode 100644 index efa33d11c..000000000 --- a/certora/munged/proxy/beacon/IBeacon.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/beacon/IBeacon.sol) - -pragma solidity ^0.8.0; - -/** - * @dev This is the interface that {BeaconProxy} expects of its beacon. - */ -interface IBeacon { - /** - * @dev Must return an address that can be used as a delegate call target. - * - * {BeaconProxy} will check that this address is a contract. - */ - function implementation() external view returns (address); -} diff --git a/certora/munged/proxy/beacon/UpgradeableBeacon.sol b/certora/munged/proxy/beacon/UpgradeableBeacon.sol deleted file mode 100644 index 4cbb5f49c..000000000 --- a/certora/munged/proxy/beacon/UpgradeableBeacon.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/beacon/UpgradeableBeacon.sol) - -pragma solidity ^0.8.0; - -import "./IBeacon.sol"; -import "../../access/Ownable.sol"; -import "../../utils/Address.sol"; - -/** - * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their - * implementation contract, which is where they will delegate all function calls. - * - * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon. - */ -contract UpgradeableBeacon is IBeacon, Ownable { - address private _implementation; - - /** - * @dev Emitted when the implementation returned by the beacon is changed. - */ - event Upgraded(address indexed implementation); - - /** - * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the - * beacon. - */ - constructor(address implementation_) { - _setImplementation(implementation_); - } - - /** - * @dev Returns the current implementation address. - */ - function implementation() public view virtual override returns (address) { - return _implementation; - } - - /** - * @dev Upgrades the beacon to a new implementation. - * - * Emits an {Upgraded} event. - * - * Requirements: - * - * - msg.sender must be the owner of the contract. - * - `newImplementation` must be a contract. - */ - function upgradeTo(address newImplementation) public virtual onlyOwner { - _setImplementation(newImplementation); - emit Upgraded(newImplementation); - } - - /** - * @dev Sets the implementation contract address for this beacon - * - * Requirements: - * - * - `newImplementation` must be a contract. - */ - function _setImplementation(address newImplementation) private { - require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract"); - _implementation = newImplementation; - } -} diff --git a/certora/munged/proxy/transparent/ProxyAdmin.sol b/certora/munged/proxy/transparent/ProxyAdmin.sol deleted file mode 100644 index 18a6ef301..000000000 --- a/certora/munged/proxy/transparent/ProxyAdmin.sol +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/transparent/ProxyAdmin.sol) - -pragma solidity ^0.8.0; - -import "./TransparentUpgradeableProxy.sol"; -import "../../access/Ownable.sol"; - -/** - * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an - * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}. - */ -contract ProxyAdmin is Ownable { - /** - * @dev Returns the current implementation of `proxy`. - * - * Requirements: - * - * - This contract must be the admin of `proxy`. - */ - function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) { - // We need to manually run the static call since the getter cannot be flagged as view - // bytes4(keccak256("implementation()")) == 0x5c60da1b - (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b"); - require(success); - return abi.decode(returndata, (address)); - } - - /** - * @dev Returns the current admin of `proxy`. - * - * Requirements: - * - * - This contract must be the admin of `proxy`. - */ - function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) { - // We need to manually run the static call since the getter cannot be flagged as view - // bytes4(keccak256("admin()")) == 0xf851a440 - (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440"); - require(success); - return abi.decode(returndata, (address)); - } - - /** - * @dev Changes the admin of `proxy` to `newAdmin`. - * - * Requirements: - * - * - This contract must be the current admin of `proxy`. - */ - function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner { - proxy.changeAdmin(newAdmin); - } - - /** - * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}. - * - * Requirements: - * - * - This contract must be the admin of `proxy`. - */ - function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner { - proxy.upgradeTo(implementation); - } - - /** - * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See - * {TransparentUpgradeableProxy-upgradeToAndCall}. - * - * Requirements: - * - * - This contract must be the admin of `proxy`. - */ - function upgradeAndCall( - TransparentUpgradeableProxy proxy, - address implementation, - bytes memory data - ) public payable virtual onlyOwner { - proxy.upgradeToAndCall{value: msg.value}(implementation, data); - } -} diff --git a/certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol b/certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol deleted file mode 100644 index 7d1b7021d..000000000 --- a/certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/transparent/TransparentUpgradeableProxy.sol) - -pragma solidity ^0.8.0; - -import "../ERC1967/ERC1967Proxy.sol"; - -/** - * @dev This contract implements a proxy that is upgradeable by an admin. - * - * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector - * clashing], which can potentially be used in an attack, this contract uses the - * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two - * things that go hand in hand: - * - * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if - * that call matches one of the admin functions exposed by the proxy itself. - * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the - * implementation. If the admin tries to call a function on the implementation it will fail with an error that says - * "admin cannot fallback to proxy target". - * - * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing - * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due - * to sudden errors when trying to call a function from the proxy implementation. - * - * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, - * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy. - */ -contract TransparentUpgradeableProxy is ERC1967Proxy { - /** - * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and - * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}. - */ - constructor( - address _logic, - address admin_, - bytes memory _data - ) payable ERC1967Proxy(_logic, _data) { - assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); - _changeAdmin(admin_); - } - - /** - * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. - */ - modifier ifAdmin() { - if (msg.sender == _getAdmin()) { - _; - } else { - _fallback(); - } - } - - /** - * @dev Returns the current admin. - * - * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. - * - * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the - * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. - * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` - */ - function admin() external ifAdmin returns (address admin_) { - admin_ = _getAdmin(); - } - - /** - * @dev Returns the current implementation. - * - * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. - * - * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the - * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. - * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` - */ - function implementation() external ifAdmin returns (address implementation_) { - implementation_ = _implementation(); - } - - /** - * @dev Changes the admin of the proxy. - * - * Emits an {AdminChanged} event. - * - * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}. - */ - function changeAdmin(address newAdmin) external virtual ifAdmin { - _changeAdmin(newAdmin); - } - - /** - * @dev Upgrade the implementation of the proxy. - * - * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}. - */ - function upgradeTo(address newImplementation) external ifAdmin { - _upgradeToAndCall(newImplementation, bytes(""), false); - } - - /** - * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified - * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the - * proxied contract. - * - * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}. - */ - function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin { - _upgradeToAndCall(newImplementation, data, true); - } - - /** - * @dev Returns the current admin. - */ - function _admin() internal view virtual returns (address) { - return _getAdmin(); - } - - /** - * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}. - */ - function _beforeFallback() internal virtual override { - require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target"); - super._beforeFallback(); - } -} diff --git a/certora/munged/proxy/utils/Initializable.sol b/certora/munged/proxy/utils/Initializable.sol deleted file mode 100644 index 12c77dca7..000000000 --- a/certora/munged/proxy/utils/Initializable.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/utils/Initializable.sol) - -pragma solidity ^0.8.0; - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the - * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() initializer {} - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - */ - bool private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Modifier to protect an initializer function from being invoked twice. - */ - modifier initializer() { - require(_initializing || !_initialized, "Initializable: contract is already initialized"); - - bool isTopLevelCall = !_initializing; - if (isTopLevelCall) { - _initializing = true; - _initialized = true; - } - - _; - - if (isTopLevelCall) { - _initializing = false; - } - } -} diff --git a/certora/munged/proxy/utils/UUPSUpgradeable.sol b/certora/munged/proxy/utils/UUPSUpgradeable.sol deleted file mode 100644 index 9969100d2..000000000 --- a/certora/munged/proxy/utils/UUPSUpgradeable.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (proxy/utils/UUPSUpgradeable.sol) - -pragma solidity ^0.8.0; - -import "../ERC1967/ERC1967Upgrade.sol"; - -/** - * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an - * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy. - * - * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is - * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing - * `UUPSUpgradeable` with a custom implementation of upgrades. - * - * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism. - * - * _Available since v4.1._ - */ -abstract contract UUPSUpgradeable is ERC1967Upgrade { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment - address private immutable __self = address(this); - - /** - * @dev Check that the execution is being performed through a delegatecall call and that the execution context is - * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case - * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a - * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to - * fail. - */ - modifier onlyProxy() { - require(address(this) != __self, "Function must be called through delegatecall"); - require(_getImplementation() == __self, "Function must be called through active proxy"); - _; - } - - /** - * @dev Upgrade the implementation of the proxy to `newImplementation`. - * - * Calls {_authorizeUpgrade}. - * - * Emits an {Upgraded} event. - */ - function upgradeTo(address newImplementation) external virtual onlyProxy { - _authorizeUpgrade(newImplementation); - _upgradeToAndCallSecure(newImplementation, new bytes(0), false); - } - - /** - * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call - * encoded in `data`. - * - * Calls {_authorizeUpgrade}. - * - * Emits an {Upgraded} event. - */ - function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy { - _authorizeUpgrade(newImplementation); - _upgradeToAndCallSecure(newImplementation, data, true); - } - - /** - * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by - * {upgradeTo} and {upgradeToAndCall}. - * - * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}. - * - * ```solidity - * function _authorizeUpgrade(address) internal override onlyOwner {} - * ``` - */ - function _authorizeUpgrade(address newImplementation) internal virtual; -} diff --git a/certora/munged/security/Pausable.sol b/certora/munged/security/Pausable.sol deleted file mode 100644 index bca14e968..000000000 --- a/certora/munged/security/Pausable.sol +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (security/Pausable.sol) - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - require(!paused(), "Pausable: paused"); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - require(paused(), "Pausable: not paused"); - _; - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} diff --git a/certora/munged/security/PullPayment.sol b/certora/munged/security/PullPayment.sol deleted file mode 100644 index df03ec08a..000000000 --- a/certora/munged/security/PullPayment.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (security/PullPayment.sol) - -pragma solidity ^0.8.0; - -import "../utils/escrow/Escrow.sol"; - -/** - * @dev Simple implementation of a - * https://consensys.github.io/smart-contract-best-practices/recommendations/#favor-pull-over-push-for-external-calls[pull-payment] - * strategy, where the paying contract doesn't interact directly with the - * receiver account, which must withdraw its payments itself. - * - * Pull-payments are often considered the best practice when it comes to sending - * Ether, security-wise. It prevents recipients from blocking execution, and - * eliminates reentrancy concerns. - * - * TIP: If you would like to learn more about reentrancy and alternative ways - * to protect against it, check out our blog post - * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. - * - * To use, derive from the `PullPayment` contract, and use {_asyncTransfer} - * instead of Solidity's `transfer` function. Payees can query their due - * payments with {payments}, and retrieve them with {withdrawPayments}. - */ -abstract contract PullPayment { - Escrow private immutable _escrow; - - constructor() { - _escrow = new Escrow(); - } - - /** - * @dev Withdraw accumulated payments, forwarding all gas to the recipient. - * - * Note that _any_ account can call this function, not just the `payee`. - * This means that contracts unaware of the `PullPayment` protocol can still - * receive funds this way, by having a separate account call - * {withdrawPayments}. - * - * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. - * Make sure you trust the recipient, or are either following the - * checks-effects-interactions pattern or using {ReentrancyGuard}. - * - * @param payee Whose payments will be withdrawn. - */ - function withdrawPayments(address payable payee) public virtual { - _escrow.withdraw(payee); - } - - /** - * @dev Returns the payments owed to an address. - * @param dest The creditor's address. - */ - function payments(address dest) public view returns (uint256) { - return _escrow.depositsOf(dest); - } - - /** - * @dev Called by the payer to store the sent amount as credit to be pulled. - * Funds sent in this way are stored in an intermediate {Escrow} contract, so - * there is no danger of them being spent before withdrawal. - * - * @param dest The destination address of the funds. - * @param amount The amount to transfer. - */ - function _asyncTransfer(address dest, uint256 amount) internal virtual { - _escrow.deposit{value: amount}(dest); - } -} diff --git a/certora/munged/security/README.adoc b/certora/munged/security/README.adoc deleted file mode 100644 index 66f398fec..000000000 --- a/certora/munged/security/README.adoc +++ /dev/null @@ -1,20 +0,0 @@ -= Security - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/security - -These contracts aim to cover common security practices. - -* {PullPayment}: A pattern that can be used to avoid reentrancy attacks. -* {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions. -* {Pausable}: A common emergency response mechanism that can pause functionality while a remediation is pending. - -TIP: For an overview on reentrancy and the possible mechanisms to prevent it, read our article https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. - -== Contracts - -{{PullPayment}} - -{{ReentrancyGuard}} - -{{Pausable}} diff --git a/certora/munged/security/ReentrancyGuard.sol b/certora/munged/security/ReentrancyGuard.sol deleted file mode 100644 index aea68d3db..000000000 --- a/certora/munged/security/ReentrancyGuard.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (security/ReentrancyGuard.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Contract module that helps prevent reentrant calls to a function. - * - * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier - * available, which can be applied to functions to make sure there are no nested - * (reentrant) calls to them. - * - * Note that because there is a single `nonReentrant` guard, functions marked as - * `nonReentrant` may not call one another. This can be worked around by making - * those functions `private`, and then adding `external` `nonReentrant` entry - * points to them. - * - * TIP: If you would like to learn more about reentrancy and alternative ways - * to protect against it, check out our blog post - * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. - */ -abstract contract ReentrancyGuard { - // Booleans are more expensive than uint256 or any type that takes up a full - // word because each write operation emits an extra SLOAD to first read the - // slot's contents, replace the bits taken up by the boolean, and then write - // back. This is the compiler's defense against contract upgrades and - // pointer aliasing, and it cannot be disabled. - - // The values being non-zero value makes deployment a bit more expensive, - // but in exchange the refund on every call to nonReentrant will be lower in - // amount. Since refunds are capped to a percentage of the total - // transaction's gas, it is best to keep them low in cases like this one, to - // increase the likelihood of the full refund coming into effect. - uint256 private constant _NOT_ENTERED = 1; - uint256 private constant _ENTERED = 2; - - uint256 private _status; - - constructor() { - _status = _NOT_ENTERED; - } - - /** - * @dev Prevents a contract from calling itself, directly or indirectly. - * Calling a `nonReentrant` function from another `nonReentrant` - * function is not supported. It is possible to prevent this from happening - * by making the `nonReentrant` function external, and making it call a - * `private` function that does the actual work. - */ - modifier nonReentrant() { - // On the first call to nonReentrant, _notEntered will be true - require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); - - // Any calls to nonReentrant after this point will fail - _status = _ENTERED; - - _; - - // By storing the original value once again, a refund is triggered (see - // https://eips.ethereum.org/EIPS/eip-2200) - _status = _NOT_ENTERED; - } -} diff --git a/certora/munged/token/ERC1155/ERC1155.sol b/certora/munged/token/ERC1155/ERC1155.sol deleted file mode 100644 index 38e101e64..000000000 --- a/certora/munged/token/ERC1155/ERC1155.sol +++ /dev/null @@ -1,464 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC1155/ERC1155.sol) - -pragma solidity ^0.8.0; - -import "./IERC1155.sol"; -import "./IERC1155Receiver.sol"; -import "./extensions/IERC1155MetadataURI.sol"; -import "../../utils/Address.sol"; -import "../../utils/Context.sol"; -import "../../utils/introspection/ERC165.sol"; - -/** - * @dev Implementation of the basic standard multi-token. - * See https://eips.ethereum.org/EIPS/eip-1155 - * Originally based on code by Enjin: https://github.com/enjin/erc-1155 - * - * _Available since v3.1._ - */ -contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { - using Address for address; - - // Mapping from token ID to account balances - mapping(uint256 => mapping(address => uint256)) private _balances; - - // Mapping from account to operator approvals - mapping(address => mapping(address => bool)) private _operatorApprovals; - - // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json - string private _uri; - - /** - * @dev See {_setURI}. - */ - constructor(string memory uri_) { - _setURI(uri_); - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { - return - interfaceId == type(IERC1155).interfaceId || - interfaceId == type(IERC1155MetadataURI).interfaceId || - super.supportsInterface(interfaceId); - } - - /** - * @dev See {IERC1155MetadataURI-uri}. - * - * This implementation returns the same URI for *all* token types. It relies - * on the token type ID substitution mechanism - * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. - * - * Clients calling this function must replace the `\{id\}` substring with the - * actual token type ID. - */ - function uri(uint256) public view virtual override returns (string memory) { - return _uri; - } - - /** - * @dev See {IERC1155-balanceOf}. - * - * Requirements: - * - * - `account` cannot be the zero address. - */ - function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { - require(account != address(0), "ERC1155: balance query for the zero address"); - return _balances[id][account]; - } - - /** - * @dev See {IERC1155-balanceOfBatch}. - * - * Requirements: - * - * - `accounts` and `ids` must have the same length. - */ - function balanceOfBatch(address[] memory accounts, uint256[] memory ids) - public - view - virtual - override - returns (uint256[] memory) - { - require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch"); - - uint256[] memory batchBalances = new uint256[](accounts.length); - - for (uint256 i = 0; i < accounts.length; ++i) { - batchBalances[i] = balanceOf(accounts[i], ids[i]); - } - - return batchBalances; - } - - /** - * @dev See {IERC1155-setApprovalForAll}. - */ - function setApprovalForAll(address operator, bool approved) public virtual override { - _setApprovalForAll(_msgSender(), operator, approved); - } - - /** - * @dev See {IERC1155-isApprovedForAll}. - */ - function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { - return _operatorApprovals[account][operator]; - } - - /** - * @dev See {IERC1155-safeTransferFrom}. - */ - function safeTransferFrom( - address from, - address to, - uint256 id, - uint256 amount, - bytes memory data - ) public virtual override { - require( - from == _msgSender() || isApprovedForAll(from, _msgSender()), - "ERC1155: caller is not owner nor approved" - ); - _safeTransferFrom(from, to, id, amount, data); - } - - /** - * @dev See {IERC1155-safeBatchTransferFrom}. - */ - function safeBatchTransferFrom( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) public virtual override { - require( - from == _msgSender() || isApprovedForAll(from, _msgSender()), - "ERC1155: transfer caller is not owner nor approved" - ); - _safeBatchTransferFrom(from, to, ids, amounts, data); - } - - /** - * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. - * - * Emits a {TransferSingle} event. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - `from` must have a balance of tokens of type `id` of at least `amount`. - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the - * acceptance magic value. - */ - function _safeTransferFrom( - address from, - address to, - uint256 id, - uint256 amount, - bytes memory data - ) internal virtual { - require(to != address(0), "ERC1155: transfer to the zero address"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data); - - uint256 fromBalance = _balances[id][from]; - require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); - unchecked { - _balances[id][from] = fromBalance - amount; - } - _balances[id][to] += amount; - - emit TransferSingle(operator, from, to, id, amount); - - _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); - } - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. - * - * Emits a {TransferBatch} event. - * - * Requirements: - * - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the - * acceptance magic value. - */ - function _safeBatchTransferFrom( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual { - require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); - require(to != address(0), "ERC1155: transfer to the zero address"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, from, to, ids, amounts, data); - - for (uint256 i = 0; i < ids.length; ++i) { - uint256 id = ids[i]; - uint256 amount = amounts[i]; - - uint256 fromBalance = _balances[id][from]; - require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); - unchecked { - _balances[id][from] = fromBalance - amount; - } - _balances[id][to] += amount; - } - - emit TransferBatch(operator, from, to, ids, amounts); - - _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); - } - - /** - * @dev Sets a new URI for all token types, by relying on the token type ID - * substitution mechanism - * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. - * - * By this mechanism, any occurrence of the `\{id\}` substring in either the - * URI or any of the amounts in the JSON file at said URI will be replaced by - * clients with the token type ID. - * - * For example, the `https://token-cdn-domain/\{id\}.json` URI would be - * interpreted by clients as - * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` - * for token type ID 0x4cce0. - * - * See {uri}. - * - * Because these URIs cannot be meaningfully represented by the {URI} event, - * this function emits no events. - */ - function _setURI(string memory newuri) internal virtual { - _uri = newuri; - } - - /** - * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`. - * - * Emits a {TransferSingle} event. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the - * acceptance magic value. - */ - function _mint( - address to, - uint256 id, - uint256 amount, - bytes memory data - ) internal virtual { - require(to != address(0), "ERC1155: mint to the zero address"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, address(0), to, _asSingletonArray(id), _asSingletonArray(amount), data); - - _balances[id][to] += amount; - emit TransferSingle(operator, address(0), to, id, amount); - - _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data); - } - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}. - * - * Requirements: - * - * - `ids` and `amounts` must have the same length. - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the - * acceptance magic value. - */ - function _mintBatch( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual { - require(to != address(0), "ERC1155: mint to the zero address"); - require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); - - for (uint256 i = 0; i < ids.length; i++) { - _balances[ids[i]][to] += amounts[i]; - } - - emit TransferBatch(operator, address(0), to, ids, amounts); - - _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data); - } - - /** - * @dev Destroys `amount` tokens of token type `id` from `from` - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `from` must have at least `amount` tokens of token type `id`. - */ - function _burn( - address from, - uint256 id, - uint256 amount - ) internal virtual { - require(from != address(0), "ERC1155: burn from the zero address"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, from, address(0), _asSingletonArray(id), _asSingletonArray(amount), ""); - - uint256 fromBalance = _balances[id][from]; - require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); - unchecked { - _balances[id][from] = fromBalance - amount; - } - - emit TransferSingle(operator, from, address(0), id, amount); - } - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}. - * - * Requirements: - * - * - `ids` and `amounts` must have the same length. - */ - function _burnBatch( - address from, - uint256[] memory ids, - uint256[] memory amounts - ) internal virtual { - require(from != address(0), "ERC1155: burn from the zero address"); - require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); - - for (uint256 i = 0; i < ids.length; i++) { - uint256 id = ids[i]; - uint256 amount = amounts[i]; - - uint256 fromBalance = _balances[id][from]; - require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); - unchecked { - _balances[id][from] = fromBalance - amount; - } - } - - emit TransferBatch(operator, from, address(0), ids, amounts); - } - - /** - * @dev Approve `operator` to operate on all of `owner` tokens - * - * Emits a {ApprovalForAll} event. - */ - function _setApprovalForAll( - address owner, - address operator, - bool approved - ) internal virtual { - require(owner != operator, "ERC1155: setting approval status for self"); - _operatorApprovals[owner][operator] = approved; - emit ApprovalForAll(owner, operator, approved); - } - - /** - * @dev Hook that is called before any token transfer. This includes minting - * and burning, as well as batched variants. - * - * The same hook is called on both single and batched variants. For single - * transfers, the length of the `id` and `amount` arrays will be 1. - * - * Calling conditions (for each `id` and `amount` pair): - * - * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * of token type `id` will be transferred to `to`. - * - When `from` is zero, `amount` tokens of token type `id` will be minted - * for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` - * will be burned. - * - `from` and `to` are never both zero. - * - `ids` and `amounts` have the same, non-zero length. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual {} - - function _doSafeTransferAcceptanceCheck( - address operator, - address from, - address to, - uint256 id, - uint256 amount, - bytes memory data - ) private { - if (to.isContract()) { - try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { - if (response != IERC1155Receiver.onERC1155Received.selector) { - revert("ERC1155: ERC1155Receiver rejected tokens"); - } - } catch Error(string memory reason) { - revert(reason); - } catch { - revert("ERC1155: transfer to non ERC1155Receiver implementer"); - } - } - } - - function _doSafeBatchTransferAcceptanceCheck( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) private { - if (to.isContract()) { - try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( - bytes4 response - ) { - if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { - revert("ERC1155: ERC1155Receiver rejected tokens"); - } - } catch Error(string memory reason) { - revert(reason); - } catch { - revert("ERC1155: transfer to non ERC1155Receiver implementer"); - } - } - } - - function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { - uint256[] memory array = new uint256[](1); - array[0] = element; - - return array; - } -} diff --git a/certora/munged/token/ERC1155/IERC1155.sol b/certora/munged/token/ERC1155/IERC1155.sol deleted file mode 100644 index c6c204898..000000000 --- a/certora/munged/token/ERC1155/IERC1155.sol +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC1155/IERC1155.sol) - -pragma solidity ^0.8.0; - -import "../../utils/introspection/IERC165.sol"; - -/** - * @dev Required interface of an ERC1155 compliant contract, as defined in the - * https://eips.ethereum.org/EIPS/eip-1155[EIP]. - * - * _Available since v3.1._ - */ -interface IERC1155 is IERC165 { - /** - * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. - */ - event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); - - /** - * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all - * transfers. - */ - event TransferBatch( - address indexed operator, - address indexed from, - address indexed to, - uint256[] ids, - uint256[] values - ); - - /** - * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to - * `approved`. - */ - event ApprovalForAll(address indexed account, address indexed operator, bool approved); - - /** - * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. - * - * If an {URI} event was emitted for `id`, the standard - * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value - * returned by {IERC1155MetadataURI-uri}. - */ - event URI(string value, uint256 indexed id); - - /** - * @dev Returns the amount of tokens of token type `id` owned by `account`. - * - * Requirements: - * - * - `account` cannot be the zero address. - */ - function balanceOf(address account, uint256 id) external view returns (uint256); - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. - * - * Requirements: - * - * - `accounts` and `ids` must have the same length. - */ - function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) - external - view - returns (uint256[] memory); - - /** - * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, - * - * Emits an {ApprovalForAll} event. - * - * Requirements: - * - * - `operator` cannot be the caller. - */ - function setApprovalForAll(address operator, bool approved) external; - - /** - * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. - * - * See {setApprovalForAll}. - */ - function isApprovedForAll(address account, address operator) external view returns (bool); - - /** - * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. - * - * Emits a {TransferSingle} event. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}. - * - `from` must have a balance of tokens of type `id` of at least `amount`. - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the - * acceptance magic value. - */ - function safeTransferFrom( - address from, - address to, - uint256 id, - uint256 amount, - bytes calldata data - ) external; - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. - * - * Emits a {TransferBatch} event. - * - * Requirements: - * - * - `ids` and `amounts` must have the same length. - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the - * acceptance magic value. - */ - function safeBatchTransferFrom( - address from, - address to, - uint256[] calldata ids, - uint256[] calldata amounts, - bytes calldata data - ) external; -} diff --git a/certora/munged/token/ERC1155/IERC1155Receiver.sol b/certora/munged/token/ERC1155/IERC1155Receiver.sol deleted file mode 100644 index b7f7f7373..000000000 --- a/certora/munged/token/ERC1155/IERC1155Receiver.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC1155/IERC1155Receiver.sol) - -pragma solidity ^0.8.0; - -import "../../utils/introspection/IERC165.sol"; - -/** - * @dev _Available since v3.1._ - */ -interface IERC1155Receiver is IERC165 { - /** - * @dev Handles the receipt of a single ERC1155 token type. This function is - * called at the end of a `safeTransferFrom` after the balance has been updated. - * - * NOTE: To accept the transfer, this must return - * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` - * (i.e. 0xf23a6e61, or its own function selector). - * - * @param operator The address which initiated the transfer (i.e. msg.sender) - * @param from The address which previously owned the token - * @param id The ID of the token being transferred - * @param value The amount of tokens being transferred - * @param data Additional data with no specified format - * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed - */ - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes calldata data - ) external returns (bytes4); - - /** - * @dev Handles the receipt of a multiple ERC1155 token types. This function - * is called at the end of a `safeBatchTransferFrom` after the balances have - * been updated. - * - * NOTE: To accept the transfer(s), this must return - * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` - * (i.e. 0xbc197c81, or its own function selector). - * - * @param operator The address which initiated the batch transfer (i.e. msg.sender) - * @param from The address which previously owned the token - * @param ids An array containing ids of each token being transferred (order and length must match values array) - * @param values An array containing amounts of each token being transferred (order and length must match ids array) - * @param data Additional data with no specified format - * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed - */ - function onERC1155BatchReceived( - address operator, - address from, - uint256[] calldata ids, - uint256[] calldata values, - bytes calldata data - ) external returns (bytes4); -} diff --git a/certora/munged/token/ERC1155/README.adoc b/certora/munged/token/ERC1155/README.adoc deleted file mode 100644 index 2e0b22bae..000000000 --- a/certora/munged/token/ERC1155/README.adoc +++ /dev/null @@ -1,47 +0,0 @@ -= ERC 1155 - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc1155 - -This set of interfaces and contracts are all related to the https://eips.ethereum.org/EIPS/eip-1155[ERC1155 Multi Token Standard]. - -The EIP consists of three interfaces which fulfill different roles, found here as {IERC1155}, {IERC1155MetadataURI} and {IERC1155Receiver}. - -{ERC1155} implements the mandatory {IERC1155} interface, as well as the optional extension {IERC1155MetadataURI}, by relying on the substitution mechanism to use the same URI for all token types, dramatically reducing gas costs. - -Additionally there are multiple custom extensions, including: - -* designation of addresses that can pause token transfers for all users ({ERC1155Pausable}). -* destruction of own tokens ({ERC1155Burnable}). - -NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC1155 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc1155.adoc#Presets[ERC1155 Presets] (such as {ERC1155PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. - -== Core - -{{IERC1155}} - -{{IERC1155MetadataURI}} - -{{ERC1155}} - -{{IERC1155Receiver}} - -{{ERC1155Receiver}} - -== Extensions - -{{ERC1155Pausable}} - -{{ERC1155Burnable}} - -{{ERC1155Supply}} - -== Presets - -These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. - -{{ERC1155PresetMinterPauser}} - -== Utilities - -{{ERC1155Holder}} diff --git a/certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol b/certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol deleted file mode 100644 index a16d840d9..000000000 --- a/certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC1155/extensions/ERC1155Burnable.sol) - -pragma solidity ^0.8.0; - -import "../ERC1155.sol"; - -/** - * @dev Extension of {ERC1155} that allows token holders to destroy both their - * own tokens and those that they have been approved to use. - * - * _Available since v3.1._ - */ -abstract contract ERC1155Burnable is ERC1155 { - function burn( - address account, - uint256 id, - uint256 value - ) public virtual { - require( - account == _msgSender() || isApprovedForAll(account, _msgSender()), - "ERC1155: caller is not owner nor approved" - ); - - _burn(account, id, value); - } - - function burnBatch( - address account, - uint256[] memory ids, - uint256[] memory values - ) public virtual { - require( - account == _msgSender() || isApprovedForAll(account, _msgSender()), - "ERC1155: caller is not owner nor approved" - ); - - _burnBatch(account, ids, values); - } -} diff --git a/certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol b/certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol deleted file mode 100644 index d08be8ced..000000000 --- a/certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC1155/extensions/ERC1155Pausable.sol) - -pragma solidity ^0.8.0; - -import "../ERC1155.sol"; -import "../../../security/Pausable.sol"; - -/** - * @dev ERC1155 token with pausable token transfers, minting and burning. - * - * Useful for scenarios such as preventing trades until the end of an evaluation - * period, or having an emergency switch for freezing all token transfers in the - * event of a large bug. - * - * _Available since v3.1._ - */ -abstract contract ERC1155Pausable is ERC1155, Pausable { - /** - * @dev See {ERC1155-_beforeTokenTransfer}. - * - * Requirements: - * - * - the contract must not be paused. - */ - function _beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual override { - super._beforeTokenTransfer(operator, from, to, ids, amounts, data); - - require(!paused(), "ERC1155Pausable: token transfer while paused"); - } -} diff --git a/certora/munged/token/ERC1155/extensions/ERC1155Supply.sol b/certora/munged/token/ERC1155/extensions/ERC1155Supply.sol deleted file mode 100644 index 693bef63e..000000000 --- a/certora/munged/token/ERC1155/extensions/ERC1155Supply.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC1155/extensions/ERC1155Supply.sol) - -pragma solidity ^0.8.0; - -import "../ERC1155.sol"; - -/** - * @dev Extension of ERC1155 that adds tracking of total supply per id. - * - * Useful for scenarios where Fungible and Non-fungible tokens have to be - * clearly identified. Note: While a totalSupply of 1 might mean the - * corresponding is an NFT, there is no guarantees that no other token with the - * same id are not going to be minted. - */ -abstract contract ERC1155Supply is ERC1155 { - mapping(uint256 => uint256) private _totalSupply; - - /** - * @dev Total amount of tokens in with a given id. - */ - function totalSupply(uint256 id) public view virtual returns (uint256) { - return _totalSupply[id]; - } - - /** - * @dev Indicates whether any token exist with a given id, or not. - */ - function exists(uint256 id) public view virtual returns (bool) { - return ERC1155Supply.totalSupply(id) > 0; - } - - /** - * @dev See {ERC1155-_beforeTokenTransfer}. - */ - function _beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual override { - super._beforeTokenTransfer(operator, from, to, ids, amounts, data); - - if (from == address(0)) { - for (uint256 i = 0; i < ids.length; ++i) { - _totalSupply[ids[i]] += amounts[i]; - } - } - - if (to == address(0)) { - for (uint256 i = 0; i < ids.length; ++i) { - _totalSupply[ids[i]] -= amounts[i]; - } - } - } -} diff --git a/certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol b/certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol deleted file mode 100644 index f150cfb03..000000000 --- a/certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC1155/extensions/IERC1155MetadataURI.sol) - -pragma solidity ^0.8.0; - -import "../IERC1155.sol"; - -/** - * @dev Interface of the optional ERC1155MetadataExtension interface, as defined - * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP]. - * - * _Available since v3.1._ - */ -interface IERC1155MetadataURI is IERC1155 { - /** - * @dev Returns the URI for token type `id`. - * - * If the `\{id\}` substring is present in the URI, it must be replaced by - * clients with the actual token type ID. - */ - function uri(uint256 id) external view returns (string memory); -} diff --git a/certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol b/certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol deleted file mode 100644 index c7840bc26..000000000 --- a/certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC1155/presets/ERC1155PresetMinterPauser.sol) - -pragma solidity ^0.8.0; - -import "../ERC1155.sol"; -import "../extensions/ERC1155Burnable.sol"; -import "../extensions/ERC1155Pausable.sol"; -import "../../../access/AccessControlEnumerable.sol"; -import "../../../utils/Context.sol"; - -/** - * @dev {ERC1155} token, including: - * - * - ability for holders to burn (destroy) their tokens - * - a minter role that allows for token minting (creation) - * - a pauser role that allows to stop all token transfers - * - * This contract uses {AccessControl} to lock permissioned functions using the - * different roles - head to its documentation for details. - * - * The account that deploys the contract will be granted the minter and pauser - * roles, as well as the default admin role, which will let it grant both minter - * and pauser roles to other accounts. - */ -contract ERC1155PresetMinterPauser is Context, AccessControlEnumerable, ERC1155Burnable, ERC1155Pausable { - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); - bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); - - /** - * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, and `PAUSER_ROLE` to the account that - * deploys the contract. - */ - constructor(string memory uri) ERC1155(uri) { - _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); - - _setupRole(MINTER_ROLE, _msgSender()); - _setupRole(PAUSER_ROLE, _msgSender()); - } - - /** - * @dev Creates `amount` new tokens for `to`, of token type `id`. - * - * See {ERC1155-_mint}. - * - * Requirements: - * - * - the caller must have the `MINTER_ROLE`. - */ - function mint( - address to, - uint256 id, - uint256 amount, - bytes memory data - ) public virtual { - require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint"); - - _mint(to, id, amount, data); - } - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] variant of {mint}. - */ - function mintBatch( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) public virtual { - require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint"); - - _mintBatch(to, ids, amounts, data); - } - - /** - * @dev Pauses all token transfers. - * - * See {ERC1155Pausable} and {Pausable-_pause}. - * - * Requirements: - * - * - the caller must have the `PAUSER_ROLE`. - */ - function pause() public virtual { - require(hasRole(PAUSER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have pauser role to pause"); - _pause(); - } - - /** - * @dev Unpauses all token transfers. - * - * See {ERC1155Pausable} and {Pausable-_unpause}. - * - * Requirements: - * - * - the caller must have the `PAUSER_ROLE`. - */ - function unpause() public virtual { - require(hasRole(PAUSER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have pauser role to unpause"); - _unpause(); - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(AccessControlEnumerable, ERC1155) - returns (bool) - { - return super.supportsInterface(interfaceId); - } - - function _beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual override(ERC1155, ERC1155Pausable) { - super._beforeTokenTransfer(operator, from, to, ids, amounts, data); - } -} diff --git a/certora/munged/token/ERC1155/utils/ERC1155Holder.sol b/certora/munged/token/ERC1155/utils/ERC1155Holder.sol deleted file mode 100644 index d11aa0567..000000000 --- a/certora/munged/token/ERC1155/utils/ERC1155Holder.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC1155/utils/ERC1155Holder.sol) - -pragma solidity ^0.8.0; - -import "./ERC1155Receiver.sol"; - -/** - * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens. - * - * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be - * stuck. - * - * @dev _Available since v3.1._ - */ -contract ERC1155Holder is ERC1155Receiver { - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes memory - ) public virtual override returns (bytes4) { - return this.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] memory, - uint256[] memory, - bytes memory - ) public virtual override returns (bytes4) { - return this.onERC1155BatchReceived.selector; - } -} diff --git a/certora/munged/token/ERC1155/utils/ERC1155Receiver.sol b/certora/munged/token/ERC1155/utils/ERC1155Receiver.sol deleted file mode 100644 index 87b34e2bb..000000000 --- a/certora/munged/token/ERC1155/utils/ERC1155Receiver.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC1155/utils/ERC1155Receiver.sol) - -pragma solidity ^0.8.0; - -import "../IERC1155Receiver.sol"; -import "../../../utils/introspection/ERC165.sol"; - -/** - * @dev _Available since v3.1._ - */ -abstract contract ERC1155Receiver is ERC165, IERC1155Receiver { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { - return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId); - } -} diff --git a/certora/munged/token/ERC20/ERC20.sol b/certora/munged/token/ERC20/ERC20.sol deleted file mode 100644 index eddd84567..000000000 --- a/certora/munged/token/ERC20/ERC20.sol +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.0; - -import "./IERC20.sol"; -import "./extensions/IERC20Metadata.sol"; -import "../../utils/Context.sol"; - -/** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * For a generic mechanism see {ERC20PresetMinterPauser}. - * - * TIP: For a detailed writeup see our guide - * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * We have followed general OpenZeppelin Contracts guidelines: functions revert - * instead returning `false` on failure. This behavior is nonetheless - * conventional and does not conflict with the expectations of ERC20 - * applications. - * - * Additionally, an {Approval} event is emitted on calls to {transferFrom}. - * This allows applications to reconstruct the allowance for all accounts just - * by listening to said events. Other implementations of the EIP may not emit - * these events, as it isn't required by the specification. - * - * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} - * functions have been added to mitigate the well-known issues around setting - * allowances. See {IERC20-approve}. - */ -contract ERC20 is Context, IERC20, IERC20Metadata { - mapping(address => uint256) private _balances; - - mapping(address => mapping(address => uint256)) private _allowances; - - uint256 private _totalSupply; - - string private _name; - string private _symbol; - - /** - * @dev Sets the values for {name} and {symbol}. - * - * The default value of {decimals} is 18. To select a different value for - * {decimals} you should overload it. - * - * All two of these values are immutable: they can only be set once during - * construction. - */ - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - } - - /** - * @dev Returns the name of the token. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5.05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the value {ERC20} uses, unless this function is - * overridden; - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view virtual override returns (uint8) { - return 18; - } - - /** - * @dev See {IERC20-totalSupply}. - */ - function totalSupply() public view virtual override returns (uint256) { - return _totalSupply; - } - - /** - * @dev See {IERC20-balanceOf}. - */ - function balanceOf(address account) public view virtual override returns (uint256) { - return _balances[account]; - } - - /** - * @dev See {IERC20-transfer}. - * - * Requirements: - * - * - `recipient` cannot be the zero address. - * - the caller must have a balance of at least `amount`. - */ - function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(_msgSender(), recipient, amount); - return true; - } - - /** - * @dev See {IERC20-allowance}. - */ - function allowance(address owner, address spender) public view virtual override returns (uint256) { - return _allowances[owner][spender]; - } - - /** - * @dev See {IERC20-approve}. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function approve(address spender, uint256 amount) public virtual override returns (bool) { - _approve(_msgSender(), spender, amount); - return true; - } - - /** - * @dev See {IERC20-transferFrom}. - * - * Emits an {Approval} event indicating the updated allowance. This is not - * required by the EIP. See the note at the beginning of {ERC20}. - * - * Requirements: - * - * - `sender` and `recipient` cannot be the zero address. - * - `sender` must have a balance of at least `amount`. - * - the caller must have allowance for ``sender``'s tokens of at least - * `amount`. - */ - function transferFrom( - address sender, - address recipient, - uint256 amount - ) public virtual override returns (bool) { - _transfer(sender, recipient, amount); - - uint256 currentAllowance = _allowances[sender][_msgSender()]; - require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); - unchecked { - _approve(sender, _msgSender(), currentAllowance - amount); - } - - return true; - } - - /** - * @dev Atomically increases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { - _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue); - return true; - } - - /** - * @dev Atomically decreases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `spender` must have allowance for the caller of at least - * `subtractedValue`. - */ - function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { - uint256 currentAllowance = _allowances[_msgSender()][spender]; - require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); - unchecked { - _approve(_msgSender(), spender, currentAllowance - subtractedValue); - } - - return true; - } - - /** - * @dev Moves `amount` of tokens from `sender` to `recipient`. - * - * This internal function is equivalent to {transfer}, and can be used to - * e.g. implement automatic token fees, slashing mechanisms, etc. - * - * Emits a {Transfer} event. - * - * Requirements: - * - * - `sender` cannot be the zero address. - * - `recipient` cannot be the zero address. - * - `sender` must have a balance of at least `amount`. - */ - function _transfer( - address sender, - address recipient, - uint256 amount - ) internal virtual { - require(sender != address(0), "ERC20: transfer from the zero address"); - require(recipient != address(0), "ERC20: transfer to the zero address"); - - _beforeTokenTransfer(sender, recipient, amount); - - uint256 senderBalance = _balances[sender]; - require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); - unchecked { - _balances[sender] = senderBalance - amount; - } - _balances[recipient] += amount; - - emit Transfer(sender, recipient, amount); - - _afterTokenTransfer(sender, recipient, amount); - } - - /** @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * Emits a {Transfer} event with `from` set to the zero address. - * - * Requirements: - * - * - `account` cannot be the zero address. - */ - function _mint(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: mint to the zero address"); - - _beforeTokenTransfer(address(0), account, amount); - - _totalSupply += amount; - _balances[account] += amount; - emit Transfer(address(0), account, amount); - - _afterTokenTransfer(address(0), account, amount); - } - - /** - * @dev Destroys `amount` tokens from `account`, reducing the - * total supply. - * - * Emits a {Transfer} event with `to` set to the zero address. - * - * Requirements: - * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. - */ - function _burn(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: burn from the zero address"); - - _beforeTokenTransfer(account, address(0), amount); - - uint256 accountBalance = _balances[account]; - require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); - unchecked { - _balances[account] = accountBalance - amount; - } - _totalSupply -= amount; - - emit Transfer(account, address(0), amount); - - _afterTokenTransfer(account, address(0), amount); - } - - /** - * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. - * - * This internal function is equivalent to `approve`, and can be used to - * e.g. set automatic allowances for certain subsystems, etc. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `owner` cannot be the zero address. - * - `spender` cannot be the zero address. - */ - function _approve( - address owner, - address spender, - uint256 amount - ) internal virtual { - require(owner != address(0), "ERC20: approve from the zero address"); - require(spender != address(0), "ERC20: approve to the zero address"); - - _allowances[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - - /** - * @dev Hook that is called before any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual {} - - /** - * @dev Hook that is called after any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * has been transferred to `to`. - * - when `from` is zero, `amount` tokens have been minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens have been burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _afterTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual {} -} diff --git a/certora/munged/token/ERC20/IERC20.sol b/certora/munged/token/ERC20/IERC20.sol deleted file mode 100644 index 1cebc855d..000000000 --- a/certora/munged/token/ERC20/IERC20.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/IERC20.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address recipient, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external returns (bool); - - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); -} diff --git a/certora/munged/token/ERC20/README.adoc b/certora/munged/token/ERC20/README.adoc deleted file mode 100644 index f2892293d..000000000 --- a/certora/munged/token/ERC20/README.adoc +++ /dev/null @@ -1,83 +0,0 @@ -= ERC 20 - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc20 - -This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-20[ERC20 Token Standard]. - -TIP: For an overview of ERC20 tokens and a walk through on how to create a token contract read our xref:ROOT:erc20.adoc[ERC20 guide]. - -There a few core contracts that implement the behavior specified in the EIP: - -* {IERC20}: the interface all ERC20 implementations should conform to. -* {IERC20Metadata}: the extended ERC20 interface including the <>, <> and <> functions. -* {ERC20}: the implementation of the ERC20 interface, including the <>, <> and <> optional standard extension to the base interface. - -Additionally there are multiple custom extensions, including: - -* {ERC20Burnable}: destruction of own tokens. -* {ERC20Capped}: enforcement of a cap to the total supply when minting tokens. -* {ERC20Pausable}: ability to pause token transfers. -* {ERC20Snapshot}: efficient storage of past token balances to be later queried at any point in time. -* {ERC20Permit}: gasless approval of tokens (standardized as ERC2612). -* {ERC20FlashMint}: token level support for flash loans through the minting and burning of ephemeral tokens (standardized as ERC3156). -* {ERC20Votes}: support for voting and vote delegation. -* {ERC20VotesComp}: support for voting and vote delegation (compatible with Compound's token, with uint96 restrictions). -* {ERC20Wrapper}: wrapper to create an ERC20 backed by another ERC20, with deposit and withdraw methods. Useful in conjunction with {ERC20Votes}. - -Finally, there are some utilities to interact with ERC20 contracts in various ways. - -* {SafeERC20}: a wrapper around the interface that eliminates the need to handle boolean return values. -* {TokenTimelock}: hold tokens for a beneficiary until a specified time. - -The following related EIPs are in draft status. - -- {ERC20Permit} - -NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC20 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc20.adoc#Presets[ERC20 Presets] (such as {ERC20PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. - -== Core - -{{IERC20}} - -{{IERC20Metadata}} - -{{ERC20}} - -== Extensions - -{{ERC20Burnable}} - -{{ERC20Capped}} - -{{ERC20Pausable}} - -{{ERC20Snapshot}} - -{{ERC20Votes}} - -{{ERC20VotesComp}} - -{{ERC20Wrapper}} - -{{ERC20FlashMint}} - -== Draft EIPs - -The following EIPs are still in Draft status. Due to their nature as drafts, the details of these contracts may change and we cannot guarantee their xref:ROOT:releases-stability.adoc[stability]. Minor releases of OpenZeppelin Contracts may contain breaking changes for the contracts in this directory, which will be duly announced in the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md[changelog]. The EIPs included here are used by projects in production and this may make them less likely to change significantly. - -{{ERC20Permit}} - -== Presets - -These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. - -{{ERC20PresetMinterPauser}} - -{{ERC20PresetFixedSupply}} - -== Utilities - -{{SafeERC20}} - -{{TokenTimelock}} diff --git a/certora/munged/token/ERC20/extensions/ERC20Burnable.sol b/certora/munged/token/ERC20/extensions/ERC20Burnable.sol deleted file mode 100644 index 12402a982..000000000 --- a/certora/munged/token/ERC20/extensions/ERC20Burnable.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Burnable.sol) - -pragma solidity ^0.8.0; - -import "../ERC20.sol"; -import "../../../utils/Context.sol"; - -/** - * @dev Extension of {ERC20} that allows token holders to destroy both their own - * tokens and those that they have an allowance for, in a way that can be - * recognized off-chain (via event analysis). - */ -abstract contract ERC20Burnable is Context, ERC20 { - /** - * @dev Destroys `amount` tokens from the caller. - * - * See {ERC20-_burn}. - */ - function burn(uint256 amount) public virtual { - _burn(_msgSender(), amount); - } - - /** - * @dev Destroys `amount` tokens from `account`, deducting from the caller's - * allowance. - * - * See {ERC20-_burn} and {ERC20-allowance}. - * - * Requirements: - * - * - the caller must have allowance for ``accounts``'s tokens of at least - * `amount`. - */ - function burnFrom(address account, uint256 amount) public virtual { - uint256 currentAllowance = allowance(account, _msgSender()); - require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance"); - unchecked { - _approve(account, _msgSender(), currentAllowance - amount); - } - _burn(account, amount); - } -} diff --git a/certora/munged/token/ERC20/extensions/ERC20Capped.sol b/certora/munged/token/ERC20/extensions/ERC20Capped.sol deleted file mode 100644 index c85ccce30..000000000 --- a/certora/munged/token/ERC20/extensions/ERC20Capped.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Capped.sol) - -pragma solidity ^0.8.0; - -import "../ERC20.sol"; - -/** - * @dev Extension of {ERC20} that adds a cap to the supply of tokens. - */ -abstract contract ERC20Capped is ERC20 { - uint256 private immutable _cap; - - /** - * @dev Sets the value of the `cap`. This value is immutable, it can only be - * set once during construction. - */ - constructor(uint256 cap_) { - require(cap_ > 0, "ERC20Capped: cap is 0"); - _cap = cap_; - } - - /** - * @dev Returns the cap on the token's total supply. - */ - function cap() public view virtual returns (uint256) { - return _cap; - } - - /** - * @dev See {ERC20-_mint}. - */ - function _mint(address account, uint256 amount) internal virtual override { - require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded"); - super._mint(account, amount); - } -} diff --git a/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol b/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol deleted file mode 100644 index 4ab082f08..000000000 --- a/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20FlashMint.sol) - -pragma solidity ^0.8.0; - -import "../../../interfaces/IERC3156.sol"; -import "../ERC20.sol"; - -/** - * @dev Implementation of the ERC3156 Flash loans extension, as defined in - * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. - * - * Adds the {flashLoan} method, which provides flash loan support at the token - * level. By default there is no fee, but this can be changed by overriding {flashFee}. - * - * _Available since v4.1._ - */ -abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { - bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); - - /** - * @dev Returns the maximum amount of tokens available for loan. - * @param token The address of the token that is requested. - * @return The amont of token that can be loaned. - */ - function maxFlashLoan(address token) public view override returns (uint256) { - return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0; - } - - /** - * @dev Returns the fee applied when doing flash loans. By default this - * implementation has 0 fees. This function can be overloaded to make - * the flash loan mechanism deflationary. - * @param token The token to be flash loaned. - * @param amount The amount of tokens to be loaned. - * @return The fees applied to the corresponding flash loan. - */ - function flashFee(address token, uint256 amount) public view virtual override returns (uint256) { - require(token == address(this), "ERC20FlashMint: wrong token"); - // silence warning about unused variable without the addition of bytecode. - amount; - return 0; - } - - /** - * @dev Performs a flash loan. New tokens are minted and sent to the - * `receiver`, who is required to implement the {IERC3156FlashBorrower} - * interface. By the end of the flash loan, the receiver is expected to own - * amount + fee tokens and have them approved back to the token contract itself so - * they can be burned. - * @param receiver The receiver of the flash loan. Should implement the - * {IERC3156FlashBorrower.onFlashLoan} interface. - * @param token The token to be flash loaned. Only `address(this)` is - * supported. - * @param amount The amount of tokens to be loaned. - * @param data An arbitrary datafield that is passed to the receiver. - * @return `true` is the flash loan was successful. - */ - function flashLoan( - IERC3156FlashBorrower receiver, - address token, - uint256 amount, - bytes calldata data - ) public virtual override returns (bool) { - uint256 fee = flashFee(token, amount); - _mint(address(receiver), amount); - require( - receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, - "ERC20FlashMint: invalid return value" - ); - uint256 currentAllowance = allowance(address(receiver), address(this)); - require(currentAllowance >= amount + fee, "ERC20FlashMint: allowance does not allow refund"); - _approve(address(receiver), address(this), currentAllowance - amount - fee); - _burn(address(receiver), amount + fee); - return true; - } -} diff --git a/certora/munged/token/ERC20/extensions/ERC20Pausable.sol b/certora/munged/token/ERC20/extensions/ERC20Pausable.sol deleted file mode 100644 index 5c4963137..000000000 --- a/certora/munged/token/ERC20/extensions/ERC20Pausable.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Pausable.sol) - -pragma solidity ^0.8.0; - -import "../ERC20.sol"; -import "../../../security/Pausable.sol"; - -/** - * @dev ERC20 token with pausable token transfers, minting and burning. - * - * Useful for scenarios such as preventing trades until the end of an evaluation - * period, or having an emergency switch for freezing all token transfers in the - * event of a large bug. - */ -abstract contract ERC20Pausable is ERC20, Pausable { - /** - * @dev See {ERC20-_beforeTokenTransfer}. - * - * Requirements: - * - * - the contract must not be paused. - */ - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual override { - super._beforeTokenTransfer(from, to, amount); - - require(!paused(), "ERC20Pausable: token transfer while paused"); - } -} diff --git a/certora/munged/token/ERC20/extensions/ERC20Snapshot.sol b/certora/munged/token/ERC20/extensions/ERC20Snapshot.sol deleted file mode 100644 index 6fbf54a3a..000000000 --- a/certora/munged/token/ERC20/extensions/ERC20Snapshot.sol +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Snapshot.sol) - -pragma solidity ^0.8.0; - -import "../ERC20.sol"; -import "../../../utils/Arrays.sol"; -import "../../../utils/Counters.sol"; - -/** - * @dev This contract extends an ERC20 token with a snapshot mechanism. When a snapshot is created, the balances and - * total supply at the time are recorded for later access. - * - * This can be used to safely create mechanisms based on token balances such as trustless dividends or weighted voting. - * In naive implementations it's possible to perform a "double spend" attack by reusing the same balance from different - * accounts. By using snapshots to calculate dividends or voting power, those attacks no longer apply. It can also be - * used to create an efficient ERC20 forking mechanism. - * - * Snapshots are created by the internal {_snapshot} function, which will emit the {Snapshot} event and return a - * snapshot id. To get the total supply at the time of a snapshot, call the function {totalSupplyAt} with the snapshot - * id. To get the balance of an account at the time of a snapshot, call the {balanceOfAt} function with the snapshot id - * and the account address. - * - * NOTE: Snapshot policy can be customized by overriding the {_getCurrentSnapshotId} method. For example, having it - * return `block.number` will trigger the creation of snapshot at the begining of each new block. When overridding this - * function, be careful about the monotonicity of its result. Non-monotonic snapshot ids will break the contract. - * - * Implementing snapshots for every block using this method will incur significant gas costs. For a gas-efficient - * alternative consider {ERC20Votes}. - * - * ==== Gas Costs - * - * Snapshots are efficient. Snapshot creation is _O(1)_. Retrieval of balances or total supply from a snapshot is _O(log - * n)_ in the number of snapshots that have been created, although _n_ for a specific account will generally be much - * smaller since identical balances in subsequent snapshots are stored as a single entry. - * - * There is a constant overhead for normal ERC20 transfers due to the additional snapshot bookkeeping. This overhead is - * only significant for the first transfer that immediately follows a snapshot for a particular account. Subsequent - * transfers will have normal cost until the next snapshot, and so on. - */ - -abstract contract ERC20Snapshot is ERC20 { - // Inspired by Jordi Baylina's MiniMeToken to record historical balances: - // https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol - - using Arrays for uint256[]; - using Counters for Counters.Counter; - - // Snapshotted values have arrays of ids and the value corresponding to that id. These could be an array of a - // Snapshot struct, but that would impede usage of functions that work on an array. - struct Snapshots { - uint256[] ids; - uint256[] values; - } - - mapping(address => Snapshots) private _accountBalanceSnapshots; - Snapshots private _totalSupplySnapshots; - - // Snapshot ids increase monotonically, with the first value being 1. An id of 0 is invalid. - Counters.Counter private _currentSnapshotId; - - /** - * @dev Emitted by {_snapshot} when a snapshot identified by `id` is created. - */ - event Snapshot(uint256 id); - - /** - * @dev Creates a new snapshot and returns its snapshot id. - * - * Emits a {Snapshot} event that contains the same id. - * - * {_snapshot} is `internal` and you have to decide how to expose it externally. Its usage may be restricted to a - * set of accounts, for example using {AccessControl}, or it may be open to the public. - * - * [WARNING] - * ==== - * While an open way of calling {_snapshot} is required for certain trust minimization mechanisms such as forking, - * you must consider that it can potentially be used by attackers in two ways. - * - * First, it can be used to increase the cost of retrieval of values from snapshots, although it will grow - * logarithmically thus rendering this attack ineffective in the long term. Second, it can be used to target - * specific accounts and increase the cost of ERC20 transfers for them, in the ways specified in the Gas Costs - * section above. - * - * We haven't measured the actual numbers; if this is something you're interested in please reach out to us. - * ==== - */ - function _snapshot() internal virtual returns (uint256) { - _currentSnapshotId.increment(); - - uint256 currentId = _getCurrentSnapshotId(); - emit Snapshot(currentId); - return currentId; - } - - /** - * @dev Get the current snapshotId - */ - function _getCurrentSnapshotId() internal view virtual returns (uint256) { - return _currentSnapshotId.current(); - } - - /** - * @dev Retrieves the balance of `account` at the time `snapshotId` was created. - */ - function balanceOfAt(address account, uint256 snapshotId) public view virtual returns (uint256) { - (bool snapshotted, uint256 value) = _valueAt(snapshotId, _accountBalanceSnapshots[account]); - - return snapshotted ? value : balanceOf(account); - } - - /** - * @dev Retrieves the total supply at the time `snapshotId` was created. - */ - function totalSupplyAt(uint256 snapshotId) public view virtual returns (uint256) { - (bool snapshotted, uint256 value) = _valueAt(snapshotId, _totalSupplySnapshots); - - return snapshotted ? value : totalSupply(); - } - - // Update balance and/or total supply snapshots before the values are modified. This is implemented - // in the _beforeTokenTransfer hook, which is executed for _mint, _burn, and _transfer operations. - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual override { - super._beforeTokenTransfer(from, to, amount); - - if (from == address(0)) { - // mint - _updateAccountSnapshot(to); - _updateTotalSupplySnapshot(); - } else if (to == address(0)) { - // burn - _updateAccountSnapshot(from); - _updateTotalSupplySnapshot(); - } else { - // transfer - _updateAccountSnapshot(from); - _updateAccountSnapshot(to); - } - } - - function _valueAt(uint256 snapshotId, Snapshots storage snapshots) private view returns (bool, uint256) { - require(snapshotId > 0, "ERC20Snapshot: id is 0"); - require(snapshotId <= _getCurrentSnapshotId(), "ERC20Snapshot: nonexistent id"); - - // When a valid snapshot is queried, there are three possibilities: - // a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never - // created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds - // to this id is the current one. - // b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the - // requested id, and its value is the one to return. - // c) More snapshots were created after the requested one, and the queried value was later modified. There will be - // no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is - // larger than the requested one. - // - // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if - // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does - // exactly this. - - uint256 index = snapshots.ids.findUpperBound(snapshotId); - - if (index == snapshots.ids.length) { - return (false, 0); - } else { - return (true, snapshots.values[index]); - } - } - - function _updateAccountSnapshot(address account) private { - _updateSnapshot(_accountBalanceSnapshots[account], balanceOf(account)); - } - - function _updateTotalSupplySnapshot() private { - _updateSnapshot(_totalSupplySnapshots, totalSupply()); - } - - function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) private { - uint256 currentId = _getCurrentSnapshotId(); - if (_lastSnapshotId(snapshots.ids) < currentId) { - snapshots.ids.push(currentId); - snapshots.values.push(currentValue); - } - } - - function _lastSnapshotId(uint256[] storage ids) private view returns (uint256) { - if (ids.length == 0) { - return 0; - } else { - return ids[ids.length - 1]; - } - } -} diff --git a/certora/munged/token/ERC20/extensions/ERC20Votes.sol b/certora/munged/token/ERC20/extensions/ERC20Votes.sol deleted file mode 100644 index 06fd68831..000000000 --- a/certora/munged/token/ERC20/extensions/ERC20Votes.sol +++ /dev/null @@ -1,260 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Votes.sol) - -pragma solidity ^0.8.0; - -import "./draft-ERC20Permit.sol"; -import "../../../utils/math/Math.sol"; -import "../../../utils/math/SafeCast.sol"; -import "../../../utils/cryptography/ECDSA.sol"; - -/** - * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's, - * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1. - * - * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module. - * - * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either - * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting - * power can be queried through the public accessors {getVotes} and {getPastVotes}. - * - * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it - * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. - * Enabling self-delegation can easily be done by overriding the {delegates} function. Keep in mind however that this - * will significantly increase the base gas cost of transfers. - * - * _Available since v4.2._ - */ -abstract contract ERC20Votes is ERC20Permit { - struct Checkpoint { - uint32 fromBlock; - uint224 votes; - } - - bytes32 private constant _DELEGATION_TYPEHASH = - keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - - mapping(address => address) private _delegates; - mapping(address => Checkpoint[]) private _checkpoints; - Checkpoint[] private _totalSupplyCheckpoints; - - /** - * @dev Emitted when an account changes their delegate. - */ - event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); - - /** - * @dev Emitted when a token transfer or delegate change results in changes to an account's voting power. - */ - event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); - - /** - * @dev Get the `pos`-th checkpoint for `account`. - */ - function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) { - return _checkpoints[account][pos]; - } - - /** - * @dev Get number of checkpoints for `account`. - */ - function numCheckpoints(address account) public view virtual returns (uint32) { - return SafeCast.toUint32(_checkpoints[account].length); - } - - /** - * @dev Get the address `account` is currently delegating to. - */ - function delegates(address account) public view virtual returns (address) { - return _delegates[account]; - } - - /** - * @dev Gets the current votes balance for `account` - */ - function getVotes(address account) public view returns (uint256) { - uint256 pos = _checkpoints[account].length; - return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; - } - - /** - * @dev Retrieve the number of votes for `account` at the end of `blockNumber`. - * - * Requirements: - * - * - `blockNumber` must have been already mined - */ - function getPastVotes(address account, uint256 blockNumber) public view virtual returns (uint256) { - require(blockNumber < block.number, "ERC20Votes: block not yet mined"); - return _checkpointsLookup(_checkpoints[account], blockNumber); - } - - /** - * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. - * It is but NOT the sum of all the delegated votes! - * - * Requirements: - * - * - `blockNumber` must have been already mined - */ - function getPastTotalSupply(uint256 blockNumber) public view returns (uint256) { - require(blockNumber < block.number, "ERC20Votes: block not yet mined"); - return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); - } - - /** - * @dev Lookup a value in a list of (sorted) checkpoints. - */ - function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) { - // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. - // - // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). - // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant. - // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) - // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) - // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not - // out of bounds (in which case we're looking too far in the past and the result is 0). - // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is - // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out - // the same. - uint256 high = ckpts.length; - uint256 low = 0; - while (low < high) { - uint256 mid = Math.average(low, high); - if (ckpts[mid].fromBlock > blockNumber) { - high = mid; - } else { - low = mid + 1; - } - } - - return high == 0 ? 0 : ckpts[high - 1].votes; - } - - /** - * @dev Delegate votes from the sender to `delegatee`. - */ - function delegate(address delegatee) public virtual { - _delegate(_msgSender(), delegatee); - } - - /** - * @dev Delegates votes from signer to `delegatee` - */ - function delegateBySig( - address delegatee, - uint256 nonce, - uint256 expiry, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual { - require(block.timestamp <= expiry, "ERC20Votes: signature expired"); - address signer = ECDSA.recover( - _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), - v, - r, - s - ); - require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce"); - _delegate(signer, delegatee); - } - - /** - * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). - */ - function _maxSupply() internal view virtual returns (uint224) { - return type(uint224).max; - } - - /** - * @dev Snapshots the totalSupply after it has been increased. - */ - function _mint(address account, uint256 amount) internal virtual override { - super._mint(account, amount); - require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); - - _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); - } - - /** - * @dev Snapshots the totalSupply after it has been decreased. - */ - function _burn(address account, uint256 amount) internal virtual override { - super._burn(account, amount); - - _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); - } - - /** - * @dev Move voting power when tokens are transferred. - * - * Emits a {DelegateVotesChanged} event. - */ - function _afterTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual override { - super._afterTokenTransfer(from, to, amount); - - _moveVotingPower(delegates(from), delegates(to), amount); - } - - /** - * @dev Change delegation for `delegator` to `delegatee`. - * - * Emits events {DelegateChanged} and {DelegateVotesChanged}. - */ - function _delegate(address delegator, address delegatee) internal virtual { - address currentDelegate = delegates(delegator); - uint256 delegatorBalance = balanceOf(delegator); - _delegates[delegator] = delegatee; - - emit DelegateChanged(delegator, currentDelegate, delegatee); - - _moveVotingPower(currentDelegate, delegatee, delegatorBalance); - } - - function _moveVotingPower( - address src, - address dst, - uint256 amount - ) private { - if (src != dst && amount > 0) { - if (src != address(0)) { - (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); - emit DelegateVotesChanged(src, oldWeight, newWeight); - } - - if (dst != address(0)) { - (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); - emit DelegateVotesChanged(dst, oldWeight, newWeight); - } - } - } - - function _writeCheckpoint( - Checkpoint[] storage ckpts, - function(uint256, uint256) view returns (uint256) op, - uint256 delta - ) private returns (uint256 oldWeight, uint256 newWeight) { - uint256 pos = ckpts.length; - oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; - newWeight = op(oldWeight, delta); - - if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { - ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); - } else { - ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); - } - } - - function _add(uint256 a, uint256 b) private pure returns (uint256) { - return a + b; - } - - function _subtract(uint256 a, uint256 b) private pure returns (uint256) { - return a - b; - } -} diff --git a/certora/munged/token/ERC20/extensions/ERC20VotesComp.sol b/certora/munged/token/ERC20/extensions/ERC20VotesComp.sol deleted file mode 100644 index 52151111a..000000000 --- a/certora/munged/token/ERC20/extensions/ERC20VotesComp.sol +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20VotesComp.sol) - -pragma solidity ^0.8.0; - -import "./ERC20Votes.sol"; - -/** - * @dev Extension of ERC20 to support Compound's voting and delegation. This version exactly matches Compound's - * interface, with the drawback of only supporting supply up to (2^96^ - 1). - * - * NOTE: You should use this contract if you need exact compatibility with COMP (for example in order to use your token - * with Governor Alpha or Bravo) and if you are sure the supply cap of 2^96^ is enough for you. Otherwise, use the - * {ERC20Votes} variant of this module. - * - * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either - * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting - * power can be queried through the public accessors {getCurrentVotes} and {getPriorVotes}. - * - * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it - * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. - * Enabling self-delegation can easily be done by overriding the {delegates} function. Keep in mind however that this - * will significantly increase the base gas cost of transfers. - * - * _Available since v4.2._ - */ -abstract contract ERC20VotesComp is ERC20Votes { - /** - * @dev Comp version of the {getVotes} accessor, with `uint96` return type. - */ - function getCurrentVotes(address account) external view returns (uint96) { - return SafeCast.toUint96(getVotes(account)); - } - - /** - * @dev Comp version of the {getPastVotes} accessor, with `uint96` return type. - */ - function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96) { - return SafeCast.toUint96(getPastVotes(account, blockNumber)); - } - - /** - * @dev Maximum token supply. Reduced to `type(uint96).max` (2^96^ - 1) to fit COMP interface. - */ - function _maxSupply() internal view virtual override returns (uint224) { - return type(uint96).max; - } -} diff --git a/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol b/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol deleted file mode 100644 index 4404c3821..000000000 --- a/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/ERC20Wrapper.sol) - -pragma solidity ^0.8.0; - -import "../ERC20.sol"; -import "../utils/SafeERC20.sol"; - -/** - * @dev Extension of the ERC20 token contract to support token wrapping. - * - * Users can deposit and withdraw "underlying tokens" and receive a matching number of "wrapped tokens". This is useful - * in conjunction with other modules. For example, combining this wrapping mechanism with {ERC20Votes} will allow the - * wrapping of an existing "basic" ERC20 into a governance token. - * - * _Available since v4.2._ - */ -abstract contract ERC20Wrapper is ERC20 { - IERC20 public immutable underlying; - - constructor(IERC20 underlyingToken) { - underlying = underlyingToken; - } - - /** - * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens. - */ - function depositFor(address account, uint256 amount) public virtual returns (bool) { - SafeERC20.safeTransferFrom(underlying, _msgSender(), address(this), amount); - _mint(account, amount); - return true; - } - - /** - * @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens. - */ - function withdrawTo(address account, uint256 amount) public virtual returns (bool) { - _burn(_msgSender(), amount); - SafeERC20.safeTransfer(underlying, account, amount); - return true; - } - - /** - * @dev Mint wrapped token to cover any underlyingTokens that would have been transfered by mistake. Internal - * function that can be exposed with access control if desired. - */ - function _recover(address account) internal virtual returns (uint256) { - uint256 value = underlying.balanceOf(address(this)) - totalSupply(); - _mint(account, value); - return value; - } -} diff --git a/certora/munged/token/ERC20/extensions/IERC20Metadata.sol b/certora/munged/token/ERC20/extensions/IERC20Metadata.sol deleted file mode 100644 index ba019f213..000000000 --- a/certora/munged/token/ERC20/extensions/IERC20Metadata.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/IERC20Metadata.sol) - -pragma solidity ^0.8.0; - -import "../IERC20.sol"; - -/** - * @dev Interface for the optional metadata functions from the ERC20 standard. - * - * _Available since v4.1._ - */ -interface IERC20Metadata is IERC20 { - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the symbol of the token. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the decimals places of the token. - */ - function decimals() external view returns (uint8); -} diff --git a/certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol b/certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol deleted file mode 100644 index 920a5f5bf..000000000 --- a/certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/draft-ERC20Permit.sol) - -pragma solidity ^0.8.0; - -import "./draft-IERC20Permit.sol"; -import "../ERC20.sol"; -import "../../../utils/cryptography/draft-EIP712.sol"; -import "../../../utils/cryptography/ECDSA.sol"; -import "../../../utils/Counters.sol"; - -/** - * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in - * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. - * - * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by - * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't - * need to send a transaction, and thus is not required to hold Ether at all. - * - * _Available since v3.4._ - */ -abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { - using Counters for Counters.Counter; - - mapping(address => Counters.Counter) private _nonces; - - // solhint-disable-next-line var-name-mixedcase - bytes32 private immutable _PERMIT_TYPEHASH = - keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); - - /** - * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. - * - * It's a good idea to use the same `name` that is defined as the ERC20 token name. - */ - constructor(string memory name) EIP712(name, "1") {} - - /** - * @dev See {IERC20Permit-permit}. - */ - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual override { - require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); - - bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); - - bytes32 hash = _hashTypedDataV4(structHash); - - address signer = ECDSA.recover(hash, v, r, s); - require(signer == owner, "ERC20Permit: invalid signature"); - - _approve(owner, spender, value); - } - - /** - * @dev See {IERC20Permit-nonces}. - */ - function nonces(address owner) public view virtual override returns (uint256) { - return _nonces[owner].current(); - } - - /** - * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. - */ - // solhint-disable-next-line func-name-mixedcase - function DOMAIN_SEPARATOR() external view override returns (bytes32) { - return _domainSeparatorV4(); - } - - /** - * @dev "Consume a nonce": return the current value and increment. - * - * _Available since v4.1._ - */ - function _useNonce(address owner) internal virtual returns (uint256 current) { - Counters.Counter storage nonce = _nonces[owner]; - current = nonce.current(); - nonce.increment(); - } -} diff --git a/certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol b/certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol deleted file mode 100644 index 11a49cad9..000000000 --- a/certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/extensions/draft-IERC20Permit.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in - * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. - * - * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by - * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't - * need to send a transaction, and thus is not required to hold Ether at all. - */ -interface IERC20Permit { - /** - * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, - * given ``owner``'s signed approval. - * - * IMPORTANT: The same issues {IERC20-approve} has related to transaction - * ordering also apply here. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `deadline` must be a timestamp in the future. - * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` - * over the EIP712-formatted function arguments. - * - the signature must use ``owner``'s current nonce (see {nonces}). - * - * For more information on the signature format, see the - * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP - * section]. - */ - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external; - - /** - * @dev Returns the current nonce for `owner`. This value must be - * included whenever a signature is generated for {permit}. - * - * Every successful call to {permit} increases ``owner``'s nonce by one. This - * prevents a signature from being used multiple times. - */ - function nonces(address owner) external view returns (uint256); - - /** - * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. - */ - // solhint-disable-next-line func-name-mixedcase - function DOMAIN_SEPARATOR() external view returns (bytes32); -} diff --git a/certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol b/certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol deleted file mode 100644 index e761a6ac9..000000000 --- a/certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/presets/ERC20PresetFixedSupply.sol) -pragma solidity ^0.8.0; - -import "../extensions/ERC20Burnable.sol"; - -/** - * @dev {ERC20} token, including: - * - * - Preminted initial supply - * - Ability for holders to burn (destroy) their tokens - * - No access control mechanism (for minting/pausing) and hence no governance - * - * This contract uses {ERC20Burnable} to include burn capabilities - head to - * its documentation for details. - * - * _Available since v3.4._ - */ -contract ERC20PresetFixedSupply is ERC20Burnable { - /** - * @dev Mints `initialSupply` amount of token and transfers them to `owner`. - * - * See {ERC20-constructor}. - */ - constructor( - string memory name, - string memory symbol, - uint256 initialSupply, - address owner - ) ERC20(name, symbol) { - _mint(owner, initialSupply); - } -} diff --git a/certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol b/certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol deleted file mode 100644 index 1ba34a43e..000000000 --- a/certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/presets/ERC20PresetMinterPauser.sol) - -pragma solidity ^0.8.0; - -import "../ERC20.sol"; -import "../extensions/ERC20Burnable.sol"; -import "../extensions/ERC20Pausable.sol"; -import "../../../access/AccessControlEnumerable.sol"; -import "../../../utils/Context.sol"; - -/** - * @dev {ERC20} token, including: - * - * - ability for holders to burn (destroy) their tokens - * - a minter role that allows for token minting (creation) - * - a pauser role that allows to stop all token transfers - * - * This contract uses {AccessControl} to lock permissioned functions using the - * different roles - head to its documentation for details. - * - * The account that deploys the contract will be granted the minter and pauser - * roles, as well as the default admin role, which will let it grant both minter - * and pauser roles to other accounts. - */ -contract ERC20PresetMinterPauser is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable { - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); - bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); - - /** - * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the - * account that deploys the contract. - * - * See {ERC20-constructor}. - */ - constructor(string memory name, string memory symbol) ERC20(name, symbol) { - _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); - - _setupRole(MINTER_ROLE, _msgSender()); - _setupRole(PAUSER_ROLE, _msgSender()); - } - - /** - * @dev Creates `amount` new tokens for `to`. - * - * See {ERC20-_mint}. - * - * Requirements: - * - * - the caller must have the `MINTER_ROLE`. - */ - function mint(address to, uint256 amount) public virtual { - require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint"); - _mint(to, amount); - } - - /** - * @dev Pauses all token transfers. - * - * See {ERC20Pausable} and {Pausable-_pause}. - * - * Requirements: - * - * - the caller must have the `PAUSER_ROLE`. - */ - function pause() public virtual { - require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause"); - _pause(); - } - - /** - * @dev Unpauses all token transfers. - * - * See {ERC20Pausable} and {Pausable-_unpause}. - * - * Requirements: - * - * - the caller must have the `PAUSER_ROLE`. - */ - function unpause() public virtual { - require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause"); - _unpause(); - } - - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual override(ERC20, ERC20Pausable) { - super._beforeTokenTransfer(from, to, amount); - } -} diff --git a/certora/munged/token/ERC20/utils/SafeERC20.sol b/certora/munged/token/ERC20/utils/SafeERC20.sol deleted file mode 100644 index 09e38db62..000000000 --- a/certora/munged/token/ERC20/utils/SafeERC20.sol +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/utils/SafeERC20.sol) - -pragma solidity ^0.8.0; - -import "../IERC20.sol"; -import "../../../utils/Address.sol"; - -/** - * @title SafeERC20 - * @dev Wrappers around ERC20 operations that throw on failure (when the token - * contract returns false). Tokens that return no value (and instead revert or - * throw on failure) are also supported, non-reverting calls are assumed to be - * successful. - * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, - * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. - */ -library SafeERC20 { - using Address for address; - - function safeTransfer( - IERC20 token, - address to, - uint256 value - ) internal { - _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); - } - - function safeTransferFrom( - IERC20 token, - address from, - address to, - uint256 value - ) internal { - _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); - } - - /** - * @dev Deprecated. This function has issues similar to the ones found in - * {IERC20-approve}, and its usage is discouraged. - * - * Whenever possible, use {safeIncreaseAllowance} and - * {safeDecreaseAllowance} instead. - */ - function safeApprove( - IERC20 token, - address spender, - uint256 value - ) internal { - // safeApprove should only be called when setting an initial allowance, - // or when resetting it to zero. To increase and decrease it, use - // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' - require( - (value == 0) || (token.allowance(address(this), spender) == 0), - "SafeERC20: approve from non-zero to non-zero allowance" - ); - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); - } - - function safeIncreaseAllowance( - IERC20 token, - address spender, - uint256 value - ) internal { - uint256 newAllowance = token.allowance(address(this), spender) + value; - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); - } - - function safeDecreaseAllowance( - IERC20 token, - address spender, - uint256 value - ) internal { - unchecked { - uint256 oldAllowance = token.allowance(address(this), spender); - require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); - uint256 newAllowance = oldAllowance - value; - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); - } - } - - /** - * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement - * on the return value: the return value is optional (but if data is returned, it must not be false). - * @param token The token targeted by the call. - * @param data The call data (encoded using abi.encode or one of its variants). - */ - function _callOptionalReturn(IERC20 token, bytes memory data) private { - // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since - // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that - // the target address contains contract code and also asserts for success in the low-level call. - - bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); - if (returndata.length > 0) { - // Return data is optional - require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); - } - } -} diff --git a/certora/munged/token/ERC20/utils/TokenTimelock.sol b/certora/munged/token/ERC20/utils/TokenTimelock.sol deleted file mode 100644 index 5f9acceee..000000000 --- a/certora/munged/token/ERC20/utils/TokenTimelock.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC20/utils/TokenTimelock.sol) - -pragma solidity ^0.8.0; - -import "./SafeERC20.sol"; - -/** - * @dev A token holder contract that will allow a beneficiary to extract the - * tokens after a given release time. - * - * Useful for simple vesting schedules like "advisors get all of their tokens - * after 1 year". - */ -contract TokenTimelock { - using SafeERC20 for IERC20; - - // ERC20 basic token contract being held - IERC20 private immutable _token; - - // beneficiary of tokens after they are released - address private immutable _beneficiary; - - // timestamp when token release is enabled - uint256 private immutable _releaseTime; - - constructor( - IERC20 token_, - address beneficiary_, - uint256 releaseTime_ - ) { - require(releaseTime_ > block.timestamp, "TokenTimelock: release time is before current time"); - _token = token_; - _beneficiary = beneficiary_; - _releaseTime = releaseTime_; - } - - /** - * @return the token being held. - */ - function token() public view virtual returns (IERC20) { - return _token; - } - - /** - * @return the beneficiary of the tokens. - */ - function beneficiary() public view virtual returns (address) { - return _beneficiary; - } - - /** - * @return the time when the tokens are released. - */ - function releaseTime() public view virtual returns (uint256) { - return _releaseTime; - } - - /** - * @notice Transfers tokens held by timelock to beneficiary. - */ - function release() public virtual { - require(block.timestamp >= releaseTime(), "TokenTimelock: current time is before release time"); - - uint256 amount = token().balanceOf(address(this)); - require(amount > 0, "TokenTimelock: no tokens to release"); - - token().safeTransfer(beneficiary(), amount); - } -} diff --git a/certora/munged/token/ERC721/ERC721.sol b/certora/munged/token/ERC721/ERC721.sol deleted file mode 100644 index dbd91bcbc..000000000 --- a/certora/munged/token/ERC721/ERC721.sol +++ /dev/null @@ -1,424 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/ERC721.sol) - -pragma solidity ^0.8.0; - -import "./IERC721.sol"; -import "./IERC721Receiver.sol"; -import "./extensions/IERC721Metadata.sol"; -import "../../utils/Address.sol"; -import "../../utils/Context.sol"; -import "../../utils/Strings.sol"; -import "../../utils/introspection/ERC165.sol"; - -/** - * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including - * the Metadata extension, but not including the Enumerable extension, which is available separately as - * {ERC721Enumerable}. - */ -contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { - using Address for address; - using Strings for uint256; - - // Token name - string private _name; - - // Token symbol - string private _symbol; - - // Mapping from token ID to owner address - mapping(uint256 => address) private _owners; - - // Mapping owner address to token count - mapping(address => uint256) private _balances; - - // Mapping from token ID to approved address - mapping(uint256 => address) private _tokenApprovals; - - // Mapping from owner to operator approvals - mapping(address => mapping(address => bool)) private _operatorApprovals; - - /** - * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. - */ - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { - return - interfaceId == type(IERC721).interfaceId || - interfaceId == type(IERC721Metadata).interfaceId || - super.supportsInterface(interfaceId); - } - - /** - * @dev See {IERC721-balanceOf}. - */ - function balanceOf(address owner) public view virtual override returns (uint256) { - require(owner != address(0), "ERC721: balance query for the zero address"); - return _balances[owner]; - } - - /** - * @dev See {IERC721-ownerOf}. - */ - function ownerOf(uint256 tokenId) public view virtual override returns (address) { - address owner = _owners[tokenId]; - require(owner != address(0), "ERC721: owner query for nonexistent token"); - return owner; - } - - /** - * @dev See {IERC721Metadata-name}. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev See {IERC721Metadata-symbol}. - */ - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - /** - * @dev See {IERC721Metadata-tokenURI}. - */ - function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { - require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); - - string memory baseURI = _baseURI(); - return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; - } - - /** - * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each - * token will be the concatenation of the `baseURI` and the `tokenId`. Empty - * by default, can be overriden in child contracts. - */ - function _baseURI() internal view virtual returns (string memory) { - return ""; - } - - /** - * @dev See {IERC721-approve}. - */ - function approve(address to, uint256 tokenId) public virtual override { - address owner = ERC721.ownerOf(tokenId); - require(to != owner, "ERC721: approval to current owner"); - - require( - _msgSender() == owner || isApprovedForAll(owner, _msgSender()), - "ERC721: approve caller is not owner nor approved for all" - ); - - _approve(to, tokenId); - } - - /** - * @dev See {IERC721-getApproved}. - */ - function getApproved(uint256 tokenId) public view virtual override returns (address) { - require(_exists(tokenId), "ERC721: approved query for nonexistent token"); - - return _tokenApprovals[tokenId]; - } - - /** - * @dev See {IERC721-setApprovalForAll}. - */ - function setApprovalForAll(address operator, bool approved) public virtual override { - _setApprovalForAll(_msgSender(), operator, approved); - } - - /** - * @dev See {IERC721-isApprovedForAll}. - */ - function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { - return _operatorApprovals[owner][operator]; - } - - /** - * @dev See {IERC721-transferFrom}. - */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { - //solhint-disable-next-line max-line-length - require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); - - _transfer(from, to, tokenId); - } - - /** - * @dev See {IERC721-safeTransferFrom}. - */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { - safeTransferFrom(from, to, tokenId, ""); - } - - /** - * @dev See {IERC721-safeTransferFrom}. - */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory _data - ) public virtual override { - require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); - _safeTransfer(from, to, tokenId, _data); - } - - /** - * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients - * are aware of the ERC721 protocol to prevent tokens from being forever locked. - * - * `_data` is additional data, it has no specified format and it is sent in call to `to`. - * - * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. - * implement alternative mechanisms to perform token transfer, such as signature-based. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `tokenId` token must exist and be owned by `from`. - * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - * - * Emits a {Transfer} event. - */ - function _safeTransfer( - address from, - address to, - uint256 tokenId, - bytes memory _data - ) internal virtual { - _transfer(from, to, tokenId); - require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); - } - - /** - * @dev Returns whether `tokenId` exists. - * - * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. - * - * Tokens start existing when they are minted (`_mint`), - * and stop existing when they are burned (`_burn`). - */ - function _exists(uint256 tokenId) internal view virtual returns (bool) { - return _owners[tokenId] != address(0); - } - - /** - * @dev Returns whether `spender` is allowed to manage `tokenId`. - * - * Requirements: - * - * - `tokenId` must exist. - */ - function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { - require(_exists(tokenId), "ERC721: operator query for nonexistent token"); - address owner = ERC721.ownerOf(tokenId); - return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); - } - - /** - * @dev Safely mints `tokenId` and transfers it to `to`. - * - * Requirements: - * - * - `tokenId` must not exist. - * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - * - * Emits a {Transfer} event. - */ - function _safeMint(address to, uint256 tokenId) internal virtual { - _safeMint(to, tokenId, ""); - } - - /** - * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is - * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. - */ - function _safeMint( - address to, - uint256 tokenId, - bytes memory _data - ) internal virtual { - _mint(to, tokenId); - require( - _checkOnERC721Received(address(0), to, tokenId, _data), - "ERC721: transfer to non ERC721Receiver implementer" - ); - } - - /** - * @dev Mints `tokenId` and transfers it to `to`. - * - * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible - * - * Requirements: - * - * - `tokenId` must not exist. - * - `to` cannot be the zero address. - * - * Emits a {Transfer} event. - */ - function _mint(address to, uint256 tokenId) internal virtual { - require(to != address(0), "ERC721: mint to the zero address"); - require(!_exists(tokenId), "ERC721: token already minted"); - - _beforeTokenTransfer(address(0), to, tokenId); - - _balances[to] += 1; - _owners[tokenId] = to; - - emit Transfer(address(0), to, tokenId); - } - - /** - * @dev Destroys `tokenId`. - * The approval is cleared when the token is burned. - * - * Requirements: - * - * - `tokenId` must exist. - * - * Emits a {Transfer} event. - */ - function _burn(uint256 tokenId) internal virtual { - address owner = ERC721.ownerOf(tokenId); - - _beforeTokenTransfer(owner, address(0), tokenId); - - // Clear approvals - _approve(address(0), tokenId); - - _balances[owner] -= 1; - delete _owners[tokenId]; - - emit Transfer(owner, address(0), tokenId); - } - - /** - * @dev Transfers `tokenId` from `from` to `to`. - * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - `tokenId` token must be owned by `from`. - * - * Emits a {Transfer} event. - */ - function _transfer( - address from, - address to, - uint256 tokenId - ) internal virtual { - require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); - require(to != address(0), "ERC721: transfer to the zero address"); - - _beforeTokenTransfer(from, to, tokenId); - - // Clear approvals from the previous owner - _approve(address(0), tokenId); - - _balances[from] -= 1; - _balances[to] += 1; - _owners[tokenId] = to; - - emit Transfer(from, to, tokenId); - } - - /** - * @dev Approve `to` to operate on `tokenId` - * - * Emits a {Approval} event. - */ - function _approve(address to, uint256 tokenId) internal virtual { - _tokenApprovals[tokenId] = to; - emit Approval(ERC721.ownerOf(tokenId), to, tokenId); - } - - /** - * @dev Approve `operator` to operate on all of `owner` tokens - * - * Emits a {ApprovalForAll} event. - */ - function _setApprovalForAll( - address owner, - address operator, - bool approved - ) internal virtual { - require(owner != operator, "ERC721: approve to caller"); - _operatorApprovals[owner][operator] = approved; - emit ApprovalForAll(owner, operator, approved); - } - - /** - * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. - * The call is not executed if the target address is not a contract. - * - * @param from address representing the previous owner of the given token ID - * @param to target address that will receive the tokens - * @param tokenId uint256 ID of the token to be transferred - * @param _data bytes optional data to send along with the call - * @return bool whether the call correctly returned the expected magic value - */ - function _checkOnERC721Received( - address from, - address to, - uint256 tokenId, - bytes memory _data - ) private returns (bool) { - if (to.isContract()) { - try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { - return retval == IERC721Receiver.onERC721Received.selector; - } catch (bytes memory reason) { - if (reason.length == 0) { - revert("ERC721: transfer to non ERC721Receiver implementer"); - } else { - assembly { - revert(add(32, reason), mload(reason)) - } - } - } - } else { - return true; - } - } - - /** - * @dev Hook that is called before any token transfer. This includes minting - * and burning. - * - * Calling conditions: - * - * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be - * transferred to `to`. - * - When `from` is zero, `tokenId` will be minted for `to`. - * - When `to` is zero, ``from``'s `tokenId` will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer( - address from, - address to, - uint256 tokenId - ) internal virtual {} -} diff --git a/certora/munged/token/ERC721/IERC721.sol b/certora/munged/token/ERC721/IERC721.sol deleted file mode 100644 index f5e91749e..000000000 --- a/certora/munged/token/ERC721/IERC721.sol +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/IERC721.sol) - -pragma solidity ^0.8.0; - -import "../../utils/introspection/IERC165.sol"; - -/** - * @dev Required interface of an ERC721 compliant contract. - */ -interface IERC721 is IERC165 { - /** - * @dev Emitted when `tokenId` token is transferred from `from` to `to`. - */ - event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); - - /** - * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. - */ - event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); - - /** - * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. - */ - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); - - /** - * @dev Returns the number of tokens in ``owner``'s account. - */ - function balanceOf(address owner) external view returns (uint256 balance); - - /** - * @dev Returns the owner of the `tokenId` token. - * - * Requirements: - * - * - `tokenId` must exist. - */ - function ownerOf(uint256 tokenId) external view returns (address owner); - - /** - * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients - * are aware of the ERC721 protocol to prevent tokens from being forever locked. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `tokenId` token must exist and be owned by `from`. - * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. - * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - * - * Emits a {Transfer} event. - */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) external; - - /** - * @dev Transfers `tokenId` token from `from` to `to`. - * - * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `tokenId` token must be owned by `from`. - * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) external; - - /** - * @dev Gives permission to `to` to transfer `tokenId` token to another account. - * The approval is cleared when the token is transferred. - * - * Only a single account can be approved at a time, so approving the zero address clears previous approvals. - * - * Requirements: - * - * - The caller must own the token or be an approved operator. - * - `tokenId` must exist. - * - * Emits an {Approval} event. - */ - function approve(address to, uint256 tokenId) external; - - /** - * @dev Returns the account approved for `tokenId` token. - * - * Requirements: - * - * - `tokenId` must exist. - */ - function getApproved(uint256 tokenId) external view returns (address operator); - - /** - * @dev Approve or remove `operator` as an operator for the caller. - * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. - * - * Requirements: - * - * - The `operator` cannot be the caller. - * - * Emits an {ApprovalForAll} event. - */ - function setApprovalForAll(address operator, bool _approved) external; - - /** - * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. - * - * See {setApprovalForAll} - */ - function isApprovedForAll(address owner, address operator) external view returns (bool); - - /** - * @dev Safely transfers `tokenId` token from `from` to `to`. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `tokenId` token must exist and be owned by `from`. - * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. - * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - * - * Emits a {Transfer} event. - */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes calldata data - ) external; -} diff --git a/certora/munged/token/ERC721/IERC721Receiver.sol b/certora/munged/token/ERC721/IERC721Receiver.sol deleted file mode 100644 index d10250ea8..000000000 --- a/certora/munged/token/ERC721/IERC721Receiver.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/IERC721Receiver.sol) - -pragma solidity ^0.8.0; - -/** - * @title ERC721 token receiver interface - * @dev Interface for any contract that wants to support safeTransfers - * from ERC721 asset contracts. - */ -interface IERC721Receiver { - /** - * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} - * by `operator` from `from`, this function is called. - * - * It must return its Solidity selector to confirm the token transfer. - * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. - * - * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. - */ - function onERC721Received( - address operator, - address from, - uint256 tokenId, - bytes calldata data - ) external returns (bytes4); -} diff --git a/certora/munged/token/ERC721/README.adoc b/certora/munged/token/ERC721/README.adoc deleted file mode 100644 index f1122c53a..000000000 --- a/certora/munged/token/ERC721/README.adoc +++ /dev/null @@ -1,52 +0,0 @@ -= ERC 721 - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc721 - -This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-721[ERC721 Non-Fungible Token Standard]. - -TIP: For a walk through on how to create an ERC721 token read our xref:ROOT:erc721.adoc[ERC721 guide]. - -The EIP consists of three interfaces, found here as {IERC721}, {IERC721Metadata}, and {IERC721Enumerable}. Only the first one is required in a contract to be ERC721 compliant. The core interface and the metadata extension are both implemented in {ERC721}. The enumerable extension is provided separately in {ERC721Enumerable}. - -Additionally, {IERC721Receiver} can be used to prevent tokens from becoming forever locked in contracts. Imagine sending an in-game item to an exchange address that can't send it back!. When using <>, the token contract checks to see that the receiver is an {IERC721Receiver}, which implies that it knows how to handle {ERC721} tokens. If you're writing a contract that needs to receive {ERC721} tokens, you'll want to include this interface. - -Additionally there are multiple custom extensions, including: - -* designation of addresses that can pause token transfers for all users ({ERC721Pausable}). -* destruction of own tokens ({ERC721Burnable}). - -NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC721 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc721.adoc#Presets[ERC721 Presets] (such as {ERC721PresetMinterPauserAutoId}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. - - -== Core - -{{IERC721}} - -{{IERC721Metadata}} - -{{IERC721Enumerable}} - -{{ERC721}} - -{{ERC721Enumerable}} - -{{IERC721Receiver}} - -== Extensions - -{{ERC721Pausable}} - -{{ERC721Burnable}} - -{{ERC721URIStorage}} - -== Presets - -These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. - -{{ERC721PresetMinterPauserAutoId}} - -== Utilities - -{{ERC721Holder}} diff --git a/certora/munged/token/ERC721/extensions/ERC721Burnable.sol b/certora/munged/token/ERC721/extensions/ERC721Burnable.sol deleted file mode 100644 index b41ba74d0..000000000 --- a/certora/munged/token/ERC721/extensions/ERC721Burnable.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/ERC721Burnable.sol) - -pragma solidity ^0.8.0; - -import "../ERC721.sol"; -import "../../../utils/Context.sol"; - -/** - * @title ERC721 Burnable Token - * @dev ERC721 Token that can be irreversibly burned (destroyed). - */ -abstract contract ERC721Burnable is Context, ERC721 { - /** - * @dev Burns `tokenId`. See {ERC721-_burn}. - * - * Requirements: - * - * - The caller must own `tokenId` or be an approved operator. - */ - function burn(uint256 tokenId) public virtual { - //solhint-disable-next-line max-line-length - require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Burnable: caller is not owner nor approved"); - _burn(tokenId); - } -} diff --git a/certora/munged/token/ERC721/extensions/ERC721Enumerable.sol b/certora/munged/token/ERC721/extensions/ERC721Enumerable.sol deleted file mode 100644 index 49b31389b..000000000 --- a/certora/munged/token/ERC721/extensions/ERC721Enumerable.sol +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/ERC721Enumerable.sol) - -pragma solidity ^0.8.0; - -import "../ERC721.sol"; -import "./IERC721Enumerable.sol"; - -/** - * @dev This implements an optional extension of {ERC721} defined in the EIP that adds - * enumerability of all the token ids in the contract as well as all token ids owned by each - * account. - */ -abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { - // Mapping from owner to list of owned token IDs - mapping(address => mapping(uint256 => uint256)) private _ownedTokens; - - // Mapping from token ID to index of the owner tokens list - mapping(uint256 => uint256) private _ownedTokensIndex; - - // Array with all token ids, used for enumeration - uint256[] private _allTokens; - - // Mapping from token id to position in the allTokens array - mapping(uint256 => uint256) private _allTokensIndex; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { - return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. - */ - function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { - require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds"); - return _ownedTokens[owner][index]; - } - - /** - * @dev See {IERC721Enumerable-totalSupply}. - */ - function totalSupply() public view virtual override returns (uint256) { - return _allTokens.length; - } - - /** - * @dev See {IERC721Enumerable-tokenByIndex}. - */ - function tokenByIndex(uint256 index) public view virtual override returns (uint256) { - require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds"); - return _allTokens[index]; - } - - /** - * @dev Hook that is called before any token transfer. This includes minting - * and burning. - * - * Calling conditions: - * - * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be - * transferred to `to`. - * - When `from` is zero, `tokenId` will be minted for `to`. - * - When `to` is zero, ``from``'s `tokenId` will be burned. - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer( - address from, - address to, - uint256 tokenId - ) internal virtual override { - super._beforeTokenTransfer(from, to, tokenId); - - if (from == address(0)) { - _addTokenToAllTokensEnumeration(tokenId); - } else if (from != to) { - _removeTokenFromOwnerEnumeration(from, tokenId); - } - if (to == address(0)) { - _removeTokenFromAllTokensEnumeration(tokenId); - } else if (to != from) { - _addTokenToOwnerEnumeration(to, tokenId); - } - } - - /** - * @dev Private function to add a token to this extension's ownership-tracking data structures. - * @param to address representing the new owner of the given token ID - * @param tokenId uint256 ID of the token to be added to the tokens list of the given address - */ - function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { - uint256 length = ERC721.balanceOf(to); - _ownedTokens[to][length] = tokenId; - _ownedTokensIndex[tokenId] = length; - } - - /** - * @dev Private function to add a token to this extension's token tracking data structures. - * @param tokenId uint256 ID of the token to be added to the tokens list - */ - function _addTokenToAllTokensEnumeration(uint256 tokenId) private { - _allTokensIndex[tokenId] = _allTokens.length; - _allTokens.push(tokenId); - } - - /** - * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that - * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for - * gas optimizations e.g. when performing a transfer operation (avoiding double writes). - * This has O(1) time complexity, but alters the order of the _ownedTokens array. - * @param from address representing the previous owner of the given token ID - * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address - */ - function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { - // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and - // then delete the last slot (swap and pop). - - uint256 lastTokenIndex = ERC721.balanceOf(from) - 1; - uint256 tokenIndex = _ownedTokensIndex[tokenId]; - - // When the token to delete is the last token, the swap operation is unnecessary - if (tokenIndex != lastTokenIndex) { - uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; - - _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token - _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index - } - - // This also deletes the contents at the last position of the array - delete _ownedTokensIndex[tokenId]; - delete _ownedTokens[from][lastTokenIndex]; - } - - /** - * @dev Private function to remove a token from this extension's token tracking data structures. - * This has O(1) time complexity, but alters the order of the _allTokens array. - * @param tokenId uint256 ID of the token to be removed from the tokens list - */ - function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { - // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and - // then delete the last slot (swap and pop). - - uint256 lastTokenIndex = _allTokens.length - 1; - uint256 tokenIndex = _allTokensIndex[tokenId]; - - // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so - // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding - // an 'if' statement (like in _removeTokenFromOwnerEnumeration) - uint256 lastTokenId = _allTokens[lastTokenIndex]; - - _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token - _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index - - // This also deletes the contents at the last position of the array - delete _allTokensIndex[tokenId]; - _allTokens.pop(); - } -} diff --git a/certora/munged/token/ERC721/extensions/ERC721Pausable.sol b/certora/munged/token/ERC721/extensions/ERC721Pausable.sol deleted file mode 100644 index 5994cf36b..000000000 --- a/certora/munged/token/ERC721/extensions/ERC721Pausable.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/ERC721Pausable.sol) - -pragma solidity ^0.8.0; - -import "../ERC721.sol"; -import "../../../security/Pausable.sol"; - -/** - * @dev ERC721 token with pausable token transfers, minting and burning. - * - * Useful for scenarios such as preventing trades until the end of an evaluation - * period, or having an emergency switch for freezing all token transfers in the - * event of a large bug. - */ -abstract contract ERC721Pausable is ERC721, Pausable { - /** - * @dev See {ERC721-_beforeTokenTransfer}. - * - * Requirements: - * - * - the contract must not be paused. - */ - function _beforeTokenTransfer( - address from, - address to, - uint256 tokenId - ) internal virtual override { - super._beforeTokenTransfer(from, to, tokenId); - - require(!paused(), "ERC721Pausable: token transfer while paused"); - } -} diff --git a/certora/munged/token/ERC721/extensions/ERC721URIStorage.sol b/certora/munged/token/ERC721/extensions/ERC721URIStorage.sol deleted file mode 100644 index ac9070144..000000000 --- a/certora/munged/token/ERC721/extensions/ERC721URIStorage.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/ERC721URIStorage.sol) - -pragma solidity ^0.8.0; - -import "../ERC721.sol"; - -/** - * @dev ERC721 token with storage based token URI management. - */ -abstract contract ERC721URIStorage is ERC721 { - using Strings for uint256; - - // Optional mapping for token URIs - mapping(uint256 => string) private _tokenURIs; - - /** - * @dev See {IERC721Metadata-tokenURI}. - */ - function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { - require(_exists(tokenId), "ERC721URIStorage: URI query for nonexistent token"); - - string memory _tokenURI = _tokenURIs[tokenId]; - string memory base = _baseURI(); - - // If there is no base URI, return the token URI. - if (bytes(base).length == 0) { - return _tokenURI; - } - // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). - if (bytes(_tokenURI).length > 0) { - return string(abi.encodePacked(base, _tokenURI)); - } - - return super.tokenURI(tokenId); - } - - /** - * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. - * - * Requirements: - * - * - `tokenId` must exist. - */ - function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { - require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); - _tokenURIs[tokenId] = _tokenURI; - } - - /** - * @dev Destroys `tokenId`. - * The approval is cleared when the token is burned. - * - * Requirements: - * - * - `tokenId` must exist. - * - * Emits a {Transfer} event. - */ - function _burn(uint256 tokenId) internal virtual override { - super._burn(tokenId); - - if (bytes(_tokenURIs[tokenId]).length != 0) { - delete _tokenURIs[tokenId]; - } - } -} diff --git a/certora/munged/token/ERC721/extensions/IERC721Enumerable.sol b/certora/munged/token/ERC721/extensions/IERC721Enumerable.sol deleted file mode 100644 index 904639956..000000000 --- a/certora/munged/token/ERC721/extensions/IERC721Enumerable.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/IERC721Enumerable.sol) - -pragma solidity ^0.8.0; - -import "../IERC721.sol"; - -/** - * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension - * @dev See https://eips.ethereum.org/EIPS/eip-721 - */ -interface IERC721Enumerable is IERC721 { - /** - * @dev Returns the total amount of tokens stored by the contract. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns a token ID owned by `owner` at a given `index` of its token list. - * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. - */ - function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId); - - /** - * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. - * Use along with {totalSupply} to enumerate all tokens. - */ - function tokenByIndex(uint256 index) external view returns (uint256); -} diff --git a/certora/munged/token/ERC721/extensions/IERC721Metadata.sol b/certora/munged/token/ERC721/extensions/IERC721Metadata.sol deleted file mode 100644 index a50ea0db5..000000000 --- a/certora/munged/token/ERC721/extensions/IERC721Metadata.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/extensions/IERC721Metadata.sol) - -pragma solidity ^0.8.0; - -import "../IERC721.sol"; - -/** - * @title ERC-721 Non-Fungible Token Standard, optional metadata extension - * @dev See https://eips.ethereum.org/EIPS/eip-721 - */ -interface IERC721Metadata is IERC721 { - /** - * @dev Returns the token collection name. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the token collection symbol. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. - */ - function tokenURI(uint256 tokenId) external view returns (string memory); -} diff --git a/certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol b/certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol deleted file mode 100644 index 82ea1dc7d..000000000 --- a/certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol) - -pragma solidity ^0.8.0; - -import "../ERC721.sol"; -import "../extensions/ERC721Enumerable.sol"; -import "../extensions/ERC721Burnable.sol"; -import "../extensions/ERC721Pausable.sol"; -import "../../../access/AccessControlEnumerable.sol"; -import "../../../utils/Context.sol"; -import "../../../utils/Counters.sol"; - -/** - * @dev {ERC721} token, including: - * - * - ability for holders to burn (destroy) their tokens - * - a minter role that allows for token minting (creation) - * - a pauser role that allows to stop all token transfers - * - token ID and URI autogeneration - * - * This contract uses {AccessControl} to lock permissioned functions using the - * different roles - head to its documentation for details. - * - * The account that deploys the contract will be granted the minter and pauser - * roles, as well as the default admin role, which will let it grant both minter - * and pauser roles to other accounts. - */ -contract ERC721PresetMinterPauserAutoId is - Context, - AccessControlEnumerable, - ERC721Enumerable, - ERC721Burnable, - ERC721Pausable -{ - using Counters for Counters.Counter; - - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); - bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); - - Counters.Counter private _tokenIdTracker; - - string private _baseTokenURI; - - /** - * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the - * account that deploys the contract. - * - * Token URIs will be autogenerated based on `baseURI` and their token IDs. - * See {ERC721-tokenURI}. - */ - constructor( - string memory name, - string memory symbol, - string memory baseTokenURI - ) ERC721(name, symbol) { - _baseTokenURI = baseTokenURI; - - _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); - - _setupRole(MINTER_ROLE, _msgSender()); - _setupRole(PAUSER_ROLE, _msgSender()); - } - - function _baseURI() internal view virtual override returns (string memory) { - return _baseTokenURI; - } - - /** - * @dev Creates a new token for `to`. Its token ID will be automatically - * assigned (and available on the emitted {IERC721-Transfer} event), and the token - * URI autogenerated based on the base URI passed at construction. - * - * See {ERC721-_mint}. - * - * Requirements: - * - * - the caller must have the `MINTER_ROLE`. - */ - function mint(address to) public virtual { - require(hasRole(MINTER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have minter role to mint"); - - // We cannot just use balanceOf to create the new tokenId because tokens - // can be burned (destroyed), so we need a separate counter. - _mint(to, _tokenIdTracker.current()); - _tokenIdTracker.increment(); - } - - /** - * @dev Pauses all token transfers. - * - * See {ERC721Pausable} and {Pausable-_pause}. - * - * Requirements: - * - * - the caller must have the `PAUSER_ROLE`. - */ - function pause() public virtual { - require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to pause"); - _pause(); - } - - /** - * @dev Unpauses all token transfers. - * - * See {ERC721Pausable} and {Pausable-_unpause}. - * - * Requirements: - * - * - the caller must have the `PAUSER_ROLE`. - */ - function unpause() public virtual { - require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to unpause"); - _unpause(); - } - - function _beforeTokenTransfer( - address from, - address to, - uint256 tokenId - ) internal virtual override(ERC721, ERC721Enumerable, ERC721Pausable) { - super._beforeTokenTransfer(from, to, tokenId); - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(AccessControlEnumerable, ERC721, ERC721Enumerable) - returns (bool) - { - return super.supportsInterface(interfaceId); - } -} diff --git a/certora/munged/token/ERC721/utils/ERC721Holder.sol b/certora/munged/token/ERC721/utils/ERC721Holder.sol deleted file mode 100644 index e2aa303d8..000000000 --- a/certora/munged/token/ERC721/utils/ERC721Holder.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC721/utils/ERC721Holder.sol) - -pragma solidity ^0.8.0; - -import "../IERC721Receiver.sol"; - -/** - * @dev Implementation of the {IERC721Receiver} interface. - * - * Accepts all token transfers. - * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. - */ -contract ERC721Holder is IERC721Receiver { - /** - * @dev See {IERC721Receiver-onERC721Received}. - * - * Always returns `IERC721Receiver.onERC721Received.selector`. - */ - function onERC721Received( - address, - address, - uint256, - bytes memory - ) public virtual override returns (bytes4) { - return this.onERC721Received.selector; - } -} diff --git a/certora/munged/token/ERC777/ERC777.sol b/certora/munged/token/ERC777/ERC777.sol deleted file mode 100644 index 643e5a3f7..000000000 --- a/certora/munged/token/ERC777/ERC777.sol +++ /dev/null @@ -1,539 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC777/ERC777.sol) - -pragma solidity ^0.8.0; - -import "./IERC777.sol"; -import "./IERC777Recipient.sol"; -import "./IERC777Sender.sol"; -import "../ERC20/IERC20.sol"; -import "../../utils/Address.sol"; -import "../../utils/Context.sol"; -import "../../utils/introspection/IERC1820Registry.sol"; - -/** - * @dev Implementation of the {IERC777} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * - * Support for ERC20 is included in this contract, as specified by the EIP: both - * the ERC777 and ERC20 interfaces can be safely used when interacting with it. - * Both {IERC777-Sent} and {IERC20-Transfer} events are emitted on token - * movements. - * - * Additionally, the {IERC777-granularity} value is hard-coded to `1`, meaning that there - * are no special restrictions in the amount of tokens that created, moved, or - * destroyed. This makes integration with ERC20 applications seamless. - */ -contract ERC777 is Context, IERC777, IERC20 { - using Address for address; - - IERC1820Registry internal constant _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - - mapping(address => uint256) private _balances; - - uint256 private _totalSupply; - - string private _name; - string private _symbol; - - bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); - bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); - - // This isn't ever read from - it's only used to respond to the defaultOperators query. - address[] private _defaultOperatorsArray; - - // Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators). - mapping(address => bool) private _defaultOperators; - - // For each account, a mapping of its operators and revoked default operators. - mapping(address => mapping(address => bool)) private _operators; - mapping(address => mapping(address => bool)) private _revokedDefaultOperators; - - // ERC20-allowances - mapping(address => mapping(address => uint256)) private _allowances; - - /** - * @dev `defaultOperators` may be an empty array. - */ - constructor( - string memory name_, - string memory symbol_, - address[] memory defaultOperators_ - ) { - _name = name_; - _symbol = symbol_; - - _defaultOperatorsArray = defaultOperators_; - for (uint256 i = 0; i < defaultOperators_.length; i++) { - _defaultOperators[defaultOperators_[i]] = true; - } - - // register interfaces - _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this)); - _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this)); - } - - /** - * @dev See {IERC777-name}. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev See {IERC777-symbol}. - */ - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - /** - * @dev See {ERC20-decimals}. - * - * Always returns 18, as per the - * [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility). - */ - function decimals() public pure virtual returns (uint8) { - return 18; - } - - /** - * @dev See {IERC777-granularity}. - * - * This implementation always returns `1`. - */ - function granularity() public view virtual override returns (uint256) { - return 1; - } - - /** - * @dev See {IERC777-totalSupply}. - */ - function totalSupply() public view virtual override(IERC20, IERC777) returns (uint256) { - return _totalSupply; - } - - /** - * @dev Returns the amount of tokens owned by an account (`tokenHolder`). - */ - function balanceOf(address tokenHolder) public view virtual override(IERC20, IERC777) returns (uint256) { - return _balances[tokenHolder]; - } - - /** - * @dev See {IERC777-send}. - * - * Also emits a {IERC20-Transfer} event for ERC20 compatibility. - */ - function send( - address recipient, - uint256 amount, - bytes memory data - ) public virtual override { - _send(_msgSender(), recipient, amount, data, "", true); - } - - /** - * @dev See {IERC20-transfer}. - * - * Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient} - * interface if it is a contract. - * - * Also emits a {Sent} event. - */ - function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - require(recipient != address(0), "ERC777: transfer to the zero address"); - - address from = _msgSender(); - - _callTokensToSend(from, from, recipient, amount, "", ""); - - _move(from, from, recipient, amount, "", ""); - - _callTokensReceived(from, from, recipient, amount, "", "", false); - - return true; - } - - /** - * @dev See {IERC777-burn}. - * - * Also emits a {IERC20-Transfer} event for ERC20 compatibility. - */ - function burn(uint256 amount, bytes memory data) public virtual override { - _burn(_msgSender(), amount, data, ""); - } - - /** - * @dev See {IERC777-isOperatorFor}. - */ - function isOperatorFor(address operator, address tokenHolder) public view virtual override returns (bool) { - return - operator == tokenHolder || - (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) || - _operators[tokenHolder][operator]; - } - - /** - * @dev See {IERC777-authorizeOperator}. - */ - function authorizeOperator(address operator) public virtual override { - require(_msgSender() != operator, "ERC777: authorizing self as operator"); - - if (_defaultOperators[operator]) { - delete _revokedDefaultOperators[_msgSender()][operator]; - } else { - _operators[_msgSender()][operator] = true; - } - - emit AuthorizedOperator(operator, _msgSender()); - } - - /** - * @dev See {IERC777-revokeOperator}. - */ - function revokeOperator(address operator) public virtual override { - require(operator != _msgSender(), "ERC777: revoking self as operator"); - - if (_defaultOperators[operator]) { - _revokedDefaultOperators[_msgSender()][operator] = true; - } else { - delete _operators[_msgSender()][operator]; - } - - emit RevokedOperator(operator, _msgSender()); - } - - /** - * @dev See {IERC777-defaultOperators}. - */ - function defaultOperators() public view virtual override returns (address[] memory) { - return _defaultOperatorsArray; - } - - /** - * @dev See {IERC777-operatorSend}. - * - * Emits {Sent} and {IERC20-Transfer} events. - */ - function operatorSend( - address sender, - address recipient, - uint256 amount, - bytes memory data, - bytes memory operatorData - ) public virtual override { - require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder"); - _send(sender, recipient, amount, data, operatorData, true); - } - - /** - * @dev See {IERC777-operatorBurn}. - * - * Emits {Burned} and {IERC20-Transfer} events. - */ - function operatorBurn( - address account, - uint256 amount, - bytes memory data, - bytes memory operatorData - ) public virtual override { - require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder"); - _burn(account, amount, data, operatorData); - } - - /** - * @dev See {IERC20-allowance}. - * - * Note that operator and allowance concepts are orthogonal: operators may - * not have allowance, and accounts with allowance may not be operators - * themselves. - */ - function allowance(address holder, address spender) public view virtual override returns (uint256) { - return _allowances[holder][spender]; - } - - /** - * @dev See {IERC20-approve}. - * - * Note that accounts cannot have allowance issued by their operators. - */ - function approve(address spender, uint256 value) public virtual override returns (bool) { - address holder = _msgSender(); - _approve(holder, spender, value); - return true; - } - - /** - * @dev See {IERC20-transferFrom}. - * - * Note that operator and allowance concepts are orthogonal: operators cannot - * call `transferFrom` (unless they have allowance), and accounts with - * allowance cannot call `operatorSend` (unless they are operators). - * - * Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events. - */ - function transferFrom( - address holder, - address recipient, - uint256 amount - ) public virtual override returns (bool) { - require(recipient != address(0), "ERC777: transfer to the zero address"); - require(holder != address(0), "ERC777: transfer from the zero address"); - - address spender = _msgSender(); - - _callTokensToSend(spender, holder, recipient, amount, "", ""); - - _move(spender, holder, recipient, amount, "", ""); - - uint256 currentAllowance = _allowances[holder][spender]; - require(currentAllowance >= amount, "ERC777: transfer amount exceeds allowance"); - _approve(holder, spender, currentAllowance - amount); - - _callTokensReceived(spender, holder, recipient, amount, "", "", false); - - return true; - } - - /** - * @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * If a send hook is registered for `account`, the corresponding function - * will be called with `operator`, `data` and `operatorData`. - * - * See {IERC777Sender} and {IERC777Recipient}. - * - * Emits {Minted} and {IERC20-Transfer} events. - * - * Requirements - * - * - `account` cannot be the zero address. - * - if `account` is a contract, it must implement the {IERC777Recipient} - * interface. - */ - function _mint( - address account, - uint256 amount, - bytes memory userData, - bytes memory operatorData - ) internal virtual { - _mint(account, amount, userData, operatorData, true); - } - - /** - * @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * If `requireReceptionAck` is set to true, and if a send hook is - * registered for `account`, the corresponding function will be called with - * `operator`, `data` and `operatorData`. - * - * See {IERC777Sender} and {IERC777Recipient}. - * - * Emits {Minted} and {IERC20-Transfer} events. - * - * Requirements - * - * - `account` cannot be the zero address. - * - if `account` is a contract, it must implement the {IERC777Recipient} - * interface. - */ - function _mint( - address account, - uint256 amount, - bytes memory userData, - bytes memory operatorData, - bool requireReceptionAck - ) internal virtual { - require(account != address(0), "ERC777: mint to the zero address"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, address(0), account, amount); - - // Update state variables - _totalSupply += amount; - _balances[account] += amount; - - _callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck); - - emit Minted(operator, account, amount, userData, operatorData); - emit Transfer(address(0), account, amount); - } - - /** - * @dev Send tokens - * @param from address token holder address - * @param to address recipient address - * @param amount uint256 amount of tokens to transfer - * @param userData bytes extra information provided by the token holder (if any) - * @param operatorData bytes extra information provided by the operator (if any) - * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient - */ - function _send( - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData, - bool requireReceptionAck - ) internal virtual { - require(from != address(0), "ERC777: send from the zero address"); - require(to != address(0), "ERC777: send to the zero address"); - - address operator = _msgSender(); - - _callTokensToSend(operator, from, to, amount, userData, operatorData); - - _move(operator, from, to, amount, userData, operatorData); - - _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck); - } - - /** - * @dev Burn tokens - * @param from address token holder address - * @param amount uint256 amount of tokens to burn - * @param data bytes extra information provided by the token holder - * @param operatorData bytes extra information provided by the operator (if any) - */ - function _burn( - address from, - uint256 amount, - bytes memory data, - bytes memory operatorData - ) internal virtual { - require(from != address(0), "ERC777: burn from the zero address"); - - address operator = _msgSender(); - - _callTokensToSend(operator, from, address(0), amount, data, operatorData); - - _beforeTokenTransfer(operator, from, address(0), amount); - - // Update state variables - uint256 fromBalance = _balances[from]; - require(fromBalance >= amount, "ERC777: burn amount exceeds balance"); - unchecked { - _balances[from] = fromBalance - amount; - } - _totalSupply -= amount; - - emit Burned(operator, from, amount, data, operatorData); - emit Transfer(from, address(0), amount); - } - - function _move( - address operator, - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData - ) private { - _beforeTokenTransfer(operator, from, to, amount); - - uint256 fromBalance = _balances[from]; - require(fromBalance >= amount, "ERC777: transfer amount exceeds balance"); - unchecked { - _balances[from] = fromBalance - amount; - } - _balances[to] += amount; - - emit Sent(operator, from, to, amount, userData, operatorData); - emit Transfer(from, to, amount); - } - - /** - * @dev See {ERC20-_approve}. - * - * Note that accounts cannot have allowance issued by their operators. - */ - function _approve( - address holder, - address spender, - uint256 value - ) internal { - require(holder != address(0), "ERC777: approve from the zero address"); - require(spender != address(0), "ERC777: approve to the zero address"); - - _allowances[holder][spender] = value; - emit Approval(holder, spender, value); - } - - /** - * @dev Call from.tokensToSend() if the interface is registered - * @param operator address operator requesting the transfer - * @param from address token holder address - * @param to address recipient address - * @param amount uint256 amount of tokens to transfer - * @param userData bytes extra information provided by the token holder (if any) - * @param operatorData bytes extra information provided by the operator (if any) - */ - function _callTokensToSend( - address operator, - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData - ) private { - address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH); - if (implementer != address(0)) { - IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData); - } - } - - /** - * @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but - * tokensReceived() was not registered for the recipient - * @param operator address operator requesting the transfer - * @param from address token holder address - * @param to address recipient address - * @param amount uint256 amount of tokens to transfer - * @param userData bytes extra information provided by the token holder (if any) - * @param operatorData bytes extra information provided by the operator (if any) - * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient - */ - function _callTokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData, - bool requireReceptionAck - ) private { - address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH); - if (implementer != address(0)) { - IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData); - } else if (requireReceptionAck) { - require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient"); - } - } - - /** - * @dev Hook that is called before any token transfer. This includes - * calls to {send}, {transfer}, {operatorSend}, minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be to transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer( - address operator, - address from, - address to, - uint256 amount - ) internal virtual {} -} diff --git a/certora/munged/token/ERC777/IERC777.sol b/certora/munged/token/ERC777/IERC777.sol deleted file mode 100644 index 675d6133e..000000000 --- a/certora/munged/token/ERC777/IERC777.sol +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC777/IERC777.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC777Token standard as defined in the EIP. - * - * This contract uses the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let - * token holders and recipients react to token movements by using setting implementers - * for the associated interfaces in said registry. See {IERC1820Registry} and - * {ERC1820Implementer}. - */ -interface IERC777 { - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the smallest part of the token that is not divisible. This - * means all token operations (creation, movement and destruction) must have - * amounts that are a multiple of this number. - * - * For most token contracts, this value will equal 1. - */ - function granularity() external view returns (uint256); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by an account (`owner`). - */ - function balanceOf(address owner) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * If send or receive hooks are registered for the caller and `recipient`, - * the corresponding functions will be called with `data` and empty - * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. - * - * Emits a {Sent} event. - * - * Requirements - * - * - the caller must have at least `amount` tokens. - * - `recipient` cannot be the zero address. - * - if `recipient` is a contract, it must implement the {IERC777Recipient} - * interface. - */ - function send( - address recipient, - uint256 amount, - bytes calldata data - ) external; - - /** - * @dev Destroys `amount` tokens from the caller's account, reducing the - * total supply. - * - * If a send hook is registered for the caller, the corresponding function - * will be called with `data` and empty `operatorData`. See {IERC777Sender}. - * - * Emits a {Burned} event. - * - * Requirements - * - * - the caller must have at least `amount` tokens. - */ - function burn(uint256 amount, bytes calldata data) external; - - /** - * @dev Returns true if an account is an operator of `tokenHolder`. - * Operators can send and burn tokens on behalf of their owners. All - * accounts are their own operator. - * - * See {operatorSend} and {operatorBurn}. - */ - function isOperatorFor(address operator, address tokenHolder) external view returns (bool); - - /** - * @dev Make an account an operator of the caller. - * - * See {isOperatorFor}. - * - * Emits an {AuthorizedOperator} event. - * - * Requirements - * - * - `operator` cannot be calling address. - */ - function authorizeOperator(address operator) external; - - /** - * @dev Revoke an account's operator status for the caller. - * - * See {isOperatorFor} and {defaultOperators}. - * - * Emits a {RevokedOperator} event. - * - * Requirements - * - * - `operator` cannot be calling address. - */ - function revokeOperator(address operator) external; - - /** - * @dev Returns the list of default operators. These accounts are operators - * for all token holders, even if {authorizeOperator} was never called on - * them. - * - * This list is immutable, but individual holders may revoke these via - * {revokeOperator}, in which case {isOperatorFor} will return false. - */ - function defaultOperators() external view returns (address[] memory); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must - * be an operator of `sender`. - * - * If send or receive hooks are registered for `sender` and `recipient`, - * the corresponding functions will be called with `data` and - * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. - * - * Emits a {Sent} event. - * - * Requirements - * - * - `sender` cannot be the zero address. - * - `sender` must have at least `amount` tokens. - * - the caller must be an operator for `sender`. - * - `recipient` cannot be the zero address. - * - if `recipient` is a contract, it must implement the {IERC777Recipient} - * interface. - */ - function operatorSend( - address sender, - address recipient, - uint256 amount, - bytes calldata data, - bytes calldata operatorData - ) external; - - /** - * @dev Destroys `amount` tokens from `account`, reducing the total supply. - * The caller must be an operator of `account`. - * - * If a send hook is registered for `account`, the corresponding function - * will be called with `data` and `operatorData`. See {IERC777Sender}. - * - * Emits a {Burned} event. - * - * Requirements - * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. - * - the caller must be an operator for `account`. - */ - function operatorBurn( - address account, - uint256 amount, - bytes calldata data, - bytes calldata operatorData - ) external; - - event Sent( - address indexed operator, - address indexed from, - address indexed to, - uint256 amount, - bytes data, - bytes operatorData - ); - - event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); - - event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); - - event AuthorizedOperator(address indexed operator, address indexed tokenHolder); - - event RevokedOperator(address indexed operator, address indexed tokenHolder); -} diff --git a/certora/munged/token/ERC777/IERC777Recipient.sol b/certora/munged/token/ERC777/IERC777Recipient.sol deleted file mode 100644 index 3a845f662..000000000 --- a/certora/munged/token/ERC777/IERC777Recipient.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC777/IERC777Recipient.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. - * - * Accounts can be notified of {IERC777} tokens being sent to them by having a - * contract implement this interface (contract holders can be their own - * implementer) and registering it on the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. - * - * See {IERC1820Registry} and {ERC1820Implementer}. - */ -interface IERC777Recipient { - /** - * @dev Called by an {IERC777} token contract whenever tokens are being - * moved or created into a registered account (`to`). The type of operation - * is conveyed by `from` being the zero address or not. - * - * This call occurs _after_ the token contract's state is updated, so - * {IERC777-balanceOf}, etc., can be used to query the post-operation state. - * - * This function may revert to prevent the operation from being executed. - */ - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external; -} diff --git a/certora/munged/token/ERC777/IERC777Sender.sol b/certora/munged/token/ERC777/IERC777Sender.sol deleted file mode 100644 index 6f2e36080..000000000 --- a/certora/munged/token/ERC777/IERC777Sender.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC777/IERC777Sender.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC777TokensSender standard as defined in the EIP. - * - * {IERC777} Token holders can be notified of operations performed on their - * tokens by having a contract implement this interface (contract holders can be - * their own implementer) and registering it on the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. - * - * See {IERC1820Registry} and {ERC1820Implementer}. - */ -interface IERC777Sender { - /** - * @dev Called by an {IERC777} token contract whenever a registered holder's - * (`from`) tokens are about to be moved or destroyed. The type of operation - * is conveyed by `to` being the zero address or not. - * - * This call occurs _before_ the token contract's state is updated, so - * {IERC777-balanceOf}, etc., can be used to query the pre-operation state. - * - * This function may revert to prevent the operation from being executed. - */ - function tokensToSend( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external; -} diff --git a/certora/munged/token/ERC777/README.adoc b/certora/munged/token/ERC777/README.adoc deleted file mode 100644 index d8f25f060..000000000 --- a/certora/munged/token/ERC777/README.adoc +++ /dev/null @@ -1,30 +0,0 @@ -= ERC 777 - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc777 - -This set of interfaces and contracts are all related to the [ERC777 token standard](https://eips.ethereum.org/EIPS/eip-777). - -TIP: For an overview of ERC777 tokens and a walk through on how to create a token contract read our xref:ROOT:erc777.adoc[ERC777 guide]. - -The token behavior itself is implemented in the core contracts: {IERC777}, {ERC777}. - -Additionally there are interfaces used to develop contracts that react to token movements: {IERC777Sender}, {IERC777Recipient}. - -== Core - -{{IERC777}} - -{{ERC777}} - -== Hooks - -{{IERC777Sender}} - -{{IERC777Recipient}} - -== Presets - -These contracts are preconfigured combinations of features. They can be used through inheritance or as models to copy and paste their source code. - -{{ERC777PresetFixedSupply}} diff --git a/certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol b/certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol deleted file mode 100644 index d0ce2366a..000000000 --- a/certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (token/ERC777/presets/ERC777PresetFixedSupply.sol) -pragma solidity ^0.8.0; - -import "../ERC777.sol"; - -/** - * @dev {ERC777} token, including: - * - * - Preminted initial supply - * - No access control mechanism (for minting/pausing) and hence no governance - * - * _Available since v3.4._ - */ -contract ERC777PresetFixedSupply is ERC777 { - /** - * @dev Mints `initialSupply` amount of token and transfers them to `owner`. - * - * See {ERC777-constructor}. - */ - constructor( - string memory name, - string memory symbol, - address[] memory defaultOperators, - uint256 initialSupply, - address owner - ) ERC777(name, symbol, defaultOperators) { - _mint(owner, initialSupply, "", ""); - } -} diff --git a/certora/munged/utils/Address.sol b/certora/munged/utils/Address.sol deleted file mode 100644 index 1bb7af5c7..000000000 --- a/certora/munged/utils/Address.sol +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/Address.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize, which returns 0 for contracts in - // construction, since the code is only stored at the end of the - // constructor execution. - - uint256 size; - assembly { - size := extcodesize(account) - } - return size > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} diff --git a/certora/munged/utils/Arrays.sol b/certora/munged/utils/Arrays.sol deleted file mode 100644 index 70ba37e70..000000000 --- a/certora/munged/utils/Arrays.sol +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/Arrays.sol) - -pragma solidity ^0.8.0; - -import "./math/Math.sol"; - -/** - * @dev Collection of functions related to array types. - */ -library Arrays { - /** - * @dev Searches a sorted `array` and returns the first index that contains - * a value greater or equal to `element`. If no such index exists (i.e. all - * values in the array are strictly less than `element`), the array length is - * returned. Time complexity O(log n). - * - * `array` is expected to be sorted in ascending order, and to contain no - * repeated elements. - */ - function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) { - if (array.length == 0) { - return 0; - } - - uint256 low = 0; - uint256 high = array.length; - - while (low < high) { - uint256 mid = Math.average(low, high); - - // Note that mid will always be strictly less than high (i.e. it will be a valid array index) - // because Math.average rounds down (it does integer division with truncation). - if (array[mid] > element) { - high = mid; - } else { - low = mid + 1; - } - } - - // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound. - if (low > 0 && array[low - 1] == element) { - return low - 1; - } else { - return low; - } - } -} diff --git a/certora/munged/utils/Context.sol b/certora/munged/utils/Context.sol deleted file mode 100644 index 1a4936b24..000000000 --- a/certora/munged/utils/Context.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/Context.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} diff --git a/certora/munged/utils/Counters.sol b/certora/munged/utils/Counters.sol deleted file mode 100644 index 148c9fda1..000000000 --- a/certora/munged/utils/Counters.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/Counters.sol) - -pragma solidity ^0.8.0; - -/** - * @title Counters - * @author Matt Condon (@shrugs) - * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number - * of elements in a mapping, issuing ERC721 ids, or counting request ids. - * - * Include with `using Counters for Counters.Counter;` - */ -library Counters { - struct Counter { - // This variable should never be directly accessed by users of the library: interactions must be restricted to - // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add - // this feature: see https://github.com/ethereum/solidity/issues/4637 - uint256 _value; // default: 0 - } - - function current(Counter storage counter) internal view returns (uint256) { - return counter._value; - } - - function increment(Counter storage counter) internal { - unchecked { - counter._value += 1; - } - } - - function decrement(Counter storage counter) internal { - uint256 value = counter._value; - require(value > 0, "Counter: decrement overflow"); - unchecked { - counter._value = value - 1; - } - } - - function reset(Counter storage counter) internal { - counter._value = 0; - } -} diff --git a/certora/munged/utils/Create2.sol b/certora/munged/utils/Create2.sol deleted file mode 100644 index 0faa469f4..000000000 --- a/certora/munged/utils/Create2.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/Create2.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. - * `CREATE2` can be used to compute in advance the address where a smart - * contract will be deployed, which allows for interesting new mechanisms known - * as 'counterfactual interactions'. - * - * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more - * information. - */ -library Create2 { - /** - * @dev Deploys a contract using `CREATE2`. The address where the contract - * will be deployed can be known in advance via {computeAddress}. - * - * The bytecode for a contract can be obtained from Solidity with - * `type(contractName).creationCode`. - * - * Requirements: - * - * - `bytecode` must not be empty. - * - `salt` must have not been used for `bytecode` already. - * - the factory must have a balance of at least `amount`. - * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. - */ - function deploy( - uint256 amount, - bytes32 salt, - bytes memory bytecode - ) internal returns (address) { - address addr; - require(address(this).balance >= amount, "Create2: insufficient balance"); - require(bytecode.length != 0, "Create2: bytecode length is zero"); - assembly { - addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) - } - require(addr != address(0), "Create2: Failed on deploy"); - return addr; - } - - /** - * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the - * `bytecodeHash` or `salt` will result in a new destination address. - */ - function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { - return computeAddress(salt, bytecodeHash, address(this)); - } - - /** - * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at - * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. - */ - function computeAddress( - bytes32 salt, - bytes32 bytecodeHash, - address deployer - ) internal pure returns (address) { - bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)); - return address(uint160(uint256(_data))); - } -} diff --git a/certora/munged/utils/Multicall.sol b/certora/munged/utils/Multicall.sol deleted file mode 100644 index 81a0291da..000000000 --- a/certora/munged/utils/Multicall.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/Multicall.sol) - -pragma solidity ^0.8.0; - -import "./Address.sol"; - -/** - * @dev Provides a function to batch together multiple calls in a single external call. - * - * _Available since v4.1._ - */ -abstract contract Multicall { - /** - * @dev Receives and executes a batch of function calls on this contract. - */ - function multicall(bytes[] calldata data) external returns (bytes[] memory results) { - results = new bytes[](data.length); - for (uint256 i = 0; i < data.length; i++) { - results[i] = Address.functionDelegateCall(address(this), data[i]); - } - return results; - } -} diff --git a/certora/munged/utils/README.adoc b/certora/munged/utils/README.adoc deleted file mode 100644 index 4edcf923b..000000000 --- a/certora/munged/utils/README.adoc +++ /dev/null @@ -1,103 +0,0 @@ -= Utilities - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/utils - -Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives. - -The {Address}, {Arrays} and {Strings} libraries provide more operations related to these native data types, while {SafeCast} adds ways to safely convert between the different signed and unsigned numeric types. -{Multicall} provides a function to batch together multiple calls in a single external call. - -For new data types: - - * {Counters}: a simple way to get a counter that can only be incremented, decremented or reset. Very useful for ID generation, counting contract activity, among others. - * {EnumerableMap}: like Solidity's https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] type, but with key-value _enumeration_: this will let you know how many entries a mapping has, and iterate over them (which is not possible with `mapping`). - * {EnumerableSet}: like {EnumerableMap}, but for https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets]. Can be used to store privileged accounts, issued IDs, etc. - -[NOTE] -==== -Because Solidity does not support generic types, {EnumerableMap} and {EnumerableSet} are specialized to a limited number of key-value types. - -As of v3.0, {EnumerableMap} supports `uint256 -> address` (`UintToAddressMap`), and {EnumerableSet} supports `address` and `uint256` (`AddressSet` and `UintSet`). -==== - -Finally, {Create2} contains all necessary utilities to safely use the https://blog.openzeppelin.com/getting-the-most-out-of-create2/[`CREATE2` EVM opcode], without having to deal with low-level assembly. - -== Math - -{{Math}} - -{{SafeCast}} - -{{SafeMath}} - -{{SignedSafeMath}} - -== Cryptography - -{{ECDSA}} - -{{SignatureChecker}} - -{{MerkleProof}} - -{{EIP712}} - -== Escrow - -{{ConditionalEscrow}} - -{{Escrow}} - -{{RefundEscrow}} - -== Introspection - -This set of interfaces and contracts deal with https://en.wikipedia.org/wiki/Type_introspection[type introspection] of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract's _interface_. - -Ethereum contracts have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. `ERC20` tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract _declaring_ its interface can be very helpful in preventing errors. - -There are two main ways to approach this. - -* Locally, where a contract implements `IERC165` and declares an interface, and a second one queries it directly via `ERC165Checker`. -* Globally, where a global and unique registry (`IERC1820Registry`) is used to register implementers of a certain interface (`IERC1820Implementer`). It is then the registry that is queried, which allows for more complex setups, like contracts implementing interfaces for externally-owned accounts. - -Note that, in all cases, accounts simply _declare_ their interfaces, but they are not required to actually implement them. This mechanism can therefore be used to both prevent errors and allow for complex interactions (see `ERC777`), but it must not be relied on for security. - -{{IERC165}} - -{{ERC165}} - -{{ERC165Storage}} - -{{ERC165Checker}} - -{{IERC1820Registry}} - -{{IERC1820Implementer}} - -{{ERC1820Implementer}} - -== Data Structures - -{{BitMaps}} - -{{EnumerableMap}} - -{{EnumerableSet}} - -== Libraries - -{{Create2}} - -{{Address}} - -{{Arrays}} - -{{Counters}} - -{{Strings}} - -{{StorageSlot}} - -{{Multicall}} diff --git a/certora/munged/utils/StorageSlot.sol b/certora/munged/utils/StorageSlot.sol deleted file mode 100644 index d8ceee4af..000000000 --- a/certora/munged/utils/StorageSlot.sol +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/StorageSlot.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Library for reading and writing primitive types to specific storage slots. - * - * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. - * This library helps with reading and writing to such slots without the need for inline assembly. - * - * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. - * - * Example usage to set ERC1967 implementation slot: - * ``` - * contract ERC1967 { - * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - * - * function _getImplementation() internal view returns (address) { - * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; - * } - * - * function _setImplementation(address newImplementation) internal { - * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); - * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; - * } - * } - * ``` - * - * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ - */ -library StorageSlot { - struct AddressSlot { - address value; - } - - struct BooleanSlot { - bool value; - } - - struct Bytes32Slot { - bytes32 value; - } - - struct Uint256Slot { - uint256 value; - } - - /** - * @dev Returns an `AddressSlot` with member `value` located at `slot`. - */ - function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `BooleanSlot` with member `value` located at `slot`. - */ - function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. - */ - function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `Uint256Slot` with member `value` located at `slot`. - */ - function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { - assembly { - r.slot := slot - } - } -} diff --git a/certora/munged/utils/Strings.sol b/certora/munged/utils/Strings.sol deleted file mode 100644 index b84cda609..000000000 --- a/certora/munged/utils/Strings.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/Strings.sol) - -pragma solidity ^0.8.0; - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } -} diff --git a/certora/munged/utils/Timers.sol b/certora/munged/utils/Timers.sol deleted file mode 100644 index 5c2145c2f..000000000 --- a/certora/munged/utils/Timers.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/Timers.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Tooling for timepoints, timers and delays - */ -library Timers { - struct Timestamp { - uint64 _deadline; - } - - function getDeadline(Timestamp memory timer) internal pure returns (uint64) { - return timer._deadline; - } - - function setDeadline(Timestamp storage timer, uint64 timestamp) internal { - timer._deadline = timestamp; - } - - function reset(Timestamp storage timer) internal { - timer._deadline = 0; - } - - function isUnset(Timestamp memory timer) internal pure returns (bool) { - return timer._deadline == 0; - } - - function isStarted(Timestamp memory timer) internal pure returns (bool) { - return timer._deadline > 0; - } - - function isPending(Timestamp memory timer) internal view returns (bool) { - return timer._deadline > block.timestamp; - } - - function isExpired(Timestamp memory timer) internal view returns (bool) { - return isStarted(timer) && timer._deadline <= block.timestamp; - } - - struct BlockNumber { - uint64 _deadline; - } - - function getDeadline(BlockNumber memory timer) internal pure returns (uint64) { - return timer._deadline; - } - - function setDeadline(BlockNumber storage timer, uint64 timestamp) internal { - timer._deadline = timestamp; - } - - function reset(BlockNumber storage timer) internal { - timer._deadline = 0; - } - - function isUnset(BlockNumber memory timer) internal pure returns (bool) { - return timer._deadline == 0; - } - - function isStarted(BlockNumber memory timer) internal pure returns (bool) { - return timer._deadline > 0; - } - - function isPending(BlockNumber memory timer) internal view returns (bool) { - return timer._deadline > block.number; - } - - function isExpired(BlockNumber memory timer) internal view returns (bool) { - return isStarted(timer) && timer._deadline <= block.number; - } -} diff --git a/certora/munged/utils/cryptography/ECDSA.sol b/certora/munged/utils/cryptography/ECDSA.sol deleted file mode 100644 index 7de57c04b..000000000 --- a/certora/munged/utils/cryptography/ECDSA.sol +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/cryptography/ECDSA.sol) - -pragma solidity ^0.8.0; - -import "../Strings.sol"; - -/** - * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. - * - * These functions can be used to verify that a message was signed by the holder - * of the private keys of a given address. - */ -library ECDSA { - enum RecoverError { - NoError, - InvalidSignature, - InvalidSignatureLength, - InvalidSignatureS, - InvalidSignatureV - } - - function _throwError(RecoverError error) private pure { - if (error == RecoverError.NoError) { - return; // no error: do nothing - } else if (error == RecoverError.InvalidSignature) { - revert("ECDSA: invalid signature"); - } else if (error == RecoverError.InvalidSignatureLength) { - revert("ECDSA: invalid signature length"); - } else if (error == RecoverError.InvalidSignatureS) { - revert("ECDSA: invalid signature 's' value"); - } else if (error == RecoverError.InvalidSignatureV) { - revert("ECDSA: invalid signature 'v' value"); - } - } - - /** - * @dev Returns the address that signed a hashed message (`hash`) with - * `signature` or error string. This address can then be used for verification purposes. - * - * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: - * this function rejects them by requiring the `s` value to be in the lower - * half order, and the `v` value to be either 27 or 28. - * - * IMPORTANT: `hash` _must_ be the result of a hash operation for the - * verification to be secure: it is possible to craft signatures that - * recover to arbitrary addresses for non-hashed data. A safe way to ensure - * this is by receiving a hash of the original message (which may otherwise - * be too long), and then calling {toEthSignedMessageHash} on it. - * - * Documentation for signature generation: - * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] - * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] - * - * _Available since v4.3._ - */ - function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { - // Check the signature length - // - case 65: r,s,v signature (standard) - // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ - if (signature.length == 65) { - bytes32 r; - bytes32 s; - uint8 v; - // ecrecover takes the signature parameters, and the only way to get them - // currently is to use assembly. - assembly { - r := mload(add(signature, 0x20)) - s := mload(add(signature, 0x40)) - v := byte(0, mload(add(signature, 0x60))) - } - return tryRecover(hash, v, r, s); - } else if (signature.length == 64) { - bytes32 r; - bytes32 vs; - // ecrecover takes the signature parameters, and the only way to get them - // currently is to use assembly. - assembly { - r := mload(add(signature, 0x20)) - vs := mload(add(signature, 0x40)) - } - return tryRecover(hash, r, vs); - } else { - return (address(0), RecoverError.InvalidSignatureLength); - } - } - - /** - * @dev Returns the address that signed a hashed message (`hash`) with - * `signature`. This address can then be used for verification purposes. - * - * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: - * this function rejects them by requiring the `s` value to be in the lower - * half order, and the `v` value to be either 27 or 28. - * - * IMPORTANT: `hash` _must_ be the result of a hash operation for the - * verification to be secure: it is possible to craft signatures that - * recover to arbitrary addresses for non-hashed data. A safe way to ensure - * this is by receiving a hash of the original message (which may otherwise - * be too long), and then calling {toEthSignedMessageHash} on it. - */ - function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, signature); - _throwError(error); - return recovered; - } - - /** - * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. - * - * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] - * - * _Available since v4.3._ - */ - function tryRecover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address, RecoverError) { - bytes32 s; - uint8 v; - assembly { - s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - v := add(shr(255, vs), 27) - } - return tryRecover(hash, v, r, s); - } - - /** - * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. - * - * _Available since v4.2._ - */ - function recover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, r, vs); - _throwError(error); - return recovered; - } - - /** - * @dev Overload of {ECDSA-tryRecover} that receives the `v`, - * `r` and `s` signature fields separately. - * - * _Available since v4.3._ - */ - function tryRecover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address, RecoverError) { - // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature - // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines - // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most - // signatures from current libraries generate a unique signature with an s-value in the lower half order. - // - // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value - // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or - // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept - // these malleable signatures as well. - if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { - return (address(0), RecoverError.InvalidSignatureS); - } - if (v != 27 && v != 28) { - return (address(0), RecoverError.InvalidSignatureV); - } - - // If the signature is valid (and not malleable), return the signer address - address signer = ecrecover(hash, v, r, s); - if (signer == address(0)) { - return (address(0), RecoverError.InvalidSignature); - } - - return (signer, RecoverError.NoError); - } - - /** - * @dev Overload of {ECDSA-recover} that receives the `v`, - * `r` and `s` signature fields separately. - */ - function recover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, v, r, s); - _throwError(error); - return recovered; - } - - /** - * @dev Returns an Ethereum Signed Message, created from a `hash`. This - * produces hash corresponding to the one signed with the - * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] - * JSON-RPC method as part of EIP-191. - * - * See {recover}. - */ - function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { - // 32 is the length in bytes of hash, - // enforced by the type signature above - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); - } - - /** - * @dev Returns an Ethereum Signed Message, created from `s`. This - * produces hash corresponding to the one signed with the - * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] - * JSON-RPC method as part of EIP-191. - * - * See {recover}. - */ - function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); - } - - /** - * @dev Returns an Ethereum Signed Typed Data, created from a - * `domainSeparator` and a `structHash`. This produces hash corresponding - * to the one signed with the - * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] - * JSON-RPC method as part of EIP-712. - * - * See {recover}. - */ - function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - } -} diff --git a/certora/munged/utils/cryptography/MerkleProof.sol b/certora/munged/utils/cryptography/MerkleProof.sol deleted file mode 100644 index 74e248f81..000000000 --- a/certora/munged/utils/cryptography/MerkleProof.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/cryptography/MerkleProof.sol) - -pragma solidity ^0.8.0; - -/** - * @dev These functions deal with verification of Merkle Trees proofs. - * - * The proofs can be generated using the JavaScript library - * https://github.com/miguelmota/merkletreejs[merkletreejs]. - * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. - * - * See `test/utils/cryptography/MerkleProof.test.js` for some examples. - */ -library MerkleProof { - /** - * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree - * defined by `root`. For this, a `proof` must be provided, containing - * sibling hashes on the branch from the leaf to the root of the tree. Each - * pair of leaves and each pair of pre-images are assumed to be sorted. - */ - function verify( - bytes32[] memory proof, - bytes32 root, - bytes32 leaf - ) internal pure returns (bool) { - return processProof(proof, leaf) == root; - } - - /** - * @dev Returns the rebuilt hash obtained by traversing a Merklee tree up - * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt - * hash matches the root of the tree. When processing the proof, the pairs - * of leafs & pre-images are assumed to be sorted. - * - * _Available since v4.4._ - */ - function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { - bytes32 computedHash = leaf; - for (uint256 i = 0; i < proof.length; i++) { - bytes32 proofElement = proof[i]; - if (computedHash <= proofElement) { - // Hash(current computed hash + current element of the proof) - computedHash = keccak256(abi.encodePacked(computedHash, proofElement)); - } else { - // Hash(current element of the proof + current computed hash) - computedHash = keccak256(abi.encodePacked(proofElement, computedHash)); - } - } - return computedHash; - } -} diff --git a/certora/munged/utils/cryptography/SignatureChecker.sol b/certora/munged/utils/cryptography/SignatureChecker.sol deleted file mode 100644 index f392feb8d..000000000 --- a/certora/munged/utils/cryptography/SignatureChecker.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/cryptography/SignatureChecker.sol) - -pragma solidity ^0.8.0; - -import "./ECDSA.sol"; -import "../Address.sol"; -import "../../interfaces/IERC1271.sol"; - -/** - * @dev Signature verification helper: Provide a single mechanism to verify both private-key (EOA) ECDSA signature and - * ERC1271 contract signatures. Using this instead of ECDSA.recover in your contract will make them compatible with - * smart contract wallets such as Argent and Gnosis. - * - * Note: unlike ECDSA signatures, contract signature's are revocable, and the outcome of this function can thus change - * through time. It could return true at block N and false at block N+1 (or the opposite). - * - * _Available since v4.1._ - */ -library SignatureChecker { - function isValidSignatureNow( - address signer, - bytes32 hash, - bytes memory signature - ) internal view returns (bool) { - (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature); - if (error == ECDSA.RecoverError.NoError && recovered == signer) { - return true; - } - - (bool success, bytes memory result) = signer.staticcall( - abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature) - ); - return (success && result.length == 32 && abi.decode(result, (bytes4)) == IERC1271.isValidSignature.selector); - } -} diff --git a/certora/munged/utils/cryptography/draft-EIP712.sol b/certora/munged/utils/cryptography/draft-EIP712.sol deleted file mode 100644 index 918fd3297..000000000 --- a/certora/munged/utils/cryptography/draft-EIP712.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/cryptography/draft-EIP712.sol) - -pragma solidity ^0.8.0; - -import "./ECDSA.sol"; - -/** - * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. - * - * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, - * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding - * they need in their contracts using a combination of `abi.encode` and `keccak256`. - * - * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding - * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA - * ({_hashTypedDataV4}). - * - * The implementation of the domain separator was designed to be as efficient as possible while still properly updating - * the chain id to protect against replay attacks on an eventual fork of the chain. - * - * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method - * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. - * - * _Available since v3.4._ - */ -abstract contract EIP712 { - /* solhint-disable var-name-mixedcase */ - // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to - // invalidate the cached domain separator if the chain id changes. - bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; - uint256 private immutable _CACHED_CHAIN_ID; - address private immutable _CACHED_THIS; - - bytes32 private immutable _HASHED_NAME; - bytes32 private immutable _HASHED_VERSION; - bytes32 private immutable _TYPE_HASH; - - /* solhint-enable var-name-mixedcase */ - - /** - * @dev Initializes the domain separator and parameter caches. - * - * The meaning of `name` and `version` is specified in - * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: - * - * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. - * - `version`: the current major version of the signing domain. - * - * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart - * contract upgrade]. - */ - constructor(string memory name, string memory version) { - bytes32 hashedName = keccak256(bytes(name)); - bytes32 hashedVersion = keccak256(bytes(version)); - bytes32 typeHash = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - _HASHED_NAME = hashedName; - _HASHED_VERSION = hashedVersion; - _CACHED_CHAIN_ID = block.chainid; - _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); - _CACHED_THIS = address(this); - _TYPE_HASH = typeHash; - } - - /** - * @dev Returns the domain separator for the current chain. - */ - function _domainSeparatorV4() internal view returns (bytes32) { - if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { - return _CACHED_DOMAIN_SEPARATOR; - } else { - return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); - } - } - - function _buildDomainSeparator( - bytes32 typeHash, - bytes32 nameHash, - bytes32 versionHash - ) private view returns (bytes32) { - return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); - } - - /** - * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this - * function returns the hash of the fully encoded EIP712 message for this domain. - * - * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: - * - * ```solidity - * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( - * keccak256("Mail(address to,string contents)"), - * mailTo, - * keccak256(bytes(mailContents)) - * ))); - * address signer = ECDSA.recover(digest, signature); - * ``` - */ - function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { - return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); - } -} diff --git a/certora/munged/utils/escrow/ConditionalEscrow.sol b/certora/munged/utils/escrow/ConditionalEscrow.sol deleted file mode 100644 index 9f4c6aee3..000000000 --- a/certora/munged/utils/escrow/ConditionalEscrow.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/escrow/ConditionalEscrow.sol) - -pragma solidity ^0.8.0; - -import "./Escrow.sol"; - -/** - * @title ConditionalEscrow - * @dev Base abstract escrow to only allow withdrawal if a condition is met. - * @dev Intended usage: See {Escrow}. Same usage guidelines apply here. - */ -abstract contract ConditionalEscrow is Escrow { - /** - * @dev Returns whether an address is allowed to withdraw their funds. To be - * implemented by derived contracts. - * @param payee The destination address of the funds. - */ - function withdrawalAllowed(address payee) public view virtual returns (bool); - - function withdraw(address payable payee) public virtual override { - require(withdrawalAllowed(payee), "ConditionalEscrow: payee is not allowed to withdraw"); - super.withdraw(payee); - } -} diff --git a/certora/munged/utils/escrow/Escrow.sol b/certora/munged/utils/escrow/Escrow.sol deleted file mode 100644 index 9f23f84c2..000000000 --- a/certora/munged/utils/escrow/Escrow.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/escrow/Escrow.sol) - -pragma solidity ^0.8.0; - -import "../../access/Ownable.sol"; -import "../Address.sol"; - -/** - * @title Escrow - * @dev Base escrow contract, holds funds designated for a payee until they - * withdraw them. - * - * Intended usage: This contract (and derived escrow contracts) should be a - * standalone contract, that only interacts with the contract that instantiated - * it. That way, it is guaranteed that all Ether will be handled according to - * the `Escrow` rules, and there is no need to check for payable functions or - * transfers in the inheritance tree. The contract that uses the escrow as its - * payment method should be its owner, and provide public methods redirecting - * to the escrow's deposit and withdraw. - */ -contract Escrow is Ownable { - using Address for address payable; - - event Deposited(address indexed payee, uint256 weiAmount); - event Withdrawn(address indexed payee, uint256 weiAmount); - - mapping(address => uint256) private _deposits; - - function depositsOf(address payee) public view returns (uint256) { - return _deposits[payee]; - } - - /** - * @dev Stores the sent amount as credit to be withdrawn. - * @param payee The destination address of the funds. - */ - function deposit(address payee) public payable virtual onlyOwner { - uint256 amount = msg.value; - _deposits[payee] += amount; - emit Deposited(payee, amount); - } - - /** - * @dev Withdraw accumulated balance for a payee, forwarding all gas to the - * recipient. - * - * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. - * Make sure you trust the recipient, or are either following the - * checks-effects-interactions pattern or using {ReentrancyGuard}. - * - * @param payee The address whose funds will be withdrawn and transferred to. - */ - function withdraw(address payable payee) public virtual onlyOwner { - uint256 payment = _deposits[payee]; - - _deposits[payee] = 0; - - payee.sendValue(payment); - - emit Withdrawn(payee, payment); - } -} diff --git a/certora/munged/utils/escrow/RefundEscrow.sol b/certora/munged/utils/escrow/RefundEscrow.sol deleted file mode 100644 index d1218068a..000000000 --- a/certora/munged/utils/escrow/RefundEscrow.sol +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/escrow/RefundEscrow.sol) - -pragma solidity ^0.8.0; - -import "./ConditionalEscrow.sol"; - -/** - * @title RefundEscrow - * @dev Escrow that holds funds for a beneficiary, deposited from multiple - * parties. - * @dev Intended usage: See {Escrow}. Same usage guidelines apply here. - * @dev The owner account (that is, the contract that instantiates this - * contract) may deposit, close the deposit period, and allow for either - * withdrawal by the beneficiary, or refunds to the depositors. All interactions - * with `RefundEscrow` will be made through the owner contract. - */ -contract RefundEscrow is ConditionalEscrow { - using Address for address payable; - - enum State { - Active, - Refunding, - Closed - } - - event RefundsClosed(); - event RefundsEnabled(); - - State private _state; - address payable private immutable _beneficiary; - - /** - * @dev Constructor. - * @param beneficiary_ The beneficiary of the deposits. - */ - constructor(address payable beneficiary_) { - require(beneficiary_ != address(0), "RefundEscrow: beneficiary is the zero address"); - _beneficiary = beneficiary_; - _state = State.Active; - } - - /** - * @return The current state of the escrow. - */ - function state() public view virtual returns (State) { - return _state; - } - - /** - * @return The beneficiary of the escrow. - */ - function beneficiary() public view virtual returns (address payable) { - return _beneficiary; - } - - /** - * @dev Stores funds that may later be refunded. - * @param refundee The address funds will be sent to if a refund occurs. - */ - function deposit(address refundee) public payable virtual override { - require(state() == State.Active, "RefundEscrow: can only deposit while active"); - super.deposit(refundee); - } - - /** - * @dev Allows for the beneficiary to withdraw their funds, rejecting - * further deposits. - */ - function close() public virtual onlyOwner { - require(state() == State.Active, "RefundEscrow: can only close while active"); - _state = State.Closed; - emit RefundsClosed(); - } - - /** - * @dev Allows for refunds to take place, rejecting further deposits. - */ - function enableRefunds() public virtual onlyOwner { - require(state() == State.Active, "RefundEscrow: can only enable refunds while active"); - _state = State.Refunding; - emit RefundsEnabled(); - } - - /** - * @dev Withdraws the beneficiary's funds. - */ - function beneficiaryWithdraw() public virtual { - require(state() == State.Closed, "RefundEscrow: beneficiary can only withdraw while closed"); - beneficiary().sendValue(address(this).balance); - } - - /** - * @dev Returns whether refundees can withdraw their deposits (be refunded). The overridden function receives a - * 'payee' argument, but we ignore it here since the condition is global, not per-payee. - */ - function withdrawalAllowed(address) public view override returns (bool) { - return state() == State.Refunding; - } -} diff --git a/certora/munged/utils/introspection/ERC165.sol b/certora/munged/utils/introspection/ERC165.sol deleted file mode 100644 index 8253d2ddc..000000000 --- a/certora/munged/utils/introspection/ERC165.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/introspection/ERC165.sol) - -pragma solidity ^0.8.0; - -import "./IERC165.sol"; - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} diff --git a/certora/munged/utils/introspection/ERC165Checker.sol b/certora/munged/utils/introspection/ERC165Checker.sol deleted file mode 100644 index 5f5473332..000000000 --- a/certora/munged/utils/introspection/ERC165Checker.sol +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/introspection/ERC165Checker.sol) - -pragma solidity ^0.8.0; - -import "./IERC165.sol"; - -/** - * @dev Library used to query support of an interface declared via {IERC165}. - * - * Note that these functions return the actual result of the query: they do not - * `revert` if an interface is not supported. It is up to the caller to decide - * what to do in these cases. - */ -library ERC165Checker { - // As per the EIP-165 spec, no interface should ever match 0xffffffff - bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; - - /** - * @dev Returns true if `account` supports the {IERC165} interface, - */ - function supportsERC165(address account) internal view returns (bool) { - // Any contract that implements ERC165 must explicitly indicate support of - // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid - return - _supportsERC165Interface(account, type(IERC165).interfaceId) && - !_supportsERC165Interface(account, _INTERFACE_ID_INVALID); - } - - /** - * @dev Returns true if `account` supports the interface defined by - * `interfaceId`. Support for {IERC165} itself is queried automatically. - * - * See {IERC165-supportsInterface}. - */ - function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { - // query support of both ERC165 as per the spec and support of _interfaceId - return supportsERC165(account) && _supportsERC165Interface(account, interfaceId); - } - - /** - * @dev Returns a boolean array where each value corresponds to the - * interfaces passed in and whether they're supported or not. This allows - * you to batch check interfaces for a contract where your expectation - * is that some interfaces may not be supported. - * - * See {IERC165-supportsInterface}. - * - * _Available since v3.4._ - */ - function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) - internal - view - returns (bool[] memory) - { - // an array of booleans corresponding to interfaceIds and whether they're supported or not - bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); - - // query support of ERC165 itself - if (supportsERC165(account)) { - // query support of each interface in interfaceIds - for (uint256 i = 0; i < interfaceIds.length; i++) { - interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]); - } - } - - return interfaceIdsSupported; - } - - /** - * @dev Returns true if `account` supports all the interfaces defined in - * `interfaceIds`. Support for {IERC165} itself is queried automatically. - * - * Batch-querying can lead to gas savings by skipping repeated checks for - * {IERC165} support. - * - * See {IERC165-supportsInterface}. - */ - function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { - // query support of ERC165 itself - if (!supportsERC165(account)) { - return false; - } - - // query support of each interface in _interfaceIds - for (uint256 i = 0; i < interfaceIds.length; i++) { - if (!_supportsERC165Interface(account, interfaceIds[i])) { - return false; - } - } - - // all interfaces supported - return true; - } - - /** - * @notice Query if a contract implements an interface, does not check ERC165 support - * @param account The address of the contract to query for support of an interface - * @param interfaceId The interface identifier, as specified in ERC-165 - * @return true if the contract at account indicates support of the interface with - * identifier interfaceId, false otherwise - * @dev Assumes that account contains a contract that supports ERC165, otherwise - * the behavior of this method is undefined. This precondition can be checked - * with {supportsERC165}. - * Interface identification is specified in ERC-165. - */ - function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) { - bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId); - (bool success, bytes memory result) = account.staticcall{gas: 30000}(encodedParams); - if (result.length < 32) return false; - return success && abi.decode(result, (bool)); - } -} diff --git a/certora/munged/utils/introspection/ERC165Storage.sol b/certora/munged/utils/introspection/ERC165Storage.sol deleted file mode 100644 index 6151a11e6..000000000 --- a/certora/munged/utils/introspection/ERC165Storage.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/introspection/ERC165Storage.sol) - -pragma solidity ^0.8.0; - -import "./ERC165.sol"; - -/** - * @dev Storage based implementation of the {IERC165} interface. - * - * Contracts may inherit from this and call {_registerInterface} to declare - * their support of an interface. - */ -abstract contract ERC165Storage is ERC165 { - /** - * @dev Mapping of interface ids to whether or not it's supported. - */ - mapping(bytes4 => bool) private _supportedInterfaces; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return super.supportsInterface(interfaceId) || _supportedInterfaces[interfaceId]; - } - - /** - * @dev Registers the contract as an implementer of the interface defined by - * `interfaceId`. Support of the actual ERC165 interface is automatic and - * registering its interface id is not required. - * - * See {IERC165-supportsInterface}. - * - * Requirements: - * - * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). - */ - function _registerInterface(bytes4 interfaceId) internal virtual { - require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); - _supportedInterfaces[interfaceId] = true; - } -} diff --git a/certora/munged/utils/introspection/ERC1820Implementer.sol b/certora/munged/utils/introspection/ERC1820Implementer.sol deleted file mode 100644 index bf081f981..000000000 --- a/certora/munged/utils/introspection/ERC1820Implementer.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/introspection/ERC1820Implementer.sol) - -pragma solidity ^0.8.0; - -import "./IERC1820Implementer.sol"; - -/** - * @dev Implementation of the {IERC1820Implementer} interface. - * - * Contracts may inherit from this and call {_registerInterfaceForAddress} to - * declare their willingness to be implementers. - * {IERC1820Registry-setInterfaceImplementer} should then be called for the - * registration to be complete. - */ -contract ERC1820Implementer is IERC1820Implementer { - bytes32 private constant _ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC"); - - mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces; - - /** - * @dev See {IERC1820Implementer-canImplementInterfaceForAddress}. - */ - function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) - public - view - virtual - override - returns (bytes32) - { - return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00); - } - - /** - * @dev Declares the contract as willing to be an implementer of - * `interfaceHash` for `account`. - * - * See {IERC1820Registry-setInterfaceImplementer} and - * {IERC1820Registry-interfaceHash}. - */ - function _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal virtual { - _supportedInterfaces[interfaceHash][account] = true; - } -} diff --git a/certora/munged/utils/introspection/IERC165.sol b/certora/munged/utils/introspection/IERC165.sol deleted file mode 100644 index 71eb80151..000000000 --- a/certora/munged/utils/introspection/IERC165.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/introspection/IERC165.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} diff --git a/certora/munged/utils/introspection/IERC1820Implementer.sol b/certora/munged/utils/introspection/IERC1820Implementer.sol deleted file mode 100644 index 98ee372e4..000000000 --- a/certora/munged/utils/introspection/IERC1820Implementer.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/introspection/IERC1820Implementer.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface for an ERC1820 implementer, as defined in the - * https://eips.ethereum.org/EIPS/eip-1820#interface-implementation-erc1820implementerinterface[EIP]. - * Used by contracts that will be registered as implementers in the - * {IERC1820Registry}. - */ -interface IERC1820Implementer { - /** - * @dev Returns a special value (`ERC1820_ACCEPT_MAGIC`) if this contract - * implements `interfaceHash` for `account`. - * - * See {IERC1820Registry-setInterfaceImplementer}. - */ - function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) external view returns (bytes32); -} diff --git a/certora/munged/utils/introspection/IERC1820Registry.sol b/certora/munged/utils/introspection/IERC1820Registry.sol deleted file mode 100644 index eb5699b8e..000000000 --- a/certora/munged/utils/introspection/IERC1820Registry.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/introspection/IERC1820Registry.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the global ERC1820 Registry, as defined in the - * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register - * implementers for interfaces in this registry, as well as query support. - * - * Implementers may be shared by multiple accounts, and can also implement more - * than a single interface for each account. Contracts can implement interfaces - * for themselves, but externally-owned accounts (EOA) must delegate this to a - * contract. - * - * {IERC165} interfaces can also be queried via the registry. - * - * For an in-depth explanation and source code analysis, see the EIP text. - */ -interface IERC1820Registry { - /** - * @dev Sets `newManager` as the manager for `account`. A manager of an - * account is able to set interface implementers for it. - * - * By default, each account is its own manager. Passing a value of `0x0` in - * `newManager` will reset the manager to this initial state. - * - * Emits a {ManagerChanged} event. - * - * Requirements: - * - * - the caller must be the current manager for `account`. - */ - function setManager(address account, address newManager) external; - - /** - * @dev Returns the manager for `account`. - * - * See {setManager}. - */ - function getManager(address account) external view returns (address); - - /** - * @dev Sets the `implementer` contract as ``account``'s implementer for - * `interfaceHash`. - * - * `account` being the zero address is an alias for the caller's address. - * The zero address can also be used in `implementer` to remove an old one. - * - * See {interfaceHash} to learn how these are created. - * - * Emits an {InterfaceImplementerSet} event. - * - * Requirements: - * - * - the caller must be the current manager for `account`. - * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not - * end in 28 zeroes). - * - `implementer` must implement {IERC1820Implementer} and return true when - * queried for support, unless `implementer` is the caller. See - * {IERC1820Implementer-canImplementInterfaceForAddress}. - */ - function setInterfaceImplementer( - address account, - bytes32 _interfaceHash, - address implementer - ) external; - - /** - * @dev Returns the implementer of `interfaceHash` for `account`. If no such - * implementer is registered, returns the zero address. - * - * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28 - * zeroes), `account` will be queried for support of it. - * - * `account` being the zero address is an alias for the caller's address. - */ - function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address); - - /** - * @dev Returns the interface hash for an `interfaceName`, as defined in the - * corresponding - * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP]. - */ - function interfaceHash(string calldata interfaceName) external pure returns (bytes32); - - /** - * @notice Updates the cache with whether the contract implements an ERC165 interface or not. - * @param account Address of the contract for which to update the cache. - * @param interfaceId ERC165 interface for which to update the cache. - */ - function updateERC165Cache(address account, bytes4 interfaceId) external; - - /** - * @notice Checks whether a contract implements an ERC165 interface or not. - * If the result is not cached a direct lookup on the contract address is performed. - * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling - * {updateERC165Cache} with the contract address. - * @param account Address of the contract to check. - * @param interfaceId ERC165 interface to check. - * @return True if `account` implements `interfaceId`, false otherwise. - */ - function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); - - /** - * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. - * @param account Address of the contract to check. - * @param interfaceId ERC165 interface to check. - * @return True if `account` implements `interfaceId`, false otherwise. - */ - function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); - - event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); - - event ManagerChanged(address indexed account, address indexed newManager); -} diff --git a/certora/munged/utils/math/Math.sol b/certora/munged/utils/math/Math.sol deleted file mode 100644 index b31bca303..000000000 --- a/certora/munged/utils/math/Math.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/math/Math.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Standard math utilities missing in the Solidity language. - */ -library Math { - /** - * @dev Returns the largest of two numbers. - */ - function max(uint256 a, uint256 b) internal pure returns (uint256) { - return a >= b ? a : b; - } - - /** - * @dev Returns the smallest of two numbers. - */ - function min(uint256 a, uint256 b) internal pure returns (uint256) { - return a < b ? a : b; - } - - /** - * @dev Returns the average of two numbers. The result is rounded towards - * zero. - */ - function average(uint256 a, uint256 b) internal pure returns (uint256) { - // (a + b) / 2 can overflow. - return (a & b) + (a ^ b) / 2; - } - - /** - * @dev Returns the ceiling of the division of two numbers. - * - * This differs from standard division with `/` in that it rounds up instead - * of rounding down. - */ - function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { - // (a + b - 1) / b can overflow on addition, so we distribute. - return a / b + (a % b == 0 ? 0 : 1); - } -} diff --git a/certora/munged/utils/math/SafeCast.sol b/certora/munged/utils/math/SafeCast.sol deleted file mode 100644 index 19648dc91..000000000 --- a/certora/munged/utils/math/SafeCast.sol +++ /dev/null @@ -1,241 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/math/SafeCast.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow - * checks. - * - * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can - * easily result in undesired exploitation or bugs, since developers usually - * assume that overflows raise errors. `SafeCast` restores this intuition by - * reverting the transaction when such an operation overflows. - * - * Using this library instead of the unchecked operations eliminates an entire - * class of bugs, so it's recommended to use it always. - * - * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing - * all math on `uint256` and `int256` and then downcasting. - */ -library SafeCast { - /** - * @dev Returns the downcasted uint224 from uint256, reverting on - * overflow (when the input is greater than largest uint224). - * - * Counterpart to Solidity's `uint224` operator. - * - * Requirements: - * - * - input must fit into 224 bits - */ - function toUint224(uint256 value) internal pure returns (uint224) { - require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); - return uint224(value); - } - - /** - * @dev Returns the downcasted uint128 from uint256, reverting on - * overflow (when the input is greater than largest uint128). - * - * Counterpart to Solidity's `uint128` operator. - * - * Requirements: - * - * - input must fit into 128 bits - */ - function toUint128(uint256 value) internal pure returns (uint128) { - require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); - return uint128(value); - } - - /** - * @dev Returns the downcasted uint96 from uint256, reverting on - * overflow (when the input is greater than largest uint96). - * - * Counterpart to Solidity's `uint96` operator. - * - * Requirements: - * - * - input must fit into 96 bits - */ - function toUint96(uint256 value) internal pure returns (uint96) { - require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); - return uint96(value); - } - - /** - * @dev Returns the downcasted uint64 from uint256, reverting on - * overflow (when the input is greater than largest uint64). - * - * Counterpart to Solidity's `uint64` operator. - * - * Requirements: - * - * - input must fit into 64 bits - */ - function toUint64(uint256 value) internal pure returns (uint64) { - require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); - return uint64(value); - } - - /** - * @dev Returns the downcasted uint32 from uint256, reverting on - * overflow (when the input is greater than largest uint32). - * - * Counterpart to Solidity's `uint32` operator. - * - * Requirements: - * - * - input must fit into 32 bits - */ - function toUint32(uint256 value) internal pure returns (uint32) { - require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); - return uint32(value); - } - - /** - * @dev Returns the downcasted uint16 from uint256, reverting on - * overflow (when the input is greater than largest uint16). - * - * Counterpart to Solidity's `uint16` operator. - * - * Requirements: - * - * - input must fit into 16 bits - */ - function toUint16(uint256 value) internal pure returns (uint16) { - require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); - return uint16(value); - } - - /** - * @dev Returns the downcasted uint8 from uint256, reverting on - * overflow (when the input is greater than largest uint8). - * - * Counterpart to Solidity's `uint8` operator. - * - * Requirements: - * - * - input must fit into 8 bits. - */ - function toUint8(uint256 value) internal pure returns (uint8) { - require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); - return uint8(value); - } - - /** - * @dev Converts a signed int256 into an unsigned uint256. - * - * Requirements: - * - * - input must be greater than or equal to 0. - */ - function toUint256(int256 value) internal pure returns (uint256) { - require(value >= 0, "SafeCast: value must be positive"); - return uint256(value); - } - - /** - * @dev Returns the downcasted int128 from int256, reverting on - * overflow (when the input is less than smallest int128 or - * greater than largest int128). - * - * Counterpart to Solidity's `int128` operator. - * - * Requirements: - * - * - input must fit into 128 bits - * - * _Available since v3.1._ - */ - function toInt128(int256 value) internal pure returns (int128) { - require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits"); - return int128(value); - } - - /** - * @dev Returns the downcasted int64 from int256, reverting on - * overflow (when the input is less than smallest int64 or - * greater than largest int64). - * - * Counterpart to Solidity's `int64` operator. - * - * Requirements: - * - * - input must fit into 64 bits - * - * _Available since v3.1._ - */ - function toInt64(int256 value) internal pure returns (int64) { - require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits"); - return int64(value); - } - - /** - * @dev Returns the downcasted int32 from int256, reverting on - * overflow (when the input is less than smallest int32 or - * greater than largest int32). - * - * Counterpart to Solidity's `int32` operator. - * - * Requirements: - * - * - input must fit into 32 bits - * - * _Available since v3.1._ - */ - function toInt32(int256 value) internal pure returns (int32) { - require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits"); - return int32(value); - } - - /** - * @dev Returns the downcasted int16 from int256, reverting on - * overflow (when the input is less than smallest int16 or - * greater than largest int16). - * - * Counterpart to Solidity's `int16` operator. - * - * Requirements: - * - * - input must fit into 16 bits - * - * _Available since v3.1._ - */ - function toInt16(int256 value) internal pure returns (int16) { - require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits"); - return int16(value); - } - - /** - * @dev Returns the downcasted int8 from int256, reverting on - * overflow (when the input is less than smallest int8 or - * greater than largest int8). - * - * Counterpart to Solidity's `int8` operator. - * - * Requirements: - * - * - input must fit into 8 bits. - * - * _Available since v3.1._ - */ - function toInt8(int256 value) internal pure returns (int8) { - require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits"); - return int8(value); - } - - /** - * @dev Converts an unsigned uint256 into a signed int256. - * - * Requirements: - * - * - input must be less than or equal to maxInt256. - */ - function toInt256(uint256 value) internal pure returns (int256) { - // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive - require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); - return int256(value); - } -} diff --git a/certora/munged/utils/math/SafeMath.sol b/certora/munged/utils/math/SafeMath.sol deleted file mode 100644 index 275331061..000000000 --- a/certora/munged/utils/math/SafeMath.sol +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/math/SafeMath.sol) - -pragma solidity ^0.8.0; - -// CAUTION -// This version of SafeMath should only be used with Solidity 0.8 or later, -// because it relies on the compiler's built in overflow checks. - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler - * now has built in overflow checking. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - uint256 c = a + b; - if (c < a) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the substraction of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b > a) return (false, 0); - return (true, a - b); - } - } - - /** - * @dev Returns the multiplication of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) return (true, 0); - uint256 c = a * b; - if (c / a != b) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the division of two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a / b); - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a % b); - } - } - - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return a - b; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - return a * b; - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return a / b; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return a % b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {trySub}. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b <= a, errorMessage); - return a - b; - } - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a / b; - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting with custom message when dividing by zero. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryMod}. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a % b; - } - } -} diff --git a/certora/munged/utils/math/SignedSafeMath.sol b/certora/munged/utils/math/SignedSafeMath.sol deleted file mode 100644 index f9230da9b..000000000 --- a/certora/munged/utils/math/SignedSafeMath.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/math/SignedSafeMath.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler - * now has built in overflow checking. - */ -library SignedSafeMath { - /** - * @dev Returns the multiplication of two signed integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(int256 a, int256 b) internal pure returns (int256) { - return a * b; - } - - /** - * @dev Returns the integer division of two signed integers. Reverts on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(int256 a, int256 b) internal pure returns (int256) { - return a / b; - } - - /** - * @dev Returns the subtraction of two signed integers, reverting on - * overflow. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(int256 a, int256 b) internal pure returns (int256) { - return a - b; - } - - /** - * @dev Returns the addition of two signed integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(int256 a, int256 b) internal pure returns (int256) { - return a + b; - } -} diff --git a/certora/munged/utils/structs/BitMaps.sol b/certora/munged/utils/structs/BitMaps.sol deleted file mode 100644 index 966691e3a..000000000 --- a/certora/munged/utils/structs/BitMaps.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/structs/BitMaps.sol) -pragma solidity ^0.8.0; - -/** - * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential. - * Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor]. - */ -library BitMaps { - struct BitMap { - mapping(uint256 => uint256) _data; - } - - /** - * @dev Returns whether the bit at `index` is set. - */ - function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - return bitmap._data[bucket] & mask != 0; - } - - /** - * @dev Sets the bit at `index` to the boolean `value`. - */ - function setTo( - BitMap storage bitmap, - uint256 index, - bool value - ) internal { - if (value) { - set(bitmap, index); - } else { - unset(bitmap, index); - } - } - - /** - * @dev Sets the bit at `index`. - */ - function set(BitMap storage bitmap, uint256 index) internal { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - bitmap._data[bucket] |= mask; - } - - /** - * @dev Unsets the bit at `index`. - */ - function unset(BitMap storage bitmap, uint256 index) internal { - uint256 bucket = index >> 8; - uint256 mask = 1 << (index & 0xff); - bitmap._data[bucket] &= ~mask; - } -} diff --git a/certora/munged/utils/structs/EnumerableMap.sol b/certora/munged/utils/structs/EnumerableMap.sol deleted file mode 100644 index 83a7f17ce..000000000 --- a/certora/munged/utils/structs/EnumerableMap.sol +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/structs/EnumerableMap.sol) - -pragma solidity ^0.8.0; - -import "./EnumerableSet.sol"; - -/** - * @dev Library for managing an enumerable variant of Solidity's - * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] - * type. - * - * Maps have the following properties: - * - * - Entries are added, removed, and checked for existence in constant time - * (O(1)). - * - Entries are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableMap for EnumerableMap.UintToAddressMap; - * - * // Declare a set state variable - * EnumerableMap.UintToAddressMap private myMap; - * } - * ``` - * - * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are - * supported. - */ -library EnumerableMap { - using EnumerableSet for EnumerableSet.Bytes32Set; - - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Map type with - // bytes32 keys and values. - // The Map implementation uses private functions, and user-facing - // implementations (such as Uint256ToAddressMap) are just wrappers around - // the underlying Map. - // This means that we can only create new EnumerableMaps for types that fit - // in bytes32. - - struct Map { - // Storage of keys - EnumerableSet.Bytes32Set _keys; - mapping(bytes32 => bytes32) _values; - } - - /** - * @dev Adds a key-value pair to a map, or updates the value for an existing - * key. O(1). - * - * Returns true if the key was added to the map, that is if it was not - * already present. - */ - function _set( - Map storage map, - bytes32 key, - bytes32 value - ) private returns (bool) { - map._values[key] = value; - return map._keys.add(key); - } - - /** - * @dev Removes a key-value pair from a map. O(1). - * - * Returns true if the key was removed from the map, that is if it was present. - */ - function _remove(Map storage map, bytes32 key) private returns (bool) { - delete map._values[key]; - return map._keys.remove(key); - } - - /** - * @dev Returns true if the key is in the map. O(1). - */ - function _contains(Map storage map, bytes32 key) private view returns (bool) { - return map._keys.contains(key); - } - - /** - * @dev Returns the number of key-value pairs in the map. O(1). - */ - function _length(Map storage map) private view returns (uint256) { - return map._keys.length(); - } - - /** - * @dev Returns the key-value pair stored at position `index` in the map. O(1). - * - * Note that there are no guarantees on the ordering of entries inside the - * array, and it may change when more entries are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) { - bytes32 key = map._keys.at(index); - return (key, map._values[key]); - } - - /** - * @dev Tries to returns the value associated with `key`. O(1). - * Does not revert if `key` is not in the map. - */ - function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) { - bytes32 value = map._values[key]; - if (value == bytes32(0)) { - return (_contains(map, key), bytes32(0)); - } else { - return (true, value); - } - } - - /** - * @dev Returns the value associated with `key`. O(1). - * - * Requirements: - * - * - `key` must be in the map. - */ - function _get(Map storage map, bytes32 key) private view returns (bytes32) { - bytes32 value = map._values[key]; - require(value != 0 || _contains(map, key), "EnumerableMap: nonexistent key"); - return value; - } - - /** - * @dev Same as {_get}, with a custom error message when `key` is not in the map. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {_tryGet}. - */ - function _get( - Map storage map, - bytes32 key, - string memory errorMessage - ) private view returns (bytes32) { - bytes32 value = map._values[key]; - require(value != 0 || _contains(map, key), errorMessage); - return value; - } - - // UintToAddressMap - - struct UintToAddressMap { - Map _inner; - } - - /** - * @dev Adds a key-value pair to a map, or updates the value for an existing - * key. O(1). - * - * Returns true if the key was added to the map, that is if it was not - * already present. - */ - function set( - UintToAddressMap storage map, - uint256 key, - address value - ) internal returns (bool) { - return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the key was removed from the map, that is if it was present. - */ - function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { - return _remove(map._inner, bytes32(key)); - } - - /** - * @dev Returns true if the key is in the map. O(1). - */ - function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { - return _contains(map._inner, bytes32(key)); - } - - /** - * @dev Returns the number of elements in the map. O(1). - */ - function length(UintToAddressMap storage map) internal view returns (uint256) { - return _length(map._inner); - } - - /** - * @dev Returns the element stored at position `index` in the set. O(1). - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) { - (bytes32 key, bytes32 value) = _at(map._inner, index); - return (uint256(key), address(uint160(uint256(value)))); - } - - /** - * @dev Tries to returns the value associated with `key`. O(1). - * Does not revert if `key` is not in the map. - * - * _Available since v3.4._ - */ - function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) { - (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key)); - return (success, address(uint160(uint256(value)))); - } - - /** - * @dev Returns the value associated with `key`. O(1). - * - * Requirements: - * - * - `key` must be in the map. - */ - function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { - return address(uint160(uint256(_get(map._inner, bytes32(key))))); - } - - /** - * @dev Same as {get}, with a custom error message when `key` is not in the map. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryGet}. - */ - function get( - UintToAddressMap storage map, - uint256 key, - string memory errorMessage - ) internal view returns (address) { - return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage)))); - } -} diff --git a/certora/munged/utils/structs/EnumerableSet.sol b/certora/munged/utils/structs/EnumerableSet.sol deleted file mode 100644 index 2945ecca9..000000000 --- a/certora/munged/utils/structs/EnumerableSet.sol +++ /dev/null @@ -1,357 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.3.2 (utils/structs/EnumerableSet.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastvalue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastvalue; - // Update the index for the moved value - set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - return _values(set._inner); - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - assembly { - result := store - } - - return result; - } -} From 380b87dc0cbe3d9c3b8abc6a385f5958db67b0ff Mon Sep 17 00:00:00 2001 From: Michael George Date: Wed, 1 Dec 2021 10:13:42 -0500 Subject: [PATCH 125/254] switched harnesses to use munged contracts --- certora/harnesses/ERC20VotesHarness.sol | 2 +- certora/harnesses/WizardControlFirstPriority.sol | 12 ++++++------ certora/harnesses/WizardFirstTry.sol | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/certora/harnesses/ERC20VotesHarness.sol b/certora/harnesses/ERC20VotesHarness.sol index 818a2ab0f..5067ecfba 100644 --- a/certora/harnesses/ERC20VotesHarness.sol +++ b/certora/harnesses/ERC20VotesHarness.sol @@ -1,4 +1,4 @@ -import "../../contracts/token/ERC20/extensions/ERC20Votes.sol"; +import "../munged/token/ERC20/extensions/ERC20Votes.sol"; contract ERC20VotesHarness is ERC20Votes { constructor(string memory name, string memory symbol) ERC20Permit(name) ERC20(name, symbol) {} diff --git a/certora/harnesses/WizardControlFirstPriority.sol b/certora/harnesses/WizardControlFirstPriority.sol index 5e2eff17f..5ae7fe066 100644 --- a/certora/harnesses/WizardControlFirstPriority.sol +++ b/certora/harnesses/WizardControlFirstPriority.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.2; -import "../../contracts/governance/Governor.sol"; -import "../../contracts/governance/extensions/GovernorCountingSimple.sol"; -import "../../contracts/governance/extensions/GovernorVotes.sol"; -import "../../contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; -import "../../contracts/governance/extensions/GovernorTimelockControl.sol"; -import "../../contracts/governance/extensions/GovernorProposalThreshold.sol"; +import "../munged/governance/Governor.sol"; +import "../munged/governance/extensions/GovernorCountingSimple.sol"; +import "../munged/governance/extensions/GovernorVotes.sol"; +import "../munged/governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../munged/governance/extensions/GovernorTimelockControl.sol"; +import "../munged/governance/extensions/GovernorProposalThreshold.sol"; /* Wizard options: diff --git a/certora/harnesses/WizardFirstTry.sol b/certora/harnesses/WizardFirstTry.sol index 026e3d041..83fece04f 100644 --- a/certora/harnesses/WizardFirstTry.sol +++ b/certora/harnesses/WizardFirstTry.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.2; -import "../../contracts/governance/Governor.sol"; -import "../../contracts/governance/extensions/GovernorCountingSimple.sol"; -import "../../contracts/governance/extensions/GovernorVotes.sol"; -import "../../contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; -import "../../contracts/governance/extensions/GovernorTimelockCompound.sol"; +import "../munged/governance/Governor.sol"; +import "../munged/governance/extensions/GovernorCountingSimple.sol"; +import "../munged/governance/extensions/GovernorVotes.sol"; +import "../munged/governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../munged/governance/extensions/GovernorTimelockCompound.sol"; /* Wizard options: From 760edf9b87bbbf79000eb1883ae4dc76384d6144 Mon Sep 17 00:00:00 2001 From: Michael George Date: Thu, 2 Dec 2021 14:01:44 -0500 Subject: [PATCH 126/254] tweaked script to run quickly --- certora/scripts/verifyAll.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/certora/scripts/verifyAll.sh b/certora/scripts/verifyAll.sh index 6a7a42fa9..f55ab8abf 100644 --- a/certora/scripts/verifyAll.sh +++ b/certora/scripts/verifyAll.sh @@ -1,3 +1,5 @@ +#!/bin/bash + for contract in certora/harnesses/Wizard*.sol; do for spec in certora/specs/*.spec; @@ -17,6 +19,7 @@ do --disableLocalTypeChecking \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ + --send_only \ --msg "checking $specFile on ${contractFile%.*}" else certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ @@ -26,8 +29,9 @@ do --disableLocalTypeChecking \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ + --send_only \ --msg "checking $specFile on ${contractFile%.*}" fi fi done -done \ No newline at end of file +done From ec5d501791f3ac6794ed11a97306ce9afff06c6f Mon Sep 17 00:00:00 2001 From: Michael George Date: Thu, 2 Dec 2021 15:06:29 -0500 Subject: [PATCH 127/254] filtered out timeouts --- certora/specs/GovernorBase.spec | 22 +++++++++++++---- certora/specs/GovernorCountingSimple.spec | 30 ----------------------- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 8487804ab..7cec399c2 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -276,8 +276,13 @@ rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ // non of the proposal specific functions can make changes again. In executedOnlyAfterExecuteFunc // we connected the executed attribute to the execute() function, showing that only execute() can // change it, and that it will always change it. -rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selector != updateQuorumNumerator(uint256).selector && - !f.isFallback && f.selector != updateTimelock(address).selector} { +rule allFunctionsRevertIfExecuted(method f) filtered { f -> + !f.isView && !f.isFallback + && f.selector != updateTimelock(address).selector + && f.selector != updateQuorumNumerator(uint256).selector + && f.selector != queue(address[],uint256[],bytes[],bytes32).selector + && f.selector != __acceptAdmin().selector +} { env e; calldataarg args; uint256 pId; require(isExecuted(pId)); @@ -292,8 +297,13 @@ rule allFunctionsRevertIfExecuted(method f) filtered { f -> !f.isView && f.selec /* * All proposal specific (non-view) functions should revert if proposal is canceled */ -rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selector != updateQuorumNumerator(uint256).selector && - !f.isFallback && f.selector != updateTimelock(address).selector} { +rule allFunctionsRevertIfCanceled(method f) filtered { + f -> !f.isView && !f.isFallback + && f.selector != updateTimelock(address).selector + && f.selector != updateQuorumNumerator(uint256).selector + && f.selector != queue(address[],uint256[],bytes[],bytes32).selector + && f.selector != __acceptAdmin().selector +} { env e; calldataarg args; uint256 pId; require(isCanceled(pId)); @@ -308,7 +318,9 @@ rule allFunctionsRevertIfCanceled(method f) filtered { f -> !f.isView && f.selec /* * Proposal can be switched to executed only via execute() function */ -rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) { +rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) filtered { + f -> f.selector != queue(address[],uint256[],bytes[],bytes32).selector +} { env e; calldataarg args; uint256 pId; bool executedBefore = isExecuted(pId); diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 47f67aa90..6d2d5fbb7 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -125,36 +125,6 @@ invariant OneIsNotMoreThanAll(uint256 pId) ////////////////////////////////////////////////////////////////////////////// -//NOT FINISHED -/* -* the sum of voting power of those who voted is less or equal to the maximum possible votes, per each proposal -*/ -rule possibleTotalVotes(uint256 pId, uint8 sup, env e, method f) { - - // add requireinvariant for all i, j. i = i - 1 && i < j => checkpointlookup[i] < checkpointlookup[j]; - require tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)); - - uint256 againstB; - uint256 forB; - uint256 absatinB; - againstB, forB, absatinB = proposalVotes(pId); - - calldataarg args; - //f(e, args); - - castVote(e, pId, sup); - - uint256 against; - uint256 for; - uint256 absatin; - against, for, absatin = proposalVotes(pId); - - uint256 ps = proposalSnapshot(pId); - - assert tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)), "bla bla bla"; -} - - /* * Only sender's voting status can be changed by execution of any cast vote function */ From 5888bee85324562e6337f1f128ed85c1e084a7ab Mon Sep 17 00:00:00 2001 From: Michael George Date: Thu, 2 Dec 2021 15:16:26 -0500 Subject: [PATCH 128/254] fixed executeOnly rule --- certora/specs/GovernorBase.spec | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 7cec399c2..a7279b1ef 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -318,17 +318,15 @@ rule allFunctionsRevertIfCanceled(method f) filtered { /* * Proposal can be switched to executed only via execute() function */ -rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) filtered { - f -> f.selector != queue(address[],uint256[],bytes[],bytes32).selector -} { +rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) { env e; calldataarg args; uint256 pId; bool executedBefore = isExecuted(pId); require(!executedBefore); helperFunctionsWithRevert(pId, f, e); - require(!lastReverted); bool executedAfter = isExecuted(pId); - assert(executedAfter != executedBefore, "executed property did not change"); + assert(executedAfter != executedBefore => f.selector == execute(address[], uint256[], bytes[], bytes32).selector, "isExecuted only changes in the execute method"); } + From d64869545ddde84f47344aa0efd02607a32672d3 Mon Sep 17 00:00:00 2001 From: Michael George Date: Thu, 2 Dec 2021 16:47:18 -0500 Subject: [PATCH 129/254] did some harnessing --- certora/scripts/WizardFirstTry.sh | 4 ++-- certora/specs/GovernorBase.spec | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/certora/scripts/WizardFirstTry.sh b/certora/scripts/WizardFirstTry.sh index 8aa46e8cb..361814d8b 100644 --- a/certora/scripts/WizardFirstTry.sh +++ b/certora/scripts/WizardFirstTry.sh @@ -3,6 +3,6 @@ certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardFirst --solc solc8.2 \ --staging shelly/forSasha \ --optimistic_loop \ + --disableLocalTypeChecking \ --settings -copyLoopUnroll=4 \ - --rule allFunctionsRevertIfCanceled \ - --msg "$1" \ No newline at end of file + --msg "$1" diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index a7279b1ef..798d63381 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -25,8 +25,8 @@ methods { getVotes(address, uint256) returns uint256 => DISPATCHER(true) - erc20votes.getPastTotalSupply(uint256) returns uint256 - erc20votes.getPastVotes(address, uint256) returns uint256 + getPastTotalSupply(uint256) returns uint256 => NONDET + getPastVotes(address, uint256) returns uint256 => NONDET //scheduleBatch(address[],uint256[],bytes[],bytes32,bytes32,uint256) => DISPATCHER(true) //executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) @@ -281,7 +281,7 @@ rule allFunctionsRevertIfExecuted(method f) filtered { f -> && f.selector != updateTimelock(address).selector && f.selector != updateQuorumNumerator(uint256).selector && f.selector != queue(address[],uint256[],bytes[],bytes32).selector - && f.selector != __acceptAdmin().selector + && f.selector != 0xb9a61961 // __acceptAdmin() } { env e; calldataarg args; uint256 pId; @@ -302,7 +302,7 @@ rule allFunctionsRevertIfCanceled(method f) filtered { && f.selector != updateTimelock(address).selector && f.selector != updateQuorumNumerator(uint256).selector && f.selector != queue(address[],uint256[],bytes[],bytes32).selector - && f.selector != __acceptAdmin().selector + && f.selector != 0xb9a61961 // __acceptAdmin() } { env e; calldataarg args; uint256 pId; From 3c150953ed388c492202e7738bcbfe9521412d2f Mon Sep 17 00:00:00 2001 From: Michael George Date: Fri, 3 Dec 2021 14:52:53 -0500 Subject: [PATCH 130/254] harnessing fix --- certora/specs/GovernorBase.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 798d63381..6fb527eee 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -25,8 +25,8 @@ methods { getVotes(address, uint256) returns uint256 => DISPATCHER(true) - getPastTotalSupply(uint256) returns uint256 => NONDET - getPastVotes(address, uint256) returns uint256 => NONDET + getPastTotalSupply(uint256 t) returns uint256 => PER_CALLEE_CONSTANT + getPastVotes(address a, uint256 t) returns uint256 => PER_CALLEE_CONSTANT //scheduleBatch(address[],uint256[],bytes[],bytes32,bytes32,uint256) => DISPATCHER(true) //executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) From 22de642692c80f0f22fdb640e354338e39ca043d Mon Sep 17 00:00:00 2001 From: Michael George Date: Thu, 9 Dec 2021 14:43:40 -0500 Subject: [PATCH 131/254] simplified README somewhat, included additional information about munging --- certora/README.md | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/certora/README.md b/certora/README.md index 16fcd69bf..55f84d42f 100644 --- a/certora/README.md +++ b/certora/README.md @@ -8,35 +8,32 @@ Documentation for CVT and the specification language are available ## Running the verification The scripts in the `certora/scripts` directory are used to submit verification -jobs to the Certora verification service. These scripts should be run from the -root directory; for example by running - -``` -sh certora/scripts/WizardFirstPriority.sh -``` - -After the job is complete, the results will be available on +jobs to the Certora verification service. After the job is complete, the results will be available on [the Certora portal](https://vaas-stg.certora.com/). -The `verifyAll` script runs all spec files agains all contracts in the `certora/harness` that start with `Wizard` meaning that a contract generated in [wizard](https://wizard.openzeppelin.com/#governor). If you want to verify new wizard's instance you also need to give it a name starting with "Wizard..." or "WizardControl..." if you use `TimelockController` and harness this contract. We don't recommend to do it because a list of harnesses may go beyond wizard's contract. Moreover, the set of setting to run the Certora verification service may vary. The main goal of this script is to run all specs written by the team against all contracts properly harnessed. - -The `WizardFirstPriority` and `WizardFirstTry` scripts run one of the scripts for the corresponding contract. In order to run another spec you should change spec file name `` in the script (flag `--verify`): +These scripts should be run from the root directory; for example by running ``` ---verify WizardFirstPriority:certora/specs/.spec \ +sh certora/scripts/verifyAll.sh ``` -for example: +The most important of these is `verifyAll.sh`, which checks +all of the harnessed contracts (`certora/harness/Wizard*.sol`) against all of +the specifications (`certora/spec/*.spec`). + +The other scripts run a subset of the specifications or the contracts. You can +verify different contracts or specifications by changing the `--verify` option, +and you can run a single rule or method with the `--rule` or `--method` option. + +For example, to verify the `WizardFirstPriority` contract against the +`GovernorCountingSimple` specification, you could change the `--verify` line of +the `WizardControlFirstPriortity.sh` script to: ``` --verify WizardFirstPriority:certora/specs/GovernorCountingSimple.spec \ ``` - -MENTION TIMEOUTS ISSUES - - -## Adapting to changes +## Adapting to changes in the contracts Some of our rules require the code to be simplified in various ways. Our primary tool for performing these simplifications is to run verification on a @@ -46,3 +43,14 @@ These "harness" contracts can be found in the `certora/harness` directory. This pattern does require some modifications to the original code: some methods need to be made virtual or public, for example. These changes are handled by applying a patch to the code before verification. + +When one of the `verify` scripts is executed, it first applies the patch file +`certora/applyHarness.patch` to the `contracts` directory, placing the output +in the `certora/munged` directory. We then verify the contracts in the +`certora/munged` directory. + +If the original contracts change, it is possible to create a conflict with the +patch. In this case, the verify scripts will report an error message and output +rejected changes in the `munged` directory. After merging the changes, run +`make record` in the `certora` directory; this will regenerate the patch file, +which can then be checked into git. From d95c3eeee1b332682379e63a7771391c46bb261e Mon Sep 17 00:00:00 2001 From: Michael George Date: Thu, 9 Dec 2021 14:47:57 -0500 Subject: [PATCH 132/254] removed some spurious comments --- certora/applyHarness.patch | 48 +++++++++++--------------------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 317f22e73..42b10fab5 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,12 +1,12 @@ diff -ruN .gitignore .gitignore --- .gitignore 1969-12-31 19:00:00.000000000 -0500 -+++ .gitignore 2021-12-01 10:05:19.757088138 -0500 ++++ .gitignore 2021-12-09 14:46:33.923637220 -0500 @@ -0,0 +1,2 @@ +* +!.gitignore diff -ruN governance/compatibility/GovernorCompatibilityBravo.sol governance/compatibility/GovernorCompatibilityBravo.sol ---- governance/compatibility/GovernorCompatibilityBravo.sol 2021-12-01 10:02:39.909936316 -0500 -+++ governance/compatibility/GovernorCompatibilityBravo.sol 2021-12-01 10:00:48.002627620 -0500 +--- governance/compatibility/GovernorCompatibilityBravo.sol 2021-12-03 15:24:56.523654357 -0500 ++++ governance/compatibility/GovernorCompatibilityBravo.sol 2021-12-09 14:46:33.923637220 -0500 @@ -245,7 +245,7 @@ /** * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. @@ -14,7 +14,7 @@ diff -ruN governance/compatibility/GovernorCompatibilityBravo.sol governance/com - function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { + function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal ProposalDetails storage details = _proposalDetails[proposalId]; - return quorum(proposalSnapshot(proposalId)) < details.forVotes; + return quorum(proposalSnapshot(proposalId)) <= details.forVotes; } @@ -253,7 +253,7 @@ /** @@ -26,8 +26,8 @@ diff -ruN governance/compatibility/GovernorCompatibilityBravo.sol governance/com return details.forVotes > details.againstVotes; } diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol ---- governance/extensions/GovernorCountingSimple.sol 2021-12-01 10:02:39.909936316 -0500 -+++ governance/extensions/GovernorCountingSimple.sol 2021-12-01 10:00:48.002627620 -0500 +--- governance/extensions/GovernorCountingSimple.sol 2021-12-03 15:24:56.523654357 -0500 ++++ governance/extensions/GovernorCountingSimple.sol 2021-12-09 14:46:33.923637220 -0500 @@ -64,7 +64,7 @@ /** * @dev See {Governor-_quorumReached}. @@ -47,9 +47,9 @@ diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions return proposalvote.forVotes > proposalvote.againstVotes; diff -ruN governance/extensions/GovernorTimelockControl.sol governance/extensions/GovernorTimelockControl.sol ---- governance/extensions/GovernorTimelockControl.sol 2021-12-01 10:02:39.909936316 -0500 -+++ governance/extensions/GovernorTimelockControl.sol 2021-12-01 10:00:48.002627620 -0500 -@@ -109,7 +109,7 @@ +--- governance/extensions/GovernorTimelockControl.sol 2021-12-03 15:24:56.523654357 -0500 ++++ governance/extensions/GovernorTimelockControl.sol 2021-12-09 14:46:33.923637220 -0500 +@@ -111,7 +111,7 @@ bytes[] memory calldatas, bytes32 descriptionHash ) internal virtual override { @@ -59,8 +59,8 @@ diff -ruN governance/extensions/GovernorTimelockControl.sol governance/extension /** diff -ruN governance/Governor.sol governance/Governor.sol ---- governance/Governor.sol 2021-12-01 10:02:39.909936316 -0500 -+++ governance/Governor.sol 2021-12-01 10:00:48.002627620 -0500 +--- governance/Governor.sol 2021-12-03 15:24:56.523654357 -0500 ++++ governance/Governor.sol 2021-12-09 14:46:56.411503587 -0500 @@ -38,8 +38,8 @@ string private _name; @@ -72,7 +72,7 @@ diff -ruN governance/Governor.sol governance/Governor.sol /** * @dev Restrict access to governor executing address. Some module might override the _executor function to make * sure this modifier is consistant with the execution model. -@@ -154,12 +154,12 @@ +@@ -167,12 +167,12 @@ /** * @dev Amount of votes already cast passes the threshold limit. */ @@ -87,29 +87,9 @@ diff -ruN governance/Governor.sol governance/Governor.sol /** * @dev Register a vote with a given support and voting weight. -@@ -320,7 +320,7 @@ - v, - r, - s -- ); -+ ); // mention that we assume that hashing works correctly - return _castVote(proposalId, voter, support, ""); - } - -diff -ruN governance/TimelockController.sol governance/TimelockController.sol ---- governance/TimelockController.sol 2021-12-01 10:02:39.909936316 -0500 -+++ governance/TimelockController.sol 2021-12-01 10:00:48.002627620 -0500 -@@ -299,6 +299,7 @@ - _call(id, i, targets[i], values[i], datas[i]); - } - _afterCall(id); -+ // ASSUME THAT THERE IS NO REENTRANCY IN WIZARDHARNESS1 - } - - /** diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol ---- token/ERC20/extensions/ERC20Votes.sol 2021-12-01 10:02:39.909936316 -0500 -+++ token/ERC20/extensions/ERC20Votes.sol 2021-12-01 10:00:48.018627515 -0500 +--- token/ERC20/extensions/ERC20Votes.sol 2021-12-03 15:24:56.527654330 -0500 ++++ token/ERC20/extensions/ERC20Votes.sol 2021-12-09 14:46:33.927637196 -0500 @@ -84,7 +84,7 @@ * * - `blockNumber` must have been already mined From 2a75aa19bdc79ab67921ba63984f2840f761ba7a Mon Sep 17 00:00:00 2001 From: Michael George Date: Thu, 9 Dec 2021 17:17:49 -0500 Subject: [PATCH 133/254] added munging to scripts --- certora/scripts/Governor.sh | 2 ++ certora/scripts/GovernorCountingSimple-counting.sh | 4 +++- certora/scripts/WizardControlFirstPriority.sh | 4 +++- certora/scripts/WizardFirstTry.sh | 2 ++ certora/scripts/sanity.sh | 4 +++- certora/scripts/verifyAll.sh | 2 ++ 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh index 7b44e364a..53ade5060 100755 --- a/certora/scripts/Governor.sh +++ b/certora/scripts/Governor.sh @@ -1,3 +1,5 @@ +make -C certora munged + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorHarness.sol \ --verify GovernorHarness:certora/specs/GovernorBase.spec \ --solc solc8.0 \ diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/GovernorCountingSimple-counting.sh index 82b2c263e..9ed8fe34c 100644 --- a/certora/scripts/GovernorCountingSimple-counting.sh +++ b/certora/scripts/GovernorCountingSimple-counting.sh @@ -1,3 +1,5 @@ +make -C certora munged + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorBasicHarness.sol \ --verify GovernorBasicHarness:certora/specs/GovernorCountingSimple.spec \ --solc solc8.2 \ @@ -5,4 +7,4 @@ certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorBa --optimistic_loop \ --settings -copyLoopUnroll=4 \ --rule hasVotedCorrelation \ - --msg "$1" \ No newline at end of file + --msg "$1" diff --git a/certora/scripts/WizardControlFirstPriority.sh b/certora/scripts/WizardControlFirstPriority.sh index 3f367de2c..b815986ee 100644 --- a/certora/scripts/WizardControlFirstPriority.sh +++ b/certora/scripts/WizardControlFirstPriority.sh @@ -1,3 +1,5 @@ +make -C certora munged + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardControlFirstPriority.sol \ --link WizardControlFirstPriority:token=ERC20VotesHarness \ --verify WizardControlFirstPriority:certora/specs/GovernorBase.spec \ @@ -7,4 +9,4 @@ certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardContr --optimistic_loop \ --settings -copyLoopUnroll=4 \ --rule canVoteDuringVotingPeriod \ - --msg "$1" \ No newline at end of file + --msg "$1" diff --git a/certora/scripts/WizardFirstTry.sh b/certora/scripts/WizardFirstTry.sh index 361814d8b..fd5a32ab4 100644 --- a/certora/scripts/WizardFirstTry.sh +++ b/certora/scripts/WizardFirstTry.sh @@ -1,3 +1,5 @@ +make -C certora munged + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardFirstTry.sol \ --verify WizardFirstTry:certora/specs/GovernorBase.spec \ --solc solc8.2 \ diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index 58b27cc7d..b6cdb4ec3 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -1,3 +1,5 @@ +make -C certora munged + for f in certora/harnesses/Wizard*.sol do echo "Processing $f" @@ -9,4 +11,4 @@ do --optimistic_loop \ --msg "checking sanity on ${file%.*}" --settings -copyLoopUnroll=4 -done \ No newline at end of file +done diff --git a/certora/scripts/verifyAll.sh b/certora/scripts/verifyAll.sh index f55ab8abf..90d76912c 100644 --- a/certora/scripts/verifyAll.sh +++ b/certora/scripts/verifyAll.sh @@ -1,5 +1,7 @@ #!/bin/bash +make -C certora munged + for contract in certora/harnesses/Wizard*.sol; do for spec in certora/specs/*.spec; From 7912b1af7d99efd93b54f97756db7564c5f8f058 Mon Sep 17 00:00:00 2001 From: Michael George Date: Thu, 16 Dec 2021 14:58:55 -0500 Subject: [PATCH 134/254] filtered out relay, since it is havocing --- certora/specs/GovernorBase.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 6fb527eee..031b2680e 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -281,6 +281,7 @@ rule allFunctionsRevertIfExecuted(method f) filtered { f -> && f.selector != updateTimelock(address).selector && f.selector != updateQuorumNumerator(uint256).selector && f.selector != queue(address[],uint256[],bytes[],bytes32).selector + && f.selector != relay(address,uint256,bytes).selector && f.selector != 0xb9a61961 // __acceptAdmin() } { env e; calldataarg args; @@ -302,6 +303,7 @@ rule allFunctionsRevertIfCanceled(method f) filtered { && f.selector != updateTimelock(address).selector && f.selector != updateQuorumNumerator(uint256).selector && f.selector != queue(address[],uint256[],bytes[],bytes32).selector + && f.selector != relay(address,uint256,bytes).selector && f.selector != 0xb9a61961 // __acceptAdmin() } { env e; calldataarg args; From b5980a569c429e729c88c03bce7fcbb402184105 Mon Sep 17 00:00:00 2001 From: Michael George Date: Fri, 17 Dec 2021 09:22:06 -0500 Subject: [PATCH 135/254] reorganized governance verification in preparation for erc20 verification --- certora/scripts/Governor.sh | 10 ---------- certora/scripts/GovernorCountingSimple-counting.sh | 10 ---------- certora/scripts/WizardControlFirstPriority.sh | 12 ------------ certora/scripts/WizardFirstTry.sh | 10 ---------- certora/scripts/{verifyAll.sh => verifyGovernor.sh} | 2 +- certora/specs/{ => governor}/GovernorBase.spec | 0 .../specs/{ => governor}/GovernorCountingSimple.spec | 0 certora/specs/{ => governor}/RulesInProgress.spec | 0 8 files changed, 1 insertion(+), 43 deletions(-) delete mode 100755 certora/scripts/Governor.sh delete mode 100644 certora/scripts/GovernorCountingSimple-counting.sh delete mode 100644 certora/scripts/WizardControlFirstPriority.sh delete mode 100644 certora/scripts/WizardFirstTry.sh rename certora/scripts/{verifyAll.sh => verifyGovernor.sh} (96%) rename certora/specs/{ => governor}/GovernorBase.spec (100%) rename certora/specs/{ => governor}/GovernorCountingSimple.spec (100%) rename certora/specs/{ => governor}/RulesInProgress.spec (100%) diff --git a/certora/scripts/Governor.sh b/certora/scripts/Governor.sh deleted file mode 100755 index 53ade5060..000000000 --- a/certora/scripts/Governor.sh +++ /dev/null @@ -1,10 +0,0 @@ -make -C certora munged - -certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorHarness.sol \ - --verify GovernorHarness:certora/specs/GovernorBase.spec \ - --solc solc8.0 \ - --staging shelly/forSasha \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --rule voteStartBeforeVoteEnd \ - --msg "$1" diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/GovernorCountingSimple-counting.sh deleted file mode 100644 index 9ed8fe34c..000000000 --- a/certora/scripts/GovernorCountingSimple-counting.sh +++ /dev/null @@ -1,10 +0,0 @@ -make -C certora munged - -certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorBasicHarness.sol \ - --verify GovernorBasicHarness:certora/specs/GovernorCountingSimple.spec \ - --solc solc8.2 \ - --staging shelly/forSasha \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --rule hasVotedCorrelation \ - --msg "$1" diff --git a/certora/scripts/WizardControlFirstPriority.sh b/certora/scripts/WizardControlFirstPriority.sh deleted file mode 100644 index b815986ee..000000000 --- a/certora/scripts/WizardControlFirstPriority.sh +++ /dev/null @@ -1,12 +0,0 @@ -make -C certora munged - -certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardControlFirstPriority.sol \ - --link WizardControlFirstPriority:token=ERC20VotesHarness \ - --verify WizardControlFirstPriority:certora/specs/GovernorBase.spec \ - --solc solc8.2 \ - --disableLocalTypeChecking \ - --staging shelly/forSasha \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --rule canVoteDuringVotingPeriod \ - --msg "$1" diff --git a/certora/scripts/WizardFirstTry.sh b/certora/scripts/WizardFirstTry.sh deleted file mode 100644 index fd5a32ab4..000000000 --- a/certora/scripts/WizardFirstTry.sh +++ /dev/null @@ -1,10 +0,0 @@ -make -C certora munged - -certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardFirstTry.sol \ - --verify WizardFirstTry:certora/specs/GovernorBase.spec \ - --solc solc8.2 \ - --staging shelly/forSasha \ - --optimistic_loop \ - --disableLocalTypeChecking \ - --settings -copyLoopUnroll=4 \ - --msg "$1" diff --git a/certora/scripts/verifyAll.sh b/certora/scripts/verifyGovernor.sh similarity index 96% rename from certora/scripts/verifyAll.sh rename to certora/scripts/verifyGovernor.sh index 90d76912c..2e6639422 100644 --- a/certora/scripts/verifyAll.sh +++ b/certora/scripts/verifyGovernor.sh @@ -4,7 +4,7 @@ make -C certora munged for contract in certora/harnesses/Wizard*.sol; do - for spec in certora/specs/*.spec; + for spec in certora/specs/governor*.spec; do contractFile=$(basename $contract) specFile=$(basename $spec) diff --git a/certora/specs/GovernorBase.spec b/certora/specs/governor/GovernorBase.spec similarity index 100% rename from certora/specs/GovernorBase.spec rename to certora/specs/governor/GovernorBase.spec diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/governor/GovernorCountingSimple.spec similarity index 100% rename from certora/specs/GovernorCountingSimple.spec rename to certora/specs/governor/GovernorCountingSimple.spec diff --git a/certora/specs/RulesInProgress.spec b/certora/specs/governor/RulesInProgress.spec similarity index 100% rename from certora/specs/RulesInProgress.spec rename to certora/specs/governor/RulesInProgress.spec From 7ffbf6a3c8948b688d9e77ee35664acd07420fda Mon Sep 17 00:00:00 2001 From: Michael George Date: Fri, 17 Dec 2021 09:30:04 -0500 Subject: [PATCH 136/254] renamed governor sanity script --- certora/scripts/{sanity.sh => sanityGovernor.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename certora/scripts/{sanity.sh => sanityGovernor.sh} (100%) diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanityGovernor.sh similarity index 100% rename from certora/scripts/sanity.sh rename to certora/scripts/sanityGovernor.sh From 44cedd5ea2fd4cffec5a211fc9152b717d885c0b Mon Sep 17 00:00:00 2001 From: Michael George Date: Fri, 17 Dec 2021 09:30:30 -0500 Subject: [PATCH 137/254] made scripts executable --- certora/scripts/sanityGovernor.sh | 0 certora/scripts/verifyGovernor.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 certora/scripts/sanityGovernor.sh mode change 100644 => 100755 certora/scripts/verifyGovernor.sh diff --git a/certora/scripts/sanityGovernor.sh b/certora/scripts/sanityGovernor.sh old mode 100644 new mode 100755 diff --git a/certora/scripts/verifyGovernor.sh b/certora/scripts/verifyGovernor.sh old mode 100644 new mode 100755 From 2304dd7bb117f844daa8535732a44966620d1ad1 Mon Sep 17 00:00:00 2001 From: Michael George Date: Fri, 17 Dec 2021 09:36:01 -0500 Subject: [PATCH 138/254] added script for checking token sanity --- certora/harnesses/ERC20PermitHarness.sol | 5 +++++ certora/harnesses/ERC20WrapperHarness.sol | 9 +++++++++ certora/scripts/sanityTokens.sh | 17 +++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 certora/harnesses/ERC20PermitHarness.sol create mode 100644 certora/harnesses/ERC20WrapperHarness.sol create mode 100755 certora/scripts/sanityTokens.sh diff --git a/certora/harnesses/ERC20PermitHarness.sol b/certora/harnesses/ERC20PermitHarness.sol new file mode 100644 index 000000000..5b51788fa --- /dev/null +++ b/certora/harnesses/ERC20PermitHarness.sol @@ -0,0 +1,5 @@ +import "../munged/token/ERC20/extensions/draft-ERC20Permit.sol"; + +contract ERC20PermitHarness is ERC20Permit { +} + diff --git a/certora/harnesses/ERC20WrapperHarness.sol b/certora/harnesses/ERC20WrapperHarness.sol new file mode 100644 index 000000000..57a6c07be --- /dev/null +++ b/certora/harnesses/ERC20WrapperHarness.sol @@ -0,0 +1,9 @@ +import "../munged/token/ERC20/extensions/ERC20Wrapper.sol"; + +contract ERC20WrapperHarness is ERC20Wrapper { + + constructor(IERC20 underlyingToken, string memory _name, string memory _symbol) + ERC20Wrapper(underlyingToken) + {} +} + diff --git a/certora/scripts/sanityTokens.sh b/certora/scripts/sanityTokens.sh new file mode 100755 index 000000000..10254c843 --- /dev/null +++ b/certora/scripts/sanityTokens.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +make -C certora munged + +for f in certora/harnesses/{ERC20Votes,ERC20Wrapper,ERC20Permit}Harness.sol +do + echo "Processing $f" + file=$(basename $f) + echo ${file%.*} + certoraRun certora/harnesses/$file \ + --verify ${file%.*}:certora/specs/sanity.spec "$@" \ + --solc solc8.2 --staging \ + --optimistic_loop \ + --msg "checking sanity on ${file%.*}" \ + --settings -copyLoopUnroll=4 \ + --send_only +done From 97b2e1b12a8e277730a84bd05ca7e4bf771005b4 Mon Sep 17 00:00:00 2001 From: Michael George Date: Fri, 17 Dec 2021 09:43:47 -0500 Subject: [PATCH 139/254] sanity passes --- certora/harnesses/ERC20PermitHarness.sol | 4 ++++ certora/harnesses/ERC20WrapperHarness.sol | 1 + certora/scripts/sanityTokens.sh | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/certora/harnesses/ERC20PermitHarness.sol b/certora/harnesses/ERC20PermitHarness.sol index 5b51788fa..29d060232 100644 --- a/certora/harnesses/ERC20PermitHarness.sol +++ b/certora/harnesses/ERC20PermitHarness.sol @@ -1,5 +1,9 @@ import "../munged/token/ERC20/extensions/draft-ERC20Permit.sol"; contract ERC20PermitHarness is ERC20Permit { + constructor(string memory _name, string memory _symbol) + ERC20(_name, _symbol) + ERC20Permit(_name) + {} } diff --git a/certora/harnesses/ERC20WrapperHarness.sol b/certora/harnesses/ERC20WrapperHarness.sol index 57a6c07be..23e37df11 100644 --- a/certora/harnesses/ERC20WrapperHarness.sol +++ b/certora/harnesses/ERC20WrapperHarness.sol @@ -4,6 +4,7 @@ contract ERC20WrapperHarness is ERC20Wrapper { constructor(IERC20 underlyingToken, string memory _name, string memory _symbol) ERC20Wrapper(underlyingToken) + ERC20(_name, _symbol) {} } diff --git a/certora/scripts/sanityTokens.sh b/certora/scripts/sanityTokens.sh index 10254c843..30828055b 100755 --- a/certora/scripts/sanityTokens.sh +++ b/certora/scripts/sanityTokens.sh @@ -2,7 +2,7 @@ make -C certora munged -for f in certora/harnesses/{ERC20Votes,ERC20Wrapper,ERC20Permit}Harness.sol +for f in certora/harnesses/ERC20{Votes,Permit,Wrapper}Harness.sol do echo "Processing $f" file=$(basename $f) @@ -12,6 +12,6 @@ do --solc solc8.2 --staging \ --optimistic_loop \ --msg "checking sanity on ${file%.*}" \ - --settings -copyLoopUnroll=4 \ + --settings -copyLoopUnroll=4,-strictDecompiler=false \ --send_only done From ef8013ef791bca5ff99c038d62b844b01b7ef2d9 Mon Sep 17 00:00:00 2001 From: Sameer Arora Date: Thu, 3 Mar 2022 12:42:16 -0800 Subject: [PATCH 140/254] sanity for TimelockController and Votes --- .../harnesses/TimelockControllerHarness.sol | 13 + certora/harnesses/VotesHarness.sol | 14 + certora/munged/.gitignore | 2 - certora/munged/access/AccessControl.sol | 235 ++++++++ .../munged/access/AccessControlEnumerable.sol | 64 ++ certora/munged/access/IAccessControl.sol | 88 +++ .../access/IAccessControlEnumerable.sol | 31 + certora/munged/access/Ownable.sol | 76 +++ certora/munged/access/README.adoc | 21 + certora/munged/finance/PaymentSplitter.sol | 189 ++++++ certora/munged/finance/README.adoc | 20 + certora/munged/finance/VestingWallet.sol | 135 +++++ certora/munged/governance/Governor.sol | 555 ++++++++++++++++++ certora/munged/governance/IGovernor.sol | 276 +++++++++ certora/munged/governance/README.adoc | 176 ++++++ .../munged/governance/TimelockController.sol | 356 +++++++++++ .../GovernorCompatibilityBravo.sol | 289 +++++++++ .../IGovernorCompatibilityBravo.sol | 114 ++++ .../extensions/GovernorCountingSimple.sol | 107 ++++ .../extensions/GovernorPreventLateQuorum.sol | 108 ++++ .../extensions/GovernorProposalThreshold.sol | 23 + .../extensions/GovernorSettings.sol | 114 ++++ .../extensions/GovernorTimelockCompound.sol | 246 ++++++++ .../extensions/GovernorTimelockControl.sol | 166 ++++++ .../governance/extensions/GovernorVotes.sol | 31 + .../extensions/GovernorVotesComp.sol | 31 + .../GovernorVotesQuorumFraction.sol | 85 +++ .../extensions/IGovernorTimelock.sol | 26 + certora/munged/governance/utils/IVotes.sol | 61 ++ certora/munged/governance/utils/Votes.sol | 211 +++++++ certora/munged/interfaces/IERC1155.sol | 6 + .../munged/interfaces/IERC1155MetadataURI.sol | 6 + .../munged/interfaces/IERC1155Receiver.sol | 6 + certora/munged/interfaces/IERC1271.sol | 19 + certora/munged/interfaces/IERC1363.sol | 95 +++ .../munged/interfaces/IERC1363Receiver.sol | 32 + certora/munged/interfaces/IERC1363Spender.sol | 30 + certora/munged/interfaces/IERC165.sol | 6 + .../munged/interfaces/IERC1820Implementer.sol | 6 + .../munged/interfaces/IERC1820Registry.sol | 6 + certora/munged/interfaces/IERC20.sol | 6 + certora/munged/interfaces/IERC20Metadata.sol | 6 + certora/munged/interfaces/IERC2981.sol | 25 + certora/munged/interfaces/IERC3156.sol | 7 + .../interfaces/IERC3156FlashBorrower.sol | 29 + .../munged/interfaces/IERC3156FlashLender.sol | 43 ++ certora/munged/interfaces/IERC721.sol | 6 + .../munged/interfaces/IERC721Enumerable.sol | 6 + certora/munged/interfaces/IERC721Metadata.sol | 6 + certora/munged/interfaces/IERC721Receiver.sol | 6 + certora/munged/interfaces/IERC777.sol | 6 + .../munged/interfaces/IERC777Recipient.sol | 6 + certora/munged/interfaces/IERC777Sender.sol | 6 + certora/munged/interfaces/README.adoc | 50 ++ certora/munged/interfaces/draft-IERC1822.sol | 20 + certora/munged/interfaces/draft-IERC2612.sol | 8 + certora/munged/metatx/ERC2771Context.sol | 42 ++ certora/munged/metatx/MinimalForwarder.sol | 67 +++ certora/munged/metatx/README.adoc | 12 + .../mocks/AccessControlEnumerableMock.sol | 17 + certora/munged/mocks/AccessControlMock.sol | 17 + certora/munged/mocks/AddressImpl.sol | 46 ++ certora/munged/mocks/ArraysImpl.sol | 19 + certora/munged/mocks/BadBeacon.sol | 11 + certora/munged/mocks/Base64Mock.sol | 11 + certora/munged/mocks/BitmapMock.sol | 27 + certora/munged/mocks/CallReceiverMock.sol | 57 ++ certora/munged/mocks/CheckpointsImpl.sol | 23 + .../munged/mocks/ClashingImplementation.sol | 18 + certora/munged/mocks/ClonesMock.sol | 36 ++ .../munged/mocks/ConditionalEscrowMock.sol | 18 + certora/munged/mocks/ContextMock.sol | 33 ++ certora/munged/mocks/CountersImpl.sol | 27 + certora/munged/mocks/Create2Impl.sol | 34 ++ certora/munged/mocks/DoubleEndedQueueMock.sol | 58 ++ certora/munged/mocks/DummyImplementation.sol | 61 ++ certora/munged/mocks/ECDSAMock.sol | 41 ++ certora/munged/mocks/EIP712External.sol | 31 + certora/munged/mocks/ERC1155BurnableMock.sol | 18 + certora/munged/mocks/ERC1155Mock.sol | 51 ++ certora/munged/mocks/ERC1155PausableMock.sol | 29 + certora/munged/mocks/ERC1155ReceiverMock.sol | 52 ++ certora/munged/mocks/ERC1155SupplyMock.sol | 21 + certora/munged/mocks/ERC1271WalletMock.sol | 17 + .../ERC165/ERC165InterfacesSupported.sol | 58 ++ .../munged/mocks/ERC165/ERC165MissingData.sol | 7 + .../mocks/ERC165/ERC165NotSupported.sol | 5 + certora/munged/mocks/ERC165CheckerMock.sol | 25 + certora/munged/mocks/ERC165Mock.sol | 7 + certora/munged/mocks/ERC165StorageMock.sol | 11 + .../munged/mocks/ERC1820ImplementerMock.sol | 11 + certora/munged/mocks/ERC20BurnableMock.sol | 16 + certora/munged/mocks/ERC20CappedMock.sol | 17 + certora/munged/mocks/ERC20DecimalsMock.sol | 21 + certora/munged/mocks/ERC20FlashMintMock.sol | 16 + certora/munged/mocks/ERC20Mock.sol | 41 ++ certora/munged/mocks/ERC20PausableMock.sol | 33 ++ certora/munged/mocks/ERC20PermitMock.sol | 20 + certora/munged/mocks/ERC20SnapshotMock.sol | 28 + certora/munged/mocks/ERC20VotesCompMock.sol | 21 + certora/munged/mocks/ERC20VotesMock.sol | 21 + certora/munged/mocks/ERC20WrapperMock.sol | 17 + certora/munged/mocks/ERC2771ContextMock.sol | 22 + .../munged/mocks/ERC3156FlashBorrowerMock.sol | 53 ++ certora/munged/mocks/ERC721BurnableMock.sol | 29 + certora/munged/mocks/ERC721EnumerableMock.sol | 51 ++ certora/munged/mocks/ERC721Mock.sol | 41 ++ certora/munged/mocks/ERC721PausableMock.sol | 45 ++ certora/munged/mocks/ERC721ReceiverMock.sol | 42 ++ certora/munged/mocks/ERC721RoyaltyMock.sol | 33 ++ certora/munged/mocks/ERC721URIStorageMock.sol | 55 ++ certora/munged/mocks/ERC721VotesMock.sol | 25 + certora/munged/mocks/ERC777Mock.sol | 56 ++ .../mocks/ERC777SenderRecipientMock.sol | 161 +++++ certora/munged/mocks/EnumerableMapMock.sol | 87 +++ certora/munged/mocks/EnumerableSetMock.sol | 110 ++++ certora/munged/mocks/EtherReceiverMock.sol | 17 + certora/munged/mocks/GovernorCompMock.sol | 31 + .../mocks/GovernorCompatibilityBravoMock.sol | 130 ++++ certora/munged/mocks/GovernorMock.sol | 50 ++ .../mocks/GovernorPreventLateQuorumMock.sol | 61 ++ .../mocks/GovernorTimelockCompoundMock.sol | 98 ++++ .../mocks/GovernorTimelockControlMock.sol | 100 ++++ certora/munged/mocks/GovernorVoteMock.sol | 31 + .../munged/mocks/GovernorWithParamsMock.sol | 61 ++ certora/munged/mocks/InitializableMock.sol | 61 ++ certora/munged/mocks/MathMock.sol | 23 + certora/munged/mocks/MerkleProofWrapper.sol | 19 + certora/munged/mocks/MulticallTest.sol | 23 + certora/munged/mocks/MulticallTokenMock.sol | 10 + .../MultipleInheritanceInitializableMocks.sol | 136 +++++ certora/munged/mocks/OwnableMock.sol | 7 + certora/munged/mocks/PausableMock.sol | 31 + certora/munged/mocks/PullPaymentMock.sol | 15 + certora/munged/mocks/ReentrancyAttack.sol | 12 + certora/munged/mocks/ReentrancyMock.sol | 43 ++ .../munged/mocks/RegressionImplementation.sol | 61 ++ certora/munged/mocks/SafeCastMock.sol | 66 +++ certora/munged/mocks/SafeERC20Helper.sol | 144 +++++ certora/munged/mocks/SafeMathMock.sol | 138 +++++ certora/munged/mocks/SignatureCheckerMock.sol | 17 + certora/munged/mocks/SignedMathMock.sol | 23 + certora/munged/mocks/SignedSafeMathMock.sol | 23 + .../SingleInheritanceInitializableMocks.sol | 49 ++ certora/munged/mocks/StorageSlotMock.sol | 41 ++ certora/munged/mocks/StringsMock.sol | 19 + .../munged/mocks/TimersBlockNumberImpl.sol | 39 ++ certora/munged/mocks/TimersTimestampImpl.sol | 39 ++ certora/munged/mocks/UUPS/UUPSLegacy.sol | 58 ++ .../munged/mocks/UUPS/UUPSUpgradeableMock.sol | 21 + certora/munged/mocks/VotesMock.sol | 40 ++ .../munged/mocks/compound/CompTimelock.sol | 174 ++++++ certora/munged/mocks/wizard/MyGovernor1.sol | 87 +++ certora/munged/mocks/wizard/MyGovernor2.sol | 93 +++ certora/munged/mocks/wizard/MyGovernor3.sol | 96 +++ certora/munged/package.json | 32 + certora/munged/proxy/Clones.sol | 84 +++ certora/munged/proxy/ERC1967/ERC1967Proxy.sol | 33 ++ .../munged/proxy/ERC1967/ERC1967Upgrade.sol | 185 ++++++ certora/munged/proxy/Proxy.sol | 86 +++ certora/munged/proxy/README.adoc | 85 +++ certora/munged/proxy/beacon/BeaconProxy.sol | 62 ++ certora/munged/proxy/beacon/IBeacon.sol | 16 + .../munged/proxy/beacon/UpgradeableBeacon.sol | 65 ++ .../munged/proxy/transparent/ProxyAdmin.sol | 81 +++ .../TransparentUpgradeableProxy.sol | 125 ++++ certora/munged/proxy/utils/Initializable.sol | 80 +++ .../munged/proxy/utils/UUPSUpgradeable.sol | 95 +++ certora/munged/security/Pausable.sol | 91 +++ certora/munged/security/PullPayment.sol | 70 +++ certora/munged/security/README.adoc | 20 + certora/munged/security/ReentrancyGuard.sol | 63 ++ certora/munged/token/ERC1155/ERC1155.sol | 511 ++++++++++++++++ certora/munged/token/ERC1155/IERC1155.sol | 125 ++++ .../munged/token/ERC1155/IERC1155Receiver.sol | 58 ++ certora/munged/token/ERC1155/README.adoc | 47 ++ .../ERC1155/extensions/ERC1155Burnable.sol | 40 ++ .../ERC1155/extensions/ERC1155Pausable.sol | 38 ++ .../ERC1155/extensions/ERC1155Supply.sol | 64 ++ .../extensions/IERC1155MetadataURI.sol | 22 + .../presets/ERC1155PresetMinterPauser.sol | 128 ++++ .../munged/token/ERC1155/presets/README.md | 1 + .../token/ERC1155/utils/ERC1155Holder.sol | 36 ++ .../token/ERC1155/utils/ERC1155Receiver.sol | 19 + certora/munged/token/ERC20/ERC20.sol | 383 ++++++++++++ certora/munged/token/ERC20/IERC20.sol | 82 +++ certora/munged/token/ERC20/README.adoc | 83 +++ .../token/ERC20/extensions/ERC20Burnable.sol | 39 ++ .../token/ERC20/extensions/ERC20Capped.sol | 37 ++ .../token/ERC20/extensions/ERC20FlashMint.sol | 82 +++ .../token/ERC20/extensions/ERC20Pausable.sol | 33 ++ .../token/ERC20/extensions/ERC20Snapshot.sol | 195 ++++++ .../token/ERC20/extensions/ERC20Votes.sol | 249 ++++++++ .../token/ERC20/extensions/ERC20VotesComp.sol | 46 ++ .../token/ERC20/extensions/ERC20Wrapper.sol | 52 ++ .../token/ERC20/extensions/IERC20Metadata.sol | 28 + .../ERC20/extensions/draft-ERC20Permit.sol | 87 +++ .../ERC20/extensions/draft-IERC20Permit.sol | 60 ++ .../ERC20/presets/ERC20PresetFixedSupply.sol | 35 ++ .../ERC20/presets/ERC20PresetMinterPauser.sol | 94 +++ certora/munged/token/ERC20/presets/README.md | 1 + .../munged/token/ERC20/utils/SafeERC20.sol | 99 ++++ .../token/ERC20/utils/TokenTimelock.sol | 76 +++ certora/munged/token/ERC721/ERC721.sol | 447 ++++++++++++++ certora/munged/token/ERC721/IERC721.sol | 143 +++++ .../munged/token/ERC721/IERC721Receiver.sol | 27 + certora/munged/token/ERC721/README.adoc | 67 +++ .../ERC721/extensions/ERC721Burnable.sol | 26 + .../ERC721/extensions/ERC721Enumerable.sol | 163 +++++ .../ERC721/extensions/ERC721Pausable.sol | 33 ++ .../token/ERC721/extensions/ERC721Royalty.sol | 38 ++ .../ERC721/extensions/ERC721URIStorage.sol | 67 +++ .../ERC721/extensions/IERC721Enumerable.sol | 29 + .../ERC721/extensions/IERC721Metadata.sol | 27 + .../ERC721/extensions/draft-ERC721Votes.sol | 40 ++ .../ERC721PresetMinterPauserAutoId.sol | 139 +++++ certora/munged/token/ERC721/presets/README.md | 1 + .../token/ERC721/utils/ERC721Holder.sol | 28 + certora/munged/token/ERC777/ERC777.sol | 546 +++++++++++++++++ certora/munged/token/ERC777/IERC777.sol | 193 ++++++ .../munged/token/ERC777/IERC777Recipient.sol | 35 ++ certora/munged/token/ERC777/IERC777Sender.sol | 35 ++ certora/munged/token/ERC777/README.adoc | 30 + .../presets/ERC777PresetFixedSupply.sol | 30 + certora/munged/token/common/ERC2981.sol | 118 ++++ certora/munged/token/common/README.adoc | 10 + certora/munged/utils/Address.sol | 222 +++++++ certora/munged/utils/Arrays.sol | 48 ++ certora/munged/utils/Base64.sol | 91 +++ certora/munged/utils/Checkpoints.sol | 86 +++ certora/munged/utils/Context.sol | 24 + certora/munged/utils/Counters.sol | 43 ++ certora/munged/utils/Create2.sol | 65 ++ certora/munged/utils/Multicall.sol | 24 + certora/munged/utils/README.adoc | 111 ++++ certora/munged/utils/StorageSlot.sol | 84 +++ certora/munged/utils/Strings.sol | 67 +++ certora/munged/utils/Timers.sol | 73 +++ certora/munged/utils/cryptography/ECDSA.sol | 230 ++++++++ .../munged/utils/cryptography/MerkleProof.sol | 65 ++ .../utils/cryptography/SignatureChecker.sol | 40 ++ .../utils/cryptography/draft-EIP712.sol | 104 ++++ .../munged/utils/escrow/ConditionalEscrow.sol | 25 + certora/munged/utils/escrow/Escrow.sol | 63 ++ certora/munged/utils/escrow/RefundEscrow.sol | 100 ++++ certora/munged/utils/introspection/ERC165.sol | 29 + .../utils/introspection/ERC165Checker.sol | 113 ++++ .../utils/introspection/ERC165Storage.sol | 42 ++ .../introspection/ERC1820Implementer.sol | 44 ++ .../munged/utils/introspection/IERC165.sol | 25 + .../introspection/IERC1820Implementer.sol | 20 + .../utils/introspection/IERC1820Registry.sol | 116 ++++ certora/munged/utils/math/Math.sol | 43 ++ certora/munged/utils/math/SafeCast.sol | 241 ++++++++ certora/munged/utils/math/SafeMath.sol | 227 +++++++ certora/munged/utils/math/SignedMath.sol | 43 ++ certora/munged/utils/math/SignedSafeMath.sol | 68 +++ certora/munged/utils/structs/BitMaps.sol | 55 ++ .../munged/utils/structs/DoubleEndedQueue.sol | 169 ++++++ .../munged/utils/structs/EnumerableMap.sol | 322 ++++++++++ .../munged/utils/structs/EnumerableSet.sol | 357 +++++++++++ certora/scripts/sanity.sh | 45 +- 262 files changed, 18482 insertions(+), 14 deletions(-) create mode 100644 certora/harnesses/TimelockControllerHarness.sol create mode 100644 certora/harnesses/VotesHarness.sol delete mode 100644 certora/munged/.gitignore create mode 100644 certora/munged/access/AccessControl.sol create mode 100644 certora/munged/access/AccessControlEnumerable.sol create mode 100644 certora/munged/access/IAccessControl.sol create mode 100644 certora/munged/access/IAccessControlEnumerable.sol create mode 100644 certora/munged/access/Ownable.sol create mode 100644 certora/munged/access/README.adoc create mode 100644 certora/munged/finance/PaymentSplitter.sol create mode 100644 certora/munged/finance/README.adoc create mode 100644 certora/munged/finance/VestingWallet.sol create mode 100644 certora/munged/governance/Governor.sol create mode 100644 certora/munged/governance/IGovernor.sol create mode 100644 certora/munged/governance/README.adoc create mode 100644 certora/munged/governance/TimelockController.sol create mode 100644 certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol create mode 100644 certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol create mode 100644 certora/munged/governance/extensions/GovernorCountingSimple.sol create mode 100644 certora/munged/governance/extensions/GovernorPreventLateQuorum.sol create mode 100644 certora/munged/governance/extensions/GovernorProposalThreshold.sol create mode 100644 certora/munged/governance/extensions/GovernorSettings.sol create mode 100644 certora/munged/governance/extensions/GovernorTimelockCompound.sol create mode 100644 certora/munged/governance/extensions/GovernorTimelockControl.sol create mode 100644 certora/munged/governance/extensions/GovernorVotes.sol create mode 100644 certora/munged/governance/extensions/GovernorVotesComp.sol create mode 100644 certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol create mode 100644 certora/munged/governance/extensions/IGovernorTimelock.sol create mode 100644 certora/munged/governance/utils/IVotes.sol create mode 100644 certora/munged/governance/utils/Votes.sol create mode 100644 certora/munged/interfaces/IERC1155.sol create mode 100644 certora/munged/interfaces/IERC1155MetadataURI.sol create mode 100644 certora/munged/interfaces/IERC1155Receiver.sol create mode 100644 certora/munged/interfaces/IERC1271.sol create mode 100644 certora/munged/interfaces/IERC1363.sol create mode 100644 certora/munged/interfaces/IERC1363Receiver.sol create mode 100644 certora/munged/interfaces/IERC1363Spender.sol create mode 100644 certora/munged/interfaces/IERC165.sol create mode 100644 certora/munged/interfaces/IERC1820Implementer.sol create mode 100644 certora/munged/interfaces/IERC1820Registry.sol create mode 100644 certora/munged/interfaces/IERC20.sol create mode 100644 certora/munged/interfaces/IERC20Metadata.sol create mode 100644 certora/munged/interfaces/IERC2981.sol create mode 100644 certora/munged/interfaces/IERC3156.sol create mode 100644 certora/munged/interfaces/IERC3156FlashBorrower.sol create mode 100644 certora/munged/interfaces/IERC3156FlashLender.sol create mode 100644 certora/munged/interfaces/IERC721.sol create mode 100644 certora/munged/interfaces/IERC721Enumerable.sol create mode 100644 certora/munged/interfaces/IERC721Metadata.sol create mode 100644 certora/munged/interfaces/IERC721Receiver.sol create mode 100644 certora/munged/interfaces/IERC777.sol create mode 100644 certora/munged/interfaces/IERC777Recipient.sol create mode 100644 certora/munged/interfaces/IERC777Sender.sol create mode 100644 certora/munged/interfaces/README.adoc create mode 100644 certora/munged/interfaces/draft-IERC1822.sol create mode 100644 certora/munged/interfaces/draft-IERC2612.sol create mode 100644 certora/munged/metatx/ERC2771Context.sol create mode 100644 certora/munged/metatx/MinimalForwarder.sol create mode 100644 certora/munged/metatx/README.adoc create mode 100644 certora/munged/mocks/AccessControlEnumerableMock.sol create mode 100644 certora/munged/mocks/AccessControlMock.sol create mode 100644 certora/munged/mocks/AddressImpl.sol create mode 100644 certora/munged/mocks/ArraysImpl.sol create mode 100644 certora/munged/mocks/BadBeacon.sol create mode 100644 certora/munged/mocks/Base64Mock.sol create mode 100644 certora/munged/mocks/BitmapMock.sol create mode 100644 certora/munged/mocks/CallReceiverMock.sol create mode 100644 certora/munged/mocks/CheckpointsImpl.sol create mode 100644 certora/munged/mocks/ClashingImplementation.sol create mode 100644 certora/munged/mocks/ClonesMock.sol create mode 100644 certora/munged/mocks/ConditionalEscrowMock.sol create mode 100644 certora/munged/mocks/ContextMock.sol create mode 100644 certora/munged/mocks/CountersImpl.sol create mode 100644 certora/munged/mocks/Create2Impl.sol create mode 100644 certora/munged/mocks/DoubleEndedQueueMock.sol create mode 100644 certora/munged/mocks/DummyImplementation.sol create mode 100644 certora/munged/mocks/ECDSAMock.sol create mode 100644 certora/munged/mocks/EIP712External.sol create mode 100644 certora/munged/mocks/ERC1155BurnableMock.sol create mode 100644 certora/munged/mocks/ERC1155Mock.sol create mode 100644 certora/munged/mocks/ERC1155PausableMock.sol create mode 100644 certora/munged/mocks/ERC1155ReceiverMock.sol create mode 100644 certora/munged/mocks/ERC1155SupplyMock.sol create mode 100644 certora/munged/mocks/ERC1271WalletMock.sol create mode 100644 certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol create mode 100644 certora/munged/mocks/ERC165/ERC165MissingData.sol create mode 100644 certora/munged/mocks/ERC165/ERC165NotSupported.sol create mode 100644 certora/munged/mocks/ERC165CheckerMock.sol create mode 100644 certora/munged/mocks/ERC165Mock.sol create mode 100644 certora/munged/mocks/ERC165StorageMock.sol create mode 100644 certora/munged/mocks/ERC1820ImplementerMock.sol create mode 100644 certora/munged/mocks/ERC20BurnableMock.sol create mode 100644 certora/munged/mocks/ERC20CappedMock.sol create mode 100644 certora/munged/mocks/ERC20DecimalsMock.sol create mode 100644 certora/munged/mocks/ERC20FlashMintMock.sol create mode 100644 certora/munged/mocks/ERC20Mock.sol create mode 100644 certora/munged/mocks/ERC20PausableMock.sol create mode 100644 certora/munged/mocks/ERC20PermitMock.sol create mode 100644 certora/munged/mocks/ERC20SnapshotMock.sol create mode 100644 certora/munged/mocks/ERC20VotesCompMock.sol create mode 100644 certora/munged/mocks/ERC20VotesMock.sol create mode 100644 certora/munged/mocks/ERC20WrapperMock.sol create mode 100644 certora/munged/mocks/ERC2771ContextMock.sol create mode 100644 certora/munged/mocks/ERC3156FlashBorrowerMock.sol create mode 100644 certora/munged/mocks/ERC721BurnableMock.sol create mode 100644 certora/munged/mocks/ERC721EnumerableMock.sol create mode 100644 certora/munged/mocks/ERC721Mock.sol create mode 100644 certora/munged/mocks/ERC721PausableMock.sol create mode 100644 certora/munged/mocks/ERC721ReceiverMock.sol create mode 100644 certora/munged/mocks/ERC721RoyaltyMock.sol create mode 100644 certora/munged/mocks/ERC721URIStorageMock.sol create mode 100644 certora/munged/mocks/ERC721VotesMock.sol create mode 100644 certora/munged/mocks/ERC777Mock.sol create mode 100644 certora/munged/mocks/ERC777SenderRecipientMock.sol create mode 100644 certora/munged/mocks/EnumerableMapMock.sol create mode 100644 certora/munged/mocks/EnumerableSetMock.sol create mode 100644 certora/munged/mocks/EtherReceiverMock.sol create mode 100644 certora/munged/mocks/GovernorCompMock.sol create mode 100644 certora/munged/mocks/GovernorCompatibilityBravoMock.sol create mode 100644 certora/munged/mocks/GovernorMock.sol create mode 100644 certora/munged/mocks/GovernorPreventLateQuorumMock.sol create mode 100644 certora/munged/mocks/GovernorTimelockCompoundMock.sol create mode 100644 certora/munged/mocks/GovernorTimelockControlMock.sol create mode 100644 certora/munged/mocks/GovernorVoteMock.sol create mode 100644 certora/munged/mocks/GovernorWithParamsMock.sol create mode 100644 certora/munged/mocks/InitializableMock.sol create mode 100644 certora/munged/mocks/MathMock.sol create mode 100644 certora/munged/mocks/MerkleProofWrapper.sol create mode 100644 certora/munged/mocks/MulticallTest.sol create mode 100644 certora/munged/mocks/MulticallTokenMock.sol create mode 100644 certora/munged/mocks/MultipleInheritanceInitializableMocks.sol create mode 100644 certora/munged/mocks/OwnableMock.sol create mode 100644 certora/munged/mocks/PausableMock.sol create mode 100644 certora/munged/mocks/PullPaymentMock.sol create mode 100644 certora/munged/mocks/ReentrancyAttack.sol create mode 100644 certora/munged/mocks/ReentrancyMock.sol create mode 100644 certora/munged/mocks/RegressionImplementation.sol create mode 100644 certora/munged/mocks/SafeCastMock.sol create mode 100644 certora/munged/mocks/SafeERC20Helper.sol create mode 100644 certora/munged/mocks/SafeMathMock.sol create mode 100644 certora/munged/mocks/SignatureCheckerMock.sol create mode 100644 certora/munged/mocks/SignedMathMock.sol create mode 100644 certora/munged/mocks/SignedSafeMathMock.sol create mode 100644 certora/munged/mocks/SingleInheritanceInitializableMocks.sol create mode 100644 certora/munged/mocks/StorageSlotMock.sol create mode 100644 certora/munged/mocks/StringsMock.sol create mode 100644 certora/munged/mocks/TimersBlockNumberImpl.sol create mode 100644 certora/munged/mocks/TimersTimestampImpl.sol create mode 100644 certora/munged/mocks/UUPS/UUPSLegacy.sol create mode 100644 certora/munged/mocks/UUPS/UUPSUpgradeableMock.sol create mode 100644 certora/munged/mocks/VotesMock.sol create mode 100644 certora/munged/mocks/compound/CompTimelock.sol create mode 100644 certora/munged/mocks/wizard/MyGovernor1.sol create mode 100644 certora/munged/mocks/wizard/MyGovernor2.sol create mode 100644 certora/munged/mocks/wizard/MyGovernor3.sol create mode 100644 certora/munged/package.json create mode 100644 certora/munged/proxy/Clones.sol create mode 100644 certora/munged/proxy/ERC1967/ERC1967Proxy.sol create mode 100644 certora/munged/proxy/ERC1967/ERC1967Upgrade.sol create mode 100644 certora/munged/proxy/Proxy.sol create mode 100644 certora/munged/proxy/README.adoc create mode 100644 certora/munged/proxy/beacon/BeaconProxy.sol create mode 100644 certora/munged/proxy/beacon/IBeacon.sol create mode 100644 certora/munged/proxy/beacon/UpgradeableBeacon.sol create mode 100644 certora/munged/proxy/transparent/ProxyAdmin.sol create mode 100644 certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol create mode 100644 certora/munged/proxy/utils/Initializable.sol create mode 100644 certora/munged/proxy/utils/UUPSUpgradeable.sol create mode 100644 certora/munged/security/Pausable.sol create mode 100644 certora/munged/security/PullPayment.sol create mode 100644 certora/munged/security/README.adoc create mode 100644 certora/munged/security/ReentrancyGuard.sol create mode 100644 certora/munged/token/ERC1155/ERC1155.sol create mode 100644 certora/munged/token/ERC1155/IERC1155.sol create mode 100644 certora/munged/token/ERC1155/IERC1155Receiver.sol create mode 100644 certora/munged/token/ERC1155/README.adoc create mode 100644 certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol create mode 100644 certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol create mode 100644 certora/munged/token/ERC1155/extensions/ERC1155Supply.sol create mode 100644 certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol create mode 100644 certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol create mode 100644 certora/munged/token/ERC1155/presets/README.md create mode 100644 certora/munged/token/ERC1155/utils/ERC1155Holder.sol create mode 100644 certora/munged/token/ERC1155/utils/ERC1155Receiver.sol create mode 100644 certora/munged/token/ERC20/ERC20.sol create mode 100644 certora/munged/token/ERC20/IERC20.sol create mode 100644 certora/munged/token/ERC20/README.adoc create mode 100644 certora/munged/token/ERC20/extensions/ERC20Burnable.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20Capped.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20FlashMint.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20Pausable.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20Snapshot.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20Votes.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20VotesComp.sol create mode 100644 certora/munged/token/ERC20/extensions/ERC20Wrapper.sol create mode 100644 certora/munged/token/ERC20/extensions/IERC20Metadata.sol create mode 100644 certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol create mode 100644 certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol create mode 100644 certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol create mode 100644 certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol create mode 100644 certora/munged/token/ERC20/presets/README.md create mode 100644 certora/munged/token/ERC20/utils/SafeERC20.sol create mode 100644 certora/munged/token/ERC20/utils/TokenTimelock.sol create mode 100644 certora/munged/token/ERC721/ERC721.sol create mode 100644 certora/munged/token/ERC721/IERC721.sol create mode 100644 certora/munged/token/ERC721/IERC721Receiver.sol create mode 100644 certora/munged/token/ERC721/README.adoc create mode 100644 certora/munged/token/ERC721/extensions/ERC721Burnable.sol create mode 100644 certora/munged/token/ERC721/extensions/ERC721Enumerable.sol create mode 100644 certora/munged/token/ERC721/extensions/ERC721Pausable.sol create mode 100644 certora/munged/token/ERC721/extensions/ERC721Royalty.sol create mode 100644 certora/munged/token/ERC721/extensions/ERC721URIStorage.sol create mode 100644 certora/munged/token/ERC721/extensions/IERC721Enumerable.sol create mode 100644 certora/munged/token/ERC721/extensions/IERC721Metadata.sol create mode 100644 certora/munged/token/ERC721/extensions/draft-ERC721Votes.sol create mode 100644 certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol create mode 100644 certora/munged/token/ERC721/presets/README.md create mode 100644 certora/munged/token/ERC721/utils/ERC721Holder.sol create mode 100644 certora/munged/token/ERC777/ERC777.sol create mode 100644 certora/munged/token/ERC777/IERC777.sol create mode 100644 certora/munged/token/ERC777/IERC777Recipient.sol create mode 100644 certora/munged/token/ERC777/IERC777Sender.sol create mode 100644 certora/munged/token/ERC777/README.adoc create mode 100644 certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol create mode 100644 certora/munged/token/common/ERC2981.sol create mode 100644 certora/munged/token/common/README.adoc create mode 100644 certora/munged/utils/Address.sol create mode 100644 certora/munged/utils/Arrays.sol create mode 100644 certora/munged/utils/Base64.sol create mode 100644 certora/munged/utils/Checkpoints.sol create mode 100644 certora/munged/utils/Context.sol create mode 100644 certora/munged/utils/Counters.sol create mode 100644 certora/munged/utils/Create2.sol create mode 100644 certora/munged/utils/Multicall.sol create mode 100644 certora/munged/utils/README.adoc create mode 100644 certora/munged/utils/StorageSlot.sol create mode 100644 certora/munged/utils/Strings.sol create mode 100644 certora/munged/utils/Timers.sol create mode 100644 certora/munged/utils/cryptography/ECDSA.sol create mode 100644 certora/munged/utils/cryptography/MerkleProof.sol create mode 100644 certora/munged/utils/cryptography/SignatureChecker.sol create mode 100644 certora/munged/utils/cryptography/draft-EIP712.sol create mode 100644 certora/munged/utils/escrow/ConditionalEscrow.sol create mode 100644 certora/munged/utils/escrow/Escrow.sol create mode 100644 certora/munged/utils/escrow/RefundEscrow.sol create mode 100644 certora/munged/utils/introspection/ERC165.sol create mode 100644 certora/munged/utils/introspection/ERC165Checker.sol create mode 100644 certora/munged/utils/introspection/ERC165Storage.sol create mode 100644 certora/munged/utils/introspection/ERC1820Implementer.sol create mode 100644 certora/munged/utils/introspection/IERC165.sol create mode 100644 certora/munged/utils/introspection/IERC1820Implementer.sol create mode 100644 certora/munged/utils/introspection/IERC1820Registry.sol create mode 100644 certora/munged/utils/math/Math.sol create mode 100644 certora/munged/utils/math/SafeCast.sol create mode 100644 certora/munged/utils/math/SafeMath.sol create mode 100644 certora/munged/utils/math/SignedMath.sol create mode 100644 certora/munged/utils/math/SignedSafeMath.sol create mode 100644 certora/munged/utils/structs/BitMaps.sol create mode 100644 certora/munged/utils/structs/DoubleEndedQueue.sol create mode 100644 certora/munged/utils/structs/EnumerableMap.sol create mode 100644 certora/munged/utils/structs/EnumerableSet.sol diff --git a/certora/harnesses/TimelockControllerHarness.sol b/certora/harnesses/TimelockControllerHarness.sol new file mode 100644 index 000000000..8c3f794fa --- /dev/null +++ b/certora/harnesses/TimelockControllerHarness.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.8.0; + +import "../munged/governance/TimelockController.sol"; + + contract TimelockControllerHarness is TimelockController { + constructor( + uint256 minDelay, + address[] memory proposers, + address[] memory executors + ) TimelockController(minDelay, proposers, executors) { + + } +} \ No newline at end of file diff --git a/certora/harnesses/VotesHarness.sol b/certora/harnesses/VotesHarness.sol new file mode 100644 index 000000000..e57405d20 --- /dev/null +++ b/certora/harnesses/VotesHarness.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.8.0; + +import "../munged/governance/utils/Votes.sol"; + + contract VotesHarness is Votes { + + constructor(string memory name, string memory version) EIP712(name, version) { + + } + + function _getVotingUnits(address) public override returns (uint256) { + return 0; + } +} \ No newline at end of file diff --git a/certora/munged/.gitignore b/certora/munged/.gitignore deleted file mode 100644 index d6b7ef32c..000000000 --- a/certora/munged/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/certora/munged/access/AccessControl.sol b/certora/munged/access/AccessControl.sol new file mode 100644 index 000000000..2ac203867 --- /dev/null +++ b/certora/munged/access/AccessControl.sol @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol) + +pragma solidity ^0.8.0; + +import "./IAccessControl.sol"; +import "../utils/Context.sol"; +import "../utils/Strings.sol"; +import "../utils/introspection/ERC165.sol"; + +/** + * @dev Contract module that allows children to implement role-based access + * control mechanisms. This is a lightweight version that doesn't allow enumerating role + * members except through off-chain means by accessing the contract event logs. Some + * applications may benefit from on-chain enumerability, for those cases see + * {AccessControlEnumerable}. + * + * Roles are referred to by their `bytes32` identifier. These should be exposed + * in the external API and be unique. The best way to achieve this is by + * using `public constant` hash digests: + * + * ``` + * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); + * ``` + * + * Roles can be used to represent a set of permissions. To restrict access to a + * function call, use {hasRole}: + * + * ``` + * function foo() public { + * require(hasRole(MY_ROLE, msg.sender)); + * ... + * } + * ``` + * + * Roles can be granted and revoked dynamically via the {grantRole} and + * {revokeRole} functions. Each role has an associated admin role, and only + * accounts that have a role's admin role can call {grantRole} and {revokeRole}. + * + * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means + * that only accounts with this role will be able to grant or revoke other + * roles. More complex role relationships can be created by using + * {_setRoleAdmin}. + * + * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to + * grant and revoke this role. Extra precautions should be taken to secure + * accounts that have been granted it. + */ +abstract contract AccessControl is Context, IAccessControl, ERC165 { + struct RoleData { + mapping(address => bool) members; + bytes32 adminRole; + } + + mapping(bytes32 => RoleData) private _roles; + + bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; + + /** + * @dev Modifier that checks that an account has a specific role. Reverts + * with a standardized message including the required role. + * + * The format of the revert reason is given by the following regular expression: + * + * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ + * + * _Available since v4.1._ + */ + modifier onlyRole(bytes32 role) { + _checkRole(role); + _; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Returns `true` if `account` has been granted `role`. + */ + function hasRole(bytes32 role, address account) public view virtual override returns (bool) { + return _roles[role].members[account]; + } + + /** + * @dev Revert with a standard message if `_msgSender()` is missing `role`. + * Overriding this function changes the behavior of the {onlyRole} modifier. + * + * Format of the revert message is described in {_checkRole}. + * + * _Available since v4.6._ + */ + function _checkRole(bytes32 role) internal view virtual { + _checkRole(role, _msgSender()); + } + + /** + * @dev Revert with a standard message if `account` is missing `role`. + * + * The format of the revert reason is given by the following regular expression: + * + * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ + */ + function _checkRole(bytes32 role, address account) internal view virtual { + if (!hasRole(role, account)) { + revert( + string( + abi.encodePacked( + "AccessControl: account ", + Strings.toHexString(uint160(account), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ) + ); + } + } + + /** + * @dev Returns the admin role that controls `role`. See {grantRole} and + * {revokeRole}. + * + * To change a role's admin, use {_setRoleAdmin}. + */ + function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { + return _roles[role].adminRole; + } + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { + _grantRole(role, account); + } + + /** + * @dev Revokes `role` from `account`. + * + * If `account` had been granted `role`, emits a {RoleRevoked} event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { + _revokeRole(role, account); + } + + /** + * @dev Revokes `role` from the calling account. + * + * Roles are often managed via {grantRole} and {revokeRole}: this function's + * purpose is to provide a mechanism for accounts to lose their privileges + * if they are compromised (such as when a trusted device is misplaced). + * + * If the calling account had been revoked `role`, emits a {RoleRevoked} + * event. + * + * Requirements: + * + * - the caller must be `account`. + */ + function renounceRole(bytes32 role, address account) public virtual override { + require(account == _msgSender(), "AccessControl: can only renounce roles for self"); + + _revokeRole(role, account); + } + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. Note that unlike {grantRole}, this function doesn't perform any + * checks on the calling account. + * + * [WARNING] + * ==== + * This function should only be called from the constructor when setting + * up the initial roles for the system. + * + * Using this function in any other way is effectively circumventing the admin + * system imposed by {AccessControl}. + * ==== + * + * NOTE: This function is deprecated in favor of {_grantRole}. + */ + function _setupRole(bytes32 role, address account) internal virtual { + _grantRole(role, account); + } + + /** + * @dev Sets `adminRole` as ``role``'s admin role. + * + * Emits a {RoleAdminChanged} event. + */ + function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { + bytes32 previousAdminRole = getRoleAdmin(role); + _roles[role].adminRole = adminRole; + emit RoleAdminChanged(role, previousAdminRole, adminRole); + } + + /** + * @dev Grants `role` to `account`. + * + * Internal function without access restriction. + */ + function _grantRole(bytes32 role, address account) internal virtual { + if (!hasRole(role, account)) { + _roles[role].members[account] = true; + emit RoleGranted(role, account, _msgSender()); + } + } + + /** + * @dev Revokes `role` from `account`. + * + * Internal function without access restriction. + */ + function _revokeRole(bytes32 role, address account) internal virtual { + if (hasRole(role, account)) { + _roles[role].members[account] = false; + emit RoleRevoked(role, account, _msgSender()); + } + } +} diff --git a/certora/munged/access/AccessControlEnumerable.sol b/certora/munged/access/AccessControlEnumerable.sol new file mode 100644 index 000000000..354e1bed2 --- /dev/null +++ b/certora/munged/access/AccessControlEnumerable.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) + +pragma solidity ^0.8.0; + +import "./IAccessControlEnumerable.sol"; +import "./AccessControl.sol"; +import "../utils/structs/EnumerableSet.sol"; + +/** + * @dev Extension of {AccessControl} that allows enumerating the members of each role. + */ +abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { + using EnumerableSet for EnumerableSet.AddressSet; + + mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Returns one of the accounts that have `role`. `index` must be a + * value between 0 and {getRoleMemberCount}, non-inclusive. + * + * Role bearers are not sorted in any particular way, and their ordering may + * change at any point. + * + * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure + * you perform all queries on the same block. See the following + * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] + * for more information. + */ + function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { + return _roleMembers[role].at(index); + } + + /** + * @dev Returns the number of accounts that have `role`. Can be used + * together with {getRoleMember} to enumerate all bearers of a role. + */ + function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { + return _roleMembers[role].length(); + } + + /** + * @dev Overload {_grantRole} to track enumerable memberships + */ + function _grantRole(bytes32 role, address account) internal virtual override { + super._grantRole(role, account); + _roleMembers[role].add(account); + } + + /** + * @dev Overload {_revokeRole} to track enumerable memberships + */ + function _revokeRole(bytes32 role, address account) internal virtual override { + super._revokeRole(role, account); + _roleMembers[role].remove(account); + } +} diff --git a/certora/munged/access/IAccessControl.sol b/certora/munged/access/IAccessControl.sol new file mode 100644 index 000000000..f773ecc63 --- /dev/null +++ b/certora/munged/access/IAccessControl.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) + +pragma solidity ^0.8.0; + +/** + * @dev External interface of AccessControl declared to support ERC165 detection. + */ +interface IAccessControl { + /** + * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` + * + * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite + * {RoleAdminChanged} not being emitted signaling this. + * + * _Available since v3.1._ + */ + event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); + + /** + * @dev Emitted when `account` is granted `role`. + * + * `sender` is the account that originated the contract call, an admin role + * bearer except when using {AccessControl-_setupRole}. + */ + event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + + /** + * @dev Emitted when `account` is revoked `role`. + * + * `sender` is the account that originated the contract call: + * - if using `revokeRole`, it is the admin role bearer + * - if using `renounceRole`, it is the role bearer (i.e. `account`) + */ + event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); + + /** + * @dev Returns `true` if `account` has been granted `role`. + */ + function hasRole(bytes32 role, address account) external view returns (bool); + + /** + * @dev Returns the admin role that controls `role`. See {grantRole} and + * {revokeRole}. + * + * To change a role's admin, use {AccessControl-_setRoleAdmin}. + */ + function getRoleAdmin(bytes32 role) external view returns (bytes32); + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function grantRole(bytes32 role, address account) external; + + /** + * @dev Revokes `role` from `account`. + * + * If `account` had been granted `role`, emits a {RoleRevoked} event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function revokeRole(bytes32 role, address account) external; + + /** + * @dev Revokes `role` from the calling account. + * + * Roles are often managed via {grantRole} and {revokeRole}: this function's + * purpose is to provide a mechanism for accounts to lose their privileges + * if they are compromised (such as when a trusted device is misplaced). + * + * If the calling account had been granted `role`, emits a {RoleRevoked} + * event. + * + * Requirements: + * + * - the caller must be `account`. + */ + function renounceRole(bytes32 role, address account) external; +} diff --git a/certora/munged/access/IAccessControlEnumerable.sol b/certora/munged/access/IAccessControlEnumerable.sol new file mode 100644 index 000000000..61aaf57aa --- /dev/null +++ b/certora/munged/access/IAccessControlEnumerable.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) + +pragma solidity ^0.8.0; + +import "./IAccessControl.sol"; + +/** + * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. + */ +interface IAccessControlEnumerable is IAccessControl { + /** + * @dev Returns one of the accounts that have `role`. `index` must be a + * value between 0 and {getRoleMemberCount}, non-inclusive. + * + * Role bearers are not sorted in any particular way, and their ordering may + * change at any point. + * + * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure + * you perform all queries on the same block. See the following + * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] + * for more information. + */ + function getRoleMember(bytes32 role, uint256 index) external view returns (address); + + /** + * @dev Returns the number of accounts that have `role`. Can be used + * together with {getRoleMember} to enumerate all bearers of a role. + */ + function getRoleMemberCount(bytes32 role) external view returns (uint256); +} diff --git a/certora/munged/access/Ownable.sol b/certora/munged/access/Ownable.sol new file mode 100644 index 000000000..0b2ca8e3c --- /dev/null +++ b/certora/munged/access/Ownable.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * By default, the owner account will be the one that deploys the contract. This + * can later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract Ownable is Context { + address private _owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor() { + _transferOwnership(_msgSender()); + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + return _owner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(owner() == _msgSender(), "Ownable: caller is not the owner"); + _; + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions anymore. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby removing any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _transferOwnership(address(0)); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual { + address oldOwner = _owner; + _owner = newOwner; + emit OwnershipTransferred(oldOwner, newOwner); + } +} diff --git a/certora/munged/access/README.adoc b/certora/munged/access/README.adoc new file mode 100644 index 000000000..2e84c09ad --- /dev/null +++ b/certora/munged/access/README.adoc @@ -0,0 +1,21 @@ += Access Control + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/access + +This directory provides ways to restrict who can access the functions of a contract or when they can do it. + +- {AccessControl} provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts. +- {Ownable} is a simpler mechanism with a single owner "role" that can be assigned to a single account. This simpler mechanism can be useful for quick tests but projects with production concerns are likely to outgrow it. + +== Authorization + +{{Ownable}} + +{{IAccessControl}} + +{{AccessControl}} + +{{IAccessControlEnumerable}} + +{{AccessControlEnumerable}} diff --git a/certora/munged/finance/PaymentSplitter.sol b/certora/munged/finance/PaymentSplitter.sol new file mode 100644 index 000000000..94d2ab827 --- /dev/null +++ b/certora/munged/finance/PaymentSplitter.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (finance/PaymentSplitter.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/utils/SafeERC20.sol"; +import "../utils/Address.sol"; +import "../utils/Context.sol"; + +/** + * @title PaymentSplitter + * @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware + * that the Ether will be split in this way, since it is handled transparently by the contract. + * + * The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each + * account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim + * an amount proportional to the percentage of total shares they were assigned. + * + * `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the + * accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release} + * function. + * + * NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and + * tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you + * to run tests before sending real value to this contract. + */ +contract PaymentSplitter is Context { + event PayeeAdded(address account, uint256 shares); + event PaymentReleased(address to, uint256 amount); + event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount); + event PaymentReceived(address from, uint256 amount); + + uint256 private _totalShares; + uint256 private _totalReleased; + + mapping(address => uint256) private _shares; + mapping(address => uint256) private _released; + address[] private _payees; + + mapping(IERC20 => uint256) private _erc20TotalReleased; + mapping(IERC20 => mapping(address => uint256)) private _erc20Released; + + /** + * @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at + * the matching position in the `shares` array. + * + * All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no + * duplicates in `payees`. + */ + constructor(address[] memory payees, uint256[] memory shares_) payable { + require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch"); + require(payees.length > 0, "PaymentSplitter: no payees"); + + for (uint256 i = 0; i < payees.length; i++) { + _addPayee(payees[i], shares_[i]); + } + } + + /** + * @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully + * reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the + * reliability of the events, and not the actual splitting of Ether. + * + * To learn more about this see the Solidity documentation for + * https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback + * functions]. + */ + receive() external payable virtual { + emit PaymentReceived(_msgSender(), msg.value); + } + + /** + * @dev Getter for the total shares held by payees. + */ + function totalShares() public view returns (uint256) { + return _totalShares; + } + + /** + * @dev Getter for the total amount of Ether already released. + */ + function totalReleased() public view returns (uint256) { + return _totalReleased; + } + + /** + * @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20 + * contract. + */ + function totalReleased(IERC20 token) public view returns (uint256) { + return _erc20TotalReleased[token]; + } + + /** + * @dev Getter for the amount of shares held by an account. + */ + function shares(address account) public view returns (uint256) { + return _shares[account]; + } + + /** + * @dev Getter for the amount of Ether already released to a payee. + */ + function released(address account) public view returns (uint256) { + return _released[account]; + } + + /** + * @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an + * IERC20 contract. + */ + function released(IERC20 token, address account) public view returns (uint256) { + return _erc20Released[token][account]; + } + + /** + * @dev Getter for the address of the payee number `index`. + */ + function payee(uint256 index) public view returns (address) { + return _payees[index]; + } + + /** + * @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the + * total shares and their previous withdrawals. + */ + function release(address payable account) public virtual { + require(_shares[account] > 0, "PaymentSplitter: account has no shares"); + + uint256 totalReceived = address(this).balance + totalReleased(); + uint256 payment = _pendingPayment(account, totalReceived, released(account)); + + require(payment != 0, "PaymentSplitter: account is not due payment"); + + _released[account] += payment; + _totalReleased += payment; + + Address.sendValue(account, payment); + emit PaymentReleased(account, payment); + } + + /** + * @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their + * percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20 + * contract. + */ + function release(IERC20 token, address account) public virtual { + require(_shares[account] > 0, "PaymentSplitter: account has no shares"); + + uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token); + uint256 payment = _pendingPayment(account, totalReceived, released(token, account)); + + require(payment != 0, "PaymentSplitter: account is not due payment"); + + _erc20Released[token][account] += payment; + _erc20TotalReleased[token] += payment; + + SafeERC20.safeTransfer(token, account, payment); + emit ERC20PaymentReleased(token, account, payment); + } + + /** + * @dev internal logic for computing the pending payment of an `account` given the token historical balances and + * already released amounts. + */ + function _pendingPayment( + address account, + uint256 totalReceived, + uint256 alreadyReleased + ) private view returns (uint256) { + return (totalReceived * _shares[account]) / _totalShares - alreadyReleased; + } + + /** + * @dev Add a new payee to the contract. + * @param account The address of the payee to add. + * @param shares_ The number of shares owned by the payee. + */ + function _addPayee(address account, uint256 shares_) private { + require(account != address(0), "PaymentSplitter: account is the zero address"); + require(shares_ > 0, "PaymentSplitter: shares are 0"); + require(_shares[account] == 0, "PaymentSplitter: account already has shares"); + + _payees.push(account); + _shares[account] = shares_; + _totalShares = _totalShares + shares_; + emit PayeeAdded(account, shares_); + } +} diff --git a/certora/munged/finance/README.adoc b/certora/munged/finance/README.adoc new file mode 100644 index 000000000..b64af3125 --- /dev/null +++ b/certora/munged/finance/README.adoc @@ -0,0 +1,20 @@ += Finance + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/finance + +This directory includes primitives for financial systems: + +- {PaymentSplitter} allows to split Ether and ERC20 payments among a group of accounts. The sender does not need to be + aware that the assets will be split in this way, since it is handled transparently by the contract. The split can be + in equal parts or in any other arbitrary proportion. + +- {VestingWallet} handles the vesting of Ether and ERC20 tokens for a given beneficiary. Custody of multiple tokens can + be given to this contract, which will release the token to the beneficiary following a given, customizable, vesting + schedule. + +== Contracts + +{{PaymentSplitter}} + +{{VestingWallet}} diff --git a/certora/munged/finance/VestingWallet.sol b/certora/munged/finance/VestingWallet.sol new file mode 100644 index 000000000..5ffbfcb65 --- /dev/null +++ b/certora/munged/finance/VestingWallet.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (finance/VestingWallet.sol) +pragma solidity ^0.8.0; + +import "../token/ERC20/utils/SafeERC20.sol"; +import "../utils/Address.sol"; +import "../utils/Context.sol"; +import "../utils/math/Math.sol"; + +/** + * @title VestingWallet + * @dev This contract handles the vesting of Eth and ERC20 tokens for a given beneficiary. Custody of multiple tokens + * can be given to this contract, which will release the token to the beneficiary following a given vesting schedule. + * The vesting schedule is customizable through the {vestedAmount} function. + * + * Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning. + * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly) + * be immediately releasable. + */ +contract VestingWallet is Context { + event EtherReleased(uint256 amount); + event ERC20Released(address indexed token, uint256 amount); + + uint256 private _released; + mapping(address => uint256) private _erc20Released; + address private immutable _beneficiary; + uint64 private immutable _start; + uint64 private immutable _duration; + + /** + * @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet. + */ + constructor( + address beneficiaryAddress, + uint64 startTimestamp, + uint64 durationSeconds + ) { + require(beneficiaryAddress != address(0), "VestingWallet: beneficiary is zero address"); + _beneficiary = beneficiaryAddress; + _start = startTimestamp; + _duration = durationSeconds; + } + + /** + * @dev The contract should be able to receive Eth. + */ + receive() external payable virtual {} + + /** + * @dev Getter for the beneficiary address. + */ + function beneficiary() public view virtual returns (address) { + return _beneficiary; + } + + /** + * @dev Getter for the start timestamp. + */ + function start() public view virtual returns (uint256) { + return _start; + } + + /** + * @dev Getter for the vesting duration. + */ + function duration() public view virtual returns (uint256) { + return _duration; + } + + /** + * @dev Amount of eth already released + */ + function released() public view virtual returns (uint256) { + return _released; + } + + /** + * @dev Amount of token already released + */ + function released(address token) public view virtual returns (uint256) { + return _erc20Released[token]; + } + + /** + * @dev Release the native token (ether) that have already vested. + * + * Emits a {TokensReleased} event. + */ + function release() public virtual { + uint256 releasable = vestedAmount(uint64(block.timestamp)) - released(); + _released += releasable; + emit EtherReleased(releasable); + Address.sendValue(payable(beneficiary()), releasable); + } + + /** + * @dev Release the tokens that have already vested. + * + * Emits a {TokensReleased} event. + */ + function release(address token) public virtual { + uint256 releasable = vestedAmount(token, uint64(block.timestamp)) - released(token); + _erc20Released[token] += releasable; + emit ERC20Released(token, releasable); + SafeERC20.safeTransfer(IERC20(token), beneficiary(), releasable); + } + + /** + * @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve. + */ + function vestedAmount(uint64 timestamp) public view virtual returns (uint256) { + return _vestingSchedule(address(this).balance + released(), timestamp); + } + + /** + * @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve. + */ + function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) { + return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp); + } + + /** + * @dev Virtual implementation of the vesting formula. This returns the amout vested, as a function of time, for + * an asset given its total historical allocation. + */ + function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) { + if (timestamp < start()) { + return 0; + } else if (timestamp > start() + duration()) { + return totalAllocation; + } else { + return (totalAllocation * (timestamp - start())) / duration(); + } + } +} diff --git a/certora/munged/governance/Governor.sol b/certora/munged/governance/Governor.sol new file mode 100644 index 000000000..8907d9468 --- /dev/null +++ b/certora/munged/governance/Governor.sol @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/Governor.sol) + +pragma solidity ^0.8.0; + +import "../utils/cryptography/ECDSA.sol"; +import "../utils/cryptography/draft-EIP712.sol"; +import "../utils/introspection/ERC165.sol"; +import "../utils/math/SafeCast.sol"; +import "../utils/structs/DoubleEndedQueue.sol"; +import "../utils/Address.sol"; +import "../utils/Context.sol"; +import "../utils/Timers.sol"; +import "./IGovernor.sol"; + +/** + * @dev Core of the governance system, designed to be extended though various modules. + * + * This contract is abstract and requires several function to be implemented in various modules: + * + * - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote} + * - A voting module must implement {_getVotes} + * - Additionanly, the {votingPeriod} must also be implemented + * + * _Available since v4.3._ + */ +abstract contract Governor is Context, ERC165, EIP712, IGovernor { + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + using SafeCast for uint256; + using Timers for Timers.BlockNumber; + + bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); + bytes32 public constant EXTENDED_BALLOT_TYPEHASH = + keccak256("ExtendedBallot(uint256 proposalId,uint8 support,string reason,bytes params)"); + + struct ProposalCore { + Timers.BlockNumber voteStart; + Timers.BlockNumber voteEnd; + bool executed; + bool canceled; + } + + string private _name; + + mapping(uint256 => ProposalCore) private _proposals; + + // This queue keeps track of the governor operating on itself. Calls to functions protected by the + // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, + // consummed by the {onlyGovernance} modifier and eventually reset in {_afterExecute}. This ensures that the + // execution of {onlyGovernance} protected calls can only be achieved through successful proposals. + DoubleEndedQueue.Bytes32Deque private _governanceCall; + + /** + * @dev Restricts a function so it can only be executed through governance proposals. For example, governance + * parameter setters in {GovernorSettings} are protected using this modifier. + * + * The governance executing address may be different from the Governor's own address, for example it could be a + * timelock. This can be customized by modules by overriding {_executor}. The executor is only able to invoke these + * functions during the execution of the governor's {execute} function, and not under any other circumstances. Thus, + * for example, additional timelock proposers are not able to change governance parameters without going through the + * governance protocol (since v4.6). + */ + modifier onlyGovernance() { + require(_msgSender() == _executor(), "Governor: onlyGovernance"); + if (_executor() != address(this)) { + bytes32 msgDataHash = keccak256(_msgData()); + // loop until poping the expected operation - throw if deque is empty (operation not authorized) + while (_governanceCall.popFront() != msgDataHash) {} + } + _; + } + + /** + * @dev Sets the value for {name} and {version} + */ + constructor(string memory name_) EIP712(name_, version()) { + _name = name_; + } + + /** + * @dev Function to receive ETH that will be handled by the governor (disabled if executor is a third party contract) + */ + receive() external payable virtual { + require(_executor() == address(this)); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { + // In addition to the current interfaceId, also support previous version of the interfaceId that did not + // include the castVoteWithReasonAndParams() function as standard + return + interfaceId == + (type(IGovernor).interfaceId ^ + this.castVoteWithReasonAndParams.selector ^ + this.castVoteWithReasonAndParamsBySig.selector ^ + this.getVotesWithParams.selector) || + interfaceId == type(IGovernor).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IGovernor-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IGovernor-version}. + */ + function version() public view virtual override returns (string memory) { + return "1"; + } + + /** + * @dev See {IGovernor-hashProposal}. + * + * The proposal id is produced by hashing the RLC encoded `targets` array, the `values` array, the `calldatas` array + * and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id + * can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in + * advance, before the proposal is submitted. + * + * Note that the chainId and the governor address are not part of the proposal id computation. Consequently, the + * same proposal (with same operation and same description) will have the same id if submitted on multiple governors + * accross multiple networks. This also means that in order to execute the same operation twice (on the same + * governor) the proposer will have to change the description in order to avoid proposal id conflicts. + */ + function hashProposal( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public pure virtual override returns (uint256) { + return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash))); + } + + /** + * @dev See {IGovernor-state}. + */ + function state(uint256 proposalId) public view virtual override returns (ProposalState) { + ProposalCore storage proposal = _proposals[proposalId]; + + if (proposal.executed) { + return ProposalState.Executed; + } + + if (proposal.canceled) { + return ProposalState.Canceled; + } + + uint256 snapshot = proposalSnapshot(proposalId); + + if (snapshot == 0) { + revert("Governor: unknown proposal id"); + } + + if (snapshot >= block.number) { + return ProposalState.Pending; + } + + uint256 deadline = proposalDeadline(proposalId); + + if (deadline >= block.number) { + return ProposalState.Active; + } + + if (_quorumReached(proposalId) && _voteSucceeded(proposalId)) { + return ProposalState.Succeeded; + } else { + return ProposalState.Defeated; + } + } + + /** + * @dev See {IGovernor-proposalSnapshot}. + */ + function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) { + return _proposals[proposalId].voteStart.getDeadline(); + } + + /** + * @dev See {IGovernor-proposalDeadline}. + */ + function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) { + return _proposals[proposalId].voteEnd.getDeadline(); + } + + /** + * @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_. + */ + function proposalThreshold() public view virtual returns (uint256) { + return 0; + } + + /** + * @dev Amount of votes already cast passes the threshold limit. + */ + function _quorumReached(uint256 proposalId) internal view virtual returns (bool); + + /** + * @dev Is the proposal successful or not. + */ + function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool); + + /** + * @dev Get the voting weight of `account` at a specific `blockNumber`, for a vote as described by `params`. + */ + function _getVotes( + address account, + uint256 blockNumber, + bytes memory params + ) internal view virtual returns (uint256); + + /** + * @dev Register a vote with a given support and voting weight. + * + * Note: Support is generic and can represent various things depending on the voting system used. + */ + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight, + bytes memory params + ) internal virtual; + + /** + * @dev Default additional encoded parameters used by castVote methods that don't include them + * + * Note: Should be overriden by specific implementations to use an appropriate value, the + * meaning of the additional params, in the context of that implementation + */ + function _defaultParams() internal view virtual returns (bytes memory) { + return ""; + } + + /** + * @dev See {IGovernor-propose}. + */ + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + require( + getVotes(_msgSender(), block.number - 1) >= proposalThreshold(), + "GovernorCompatibilityBravo: proposer votes below proposal threshold" + ); + + uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description))); + + require(targets.length == values.length, "Governor: invalid proposal length"); + require(targets.length == calldatas.length, "Governor: invalid proposal length"); + require(targets.length > 0, "Governor: empty proposal"); + + ProposalCore storage proposal = _proposals[proposalId]; + require(proposal.voteStart.isUnset(), "Governor: proposal already exists"); + + uint64 snapshot = block.number.toUint64() + votingDelay().toUint64(); + uint64 deadline = snapshot + votingPeriod().toUint64(); + + proposal.voteStart.setDeadline(snapshot); + proposal.voteEnd.setDeadline(deadline); + + emit ProposalCreated( + proposalId, + _msgSender(), + targets, + values, + new string[](targets.length), + calldatas, + snapshot, + deadline, + description + ); + + return proposalId; + } + + /** + * @dev See {IGovernor-execute}. + */ + function execute( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public payable virtual override returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + + ProposalState status = state(proposalId); + require( + status == ProposalState.Succeeded || status == ProposalState.Queued, + "Governor: proposal not successful" + ); + _proposals[proposalId].executed = true; + + emit ProposalExecuted(proposalId); + + _beforeExecute(proposalId, targets, values, calldatas, descriptionHash); + _execute(proposalId, targets, values, calldatas, descriptionHash); + _afterExecute(proposalId, targets, values, calldatas, descriptionHash); + + return proposalId; + } + + /** + * @dev Internal execution mechanism. Can be overriden to implement different execution mechanism + */ + function _execute( + uint256, /* proposalId */ + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 /*descriptionHash*/ + ) internal virtual { + string memory errorMessage = "Governor: call reverted without message"; + for (uint256 i = 0; i < targets.length; ++i) { + (bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]); + Address.verifyCallResult(success, returndata, errorMessage); + } + } + + /** + * @dev Hook before execution is trigerred. + */ + function _beforeExecute( + uint256, /* proposalId */ + address[] memory targets, + uint256[] memory, /* values */ + bytes[] memory calldatas, + bytes32 /*descriptionHash*/ + ) internal virtual { + if (_executor() != address(this)) { + for (uint256 i = 0; i < targets.length; ++i) { + if (targets[i] == address(this)) { + _governanceCall.pushBack(keccak256(calldatas[i])); + } + } + } + } + + /** + * @dev Hook after execution is trigerred. + */ + function _afterExecute( + uint256, /* proposalId */ + address[] memory, /* targets */ + uint256[] memory, /* values */ + bytes[] memory, /* calldatas */ + bytes32 /*descriptionHash*/ + ) internal virtual { + if (_executor() != address(this)) { + if (!_governanceCall.empty()) { + _governanceCall.clear(); + } + } + } + + /** + * @dev Internal cancel mechanism: locks up the proposal timer, preventing it from being re-submitted. Marks it as + * canceled to allow distinguishing it from executed proposals. + * + * Emits a {IGovernor-ProposalCanceled} event. + */ + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + ProposalState status = state(proposalId); + + require( + status != ProposalState.Canceled && status != ProposalState.Expired && status != ProposalState.Executed, + "Governor: proposal not active" + ); + _proposals[proposalId].canceled = true; + + emit ProposalCanceled(proposalId); + + return proposalId; + } + + /** + * @dev See {IGovernor-getVotes}. + */ + function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + return _getVotes(account, blockNumber, _defaultParams()); + } + + /** + * @dev See {IGovernor-getVotesWithParams}. + */ + function getVotesWithParams( + address account, + uint256 blockNumber, + bytes memory params + ) public view virtual override returns (uint256) { + return _getVotes(account, blockNumber, params); + } + + /** + * @dev See {IGovernor-castVote}. + */ + function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { + address voter = _msgSender(); + return _castVote(proposalId, voter, support, ""); + } + + /** + * @dev See {IGovernor-castVoteWithReason}. + */ + function castVoteWithReason( + uint256 proposalId, + uint8 support, + string calldata reason + ) public virtual override returns (uint256) { + address voter = _msgSender(); + return _castVote(proposalId, voter, support, reason); + } + + /** + * @dev See {IGovernor-castVoteWithReasonAndParams}. + */ + function castVoteWithReasonAndParams( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params + ) public virtual override returns (uint256) { + address voter = _msgSender(); + return _castVote(proposalId, voter, support, reason, params); + } + + /** + * @dev See {IGovernor-castVoteBySig}. + */ + function castVoteBySig( + uint256 proposalId, + uint8 support, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override returns (uint256) { + address voter = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))), + v, + r, + s + ); + return _castVote(proposalId, voter, support, ""); + } + + /** + * @dev See {IGovernor-castVoteWithReasonAndParamsBySig}. + */ + function castVoteWithReasonAndParamsBySig( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override returns (uint256) { + address voter = ECDSA.recover( + _hashTypedDataV4( + keccak256( + abi.encode( + EXTENDED_BALLOT_TYPEHASH, + proposalId, + support, + keccak256(bytes(reason)), + keccak256(params) + ) + ) + ), + v, + r, + s + ); + + return _castVote(proposalId, voter, support, reason, params); + } + + /** + * @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve + * voting weight using {IGovernor-getVotes} and call the {_countVote} internal function. Uses the _defaultParams(). + * + * Emits a {IGovernor-VoteCast} event. + */ + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason + ) internal virtual returns (uint256) { + return _castVote(proposalId, account, support, reason, _defaultParams()); + } + + /** + * @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve + * voting weight using {IGovernor-getVotes} and call the {_countVote} internal function. + * + * Emits a {IGovernor-VoteCast} event. + */ + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason, + bytes memory params + ) internal virtual returns (uint256) { + ProposalCore storage proposal = _proposals[proposalId]; + require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active"); + + uint256 weight = _getVotes(account, proposal.voteStart.getDeadline(), params); + _countVote(proposalId, account, support, weight, params); + + if (params.length == 0) { + emit VoteCast(account, proposalId, support, weight, reason); + } else { + emit VoteCastWithParams(account, proposalId, support, weight, reason, params); + } + + return weight; + } + + /** + * @dev Relays a transaction or function call to an arbitrary target. In cases where the governance executor + * is some contract other than the governor itself, like when using a timelock, this function can be invoked + * in a governance proposal to recover tokens or Ether that was sent to the governor contract by mistake. + * Note that if the executor is simply the governor itself, use of `relay` is redundant. + */ + function relay( + address target, + uint256 value, + bytes calldata data + ) external virtual onlyGovernance { + Address.functionCallWithValue(target, data, value); + } + + /** + * @dev Address through which the governor executes action. Will be overloaded by module that execute actions + * through another contract such as a timelock. + */ + function _executor() internal view virtual returns (address) { + return address(this); + } +} diff --git a/certora/munged/governance/IGovernor.sol b/certora/munged/governance/IGovernor.sol new file mode 100644 index 000000000..eaf443a97 --- /dev/null +++ b/certora/munged/governance/IGovernor.sol @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/IGovernor.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165.sol"; + +/** + * @dev Interface of the {Governor} core. + * + * _Available since v4.3._ + */ +abstract contract IGovernor is IERC165 { + enum ProposalState { + Pending, + Active, + Canceled, + Defeated, + Succeeded, + Queued, + Expired, + Executed + } + + /** + * @dev Emitted when a proposal is created. + */ + event ProposalCreated( + uint256 proposalId, + address proposer, + address[] targets, + uint256[] values, + string[] signatures, + bytes[] calldatas, + uint256 startBlock, + uint256 endBlock, + string description + ); + + /** + * @dev Emitted when a proposal is canceled. + */ + event ProposalCanceled(uint256 proposalId); + + /** + * @dev Emitted when a proposal is executed. + */ + event ProposalExecuted(uint256 proposalId); + + /** + * @dev Emitted when a vote is cast without params. + * + * Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used. + */ + event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason); + + /** + * @dev Emitted when a vote is cast with params. + * + * Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used. + * `params` are additional encoded parameters. Their intepepretation also depends on the voting module used. + */ + event VoteCastWithParams( + address indexed voter, + uint256 proposalId, + uint8 support, + uint256 weight, + string reason, + bytes params + ); + + /** + * @notice module:core + * @dev Name of the governor instance (used in building the ERC712 domain separator). + */ + function name() public view virtual returns (string memory); + + /** + * @notice module:core + * @dev Version of the governor instance (used in building the ERC712 domain separator). Default: "1" + */ + function version() public view virtual returns (string memory); + + /** + * @notice module:voting + * @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to + * be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of + * key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`. + * + * There are 2 standard keys: `support` and `quorum`. + * + * - `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`. + * - `quorum=bravo` means that only For votes are counted towards quorum. + * - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum. + * + * If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique + * name that describes the behavior. For example: + * + * - `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain. + * - `params=erc721` might refer to a scheme where specific NFTs are delegated to vote. + * + * NOTE: The string can be decoded by the standard + * https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`] + * JavaScript class. + */ + // solhint-disable-next-line func-name-mixedcase + function COUNTING_MODE() public pure virtual returns (string memory); + + /** + * @notice module:core + * @dev Hashing function used to (re)build the proposal id from the proposal details.. + */ + function hashProposal( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata calldatas, + bytes32 descriptionHash + ) public pure virtual returns (uint256); + + /** + * @notice module:core + * @dev Current state of a proposal, following Compound's convention + */ + function state(uint256 proposalId) public view virtual returns (ProposalState); + + /** + * @notice module:core + * @dev Block number used to retrieve user's votes and quorum. As per Compound's Comp and OpenZeppelin's + * ERC20Votes, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the + * beginning of the following block. + */ + function proposalSnapshot(uint256 proposalId) public view virtual returns (uint256); + + /** + * @notice module:core + * @dev Block number at which votes close. Votes close at the end of this block, so it is possible to cast a vote + * during this block. + */ + function proposalDeadline(uint256 proposalId) public view virtual returns (uint256); + + /** + * @notice module:user-config + * @dev Delay, in number of block, between the proposal is created and the vote starts. This can be increassed to + * leave time for users to buy voting power, of delegate it, before the voting of a proposal starts. + */ + function votingDelay() public view virtual returns (uint256); + + /** + * @notice module:user-config + * @dev Delay, in number of blocks, between the vote start and vote ends. + * + * NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting + * duration compared to the voting delay. + */ + function votingPeriod() public view virtual returns (uint256); + + /** + * @notice module:user-config + * @dev Minimum number of cast voted required for a proposal to be successful. + * + * Note: The `blockNumber` parameter corresponds to the snaphot used for counting vote. This allows to scale the + * quroum depending on values such as the totalSupply of a token at this block (see {ERC20Votes}). + */ + function quorum(uint256 blockNumber) public view virtual returns (uint256); + + /** + * @notice module:reputation + * @dev Voting power of an `account` at a specific `blockNumber`. + * + * Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or + * multiple), {ERC20Votes} tokens. + */ + function getVotes(address account, uint256 blockNumber) public view virtual returns (uint256); + + /** + * @notice module:reputation + * @dev Voting power of an `account` at a specific `blockNumber` given additional encoded parameters. + */ + function getVotesWithParams( + address account, + uint256 blockNumber, + bytes memory params + ) public view virtual returns (uint256); + + /** + * @notice module:voting + * @dev Returns weither `account` has cast a vote on `proposalId`. + */ + function hasVoted(uint256 proposalId, address account) public view virtual returns (bool); + + /** + * @dev Create a new proposal. Vote start {IGovernor-votingDelay} blocks after the proposal is created and ends + * {IGovernor-votingPeriod} blocks after the voting starts. + * + * Emits a {ProposalCreated} event. + */ + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual returns (uint256 proposalId); + + /** + * @dev Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the + * deadline to be reached. + * + * Emits a {ProposalExecuted} event. + * + * Note: some module can modify the requirements for execution, for example by adding an additional timelock. + */ + function execute( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public payable virtual returns (uint256 proposalId); + + /** + * @dev Cast a vote + * + * Emits a {VoteCast} event. + */ + function castVote(uint256 proposalId, uint8 support) public virtual returns (uint256 balance); + + /** + * @dev Cast a vote with a reason + * + * Emits a {VoteCast} event. + */ + function castVoteWithReason( + uint256 proposalId, + uint8 support, + string calldata reason + ) public virtual returns (uint256 balance); + + /** + * @dev Cast a vote with a reason and additional encoded parameters + * + * Emits a {VoteCast} event. + */ + function castVoteWithReasonAndParams( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params + ) public virtual returns (uint256 balance); + + /** + * @dev Cast a vote using the user's cryptographic signature. + * + * Emits a {VoteCast} event. + */ + function castVoteBySig( + uint256 proposalId, + uint8 support, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual returns (uint256 balance); + + /** + * @dev Cast a vote with a reason and additional encoded parameters using the user's cryptographic signature. + * + * Emits a {VoteCast} event. + */ + function castVoteWithReasonAndParamsBySig( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual returns (uint256 balance); +} diff --git a/certora/munged/governance/README.adoc b/certora/munged/governance/README.adoc new file mode 100644 index 000000000..58daf56e7 --- /dev/null +++ b/certora/munged/governance/README.adoc @@ -0,0 +1,176 @@ += Governance + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance + +This directory includes primitives for on-chain governance. + +== Governor + +This modular system of Governor contracts allows the deployment on-chain voting protocols similar to https://compound.finance/docs/governance[Compound's Governor Alpha & Bravo] and beyond, through the ability to easily customize multiple aspects of the protocol. + +[TIP] +==== +For a guided experience, set up your Governor contract using https://wizard.openzeppelin.com/#governor[Contracts Wizard]. + +For a written walkthrough, check out our guide on xref:ROOT:governance.adoc[How to set up on-chain governance]. +==== + +* {Governor}: The core contract that contains all the logic and primitives. It is abstract and requires choosing one of each of the modules below, or custom ones. + +Votes modules determine the source of voting power, and sometimes quorum number. + +* {GovernorVotes}: Extracts voting weight from an {ERC20Votes} token. + +* {GovernorVotesComp}: Extracts voting weight from a COMP-like or {ERC20VotesComp} token. + +* {GovernorVotesQuorumFraction}: Combines with `GovernorVotes` to set the quorum as a fraction of the total token supply. + +Counting modules determine valid voting options. + +* {GovernorCountingSimple}: Simple voting mechanism with 3 voting options: Against, For and Abstain. + +Timelock extensions add a delay for governance decisions to be executed. The workflow is extended to require a `queue` step before execution. With these modules, proposals are executed by the external timelock contract, thus it is the timelock that has to hold the assets that are being governed. + +* {GovernorTimelockControl}: Connects with an instance of {TimelockController}. Allows multiple proposers and executors, in addition to the Governor itself. + +* {GovernorTimelockCompound}: Connects with an instance of Compound's https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol[`Timelock`] contract. + +Other extensions can customize the behavior or interface in multiple ways. + +* {GovernorCompatibilityBravo}: Extends the interface to be fully `GovernorBravo`-compatible. Note that events are compatible regardless of whether this extension is included or not. + +* {GovernorSettings}: Manages some of the settings (voting delay, voting period duration, and proposal threshold) in a way that can be updated through a governance proposal, without requiering an upgrade. + +* {GovernorPreventLateQuorum}: Ensures there is a minimum voting period after quorum is reached as a security protection against large voters. + +In addition to modules and extensions, the core contract requires a few virtual functions to be implemented to your particular specifications: + +* <>: Delay (in number of blocks) since the proposal is submitted until voting power is fixed and voting starts. This can be used to enforce a delay after a proposal is published for users to buy tokens, or delegate their votes. +* <>: Delay (in number of blocks) since the proposal starts until voting ends. +* <>: Quorum required for a proposal to be successful. This function includes a `blockNumber` argument so the quorum can adapt through time, for example, to follow a token's `totalSupply`. + +NOTE: Functions of the `Governor` contract do not include access control. If you want to restrict access, you should add these checks by overloading the particular functions. Among these, {Governor-_cancel} is internal by default, and you will have to expose it (with the right access control mechanism) yourself if this function is needed. + +=== Core + +{{IGovernor}} + +{{Governor}} + +=== Modules + +{{GovernorCountingSimple}} + +{{GovernorVotes}} + +{{GovernorVotesQuorumFraction}} + +{{GovernorVotesComp}} + +=== Extensions + +{{GovernorTimelockControl}} + +{{GovernorTimelockCompound}} + +{{GovernorSettings}} + +{{GovernorPreventLateQuorum}} + +{{GovernorCompatibilityBravo}} + +=== Deprecated + +{{GovernorProposalThreshold}} + +== Utils + +{{Votes}} + +== Timelock + +In a governance system, the {TimelockController} contract is in charge of introducing a delay between a proposal and its execution. It can be used with or without a {Governor}. + +{{TimelockController}} + +[[timelock-terminology]] +==== Terminology + +* *Operation:* A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution (see xref:access-control.adoc#operation_lifecycle[operation lifecycle]). If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content. +* *Operation status:* +** *Unset:* An operation that is not part of the timelock mechanism. +** *Pending:* An operation that has been scheduled, before the timer expires. +** *Ready:* An operation that has been scheduled, after the timer expires. +** *Done:* An operation that has been executed. +* *Predecessor*: An (optional) dependency between operations. An operation can depend on another operation (its predecessor), forcing the execution order of these two operations. +* *Role*: +** *Admin:* An address (smart contract or EOA) that is in charge of granting the roles of Proposer and Executor. +** *Proposer:* An address (smart contract or EOA) that is in charge of scheduling (and cancelling) operations. +** *Executor:* An address (smart contract or EOA) that is in charge of executing operations once the timelock has expired. This role can be given to the zero address to allow anyone to execute operations. + +[[timelock-operation]] +==== Operation structure + +Operation executed by the xref:api:governance.adoc#TimelockController[`TimelockController`] can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations. + +Both operations contain: + +* *Target*, the address of the smart contract that the timelock should operate on. +* *Value*, in wei, that should be sent with the transaction. Most of the time this will be 0. Ether can be deposited before-end or passed along when executing the transaction. +* *Data*, containing the encoded function selector and parameters of the call. This can be produced using a number of tools. For example, a maintenance operation granting role `ROLE` to `ACCOUNT` can be encode using web3js as follows: + +```javascript +const data = timelock.contract.methods.grantRole(ROLE, ACCOUNT).encodeABI() +``` + +* *Predecessor*, that specifies a dependency between operations. This dependency is optional. Use `bytes32(0)` if the operation does not have any dependency. +* *Salt*, used to disambiguate two otherwise identical operations. This can be any random value. + +In the case of batched operations, `target`, `value` and `data` are specified as arrays, which must be of the same length. + +[[timelock-operation-lifecycle]] +==== Operation lifecycle + +Timelocked operations are identified by a unique id (their hash) and follow a specific lifecycle: + +`Unset` -> `Pending` -> `Pending` + `Ready` -> `Done` + +* By calling xref:api:governance.adoc#TimelockController-schedule-address-uint256-bytes-bytes32-bytes32-uint256-[`schedule`] (or xref:api:governance.adoc#TimelockController-scheduleBatch-address---uint256---bytes---bytes32-bytes32-uint256-[`scheduleBatch`]), a proposer moves the operation from the `Unset` to the `Pending` state. This starts a timer that must be longer than the minimum delay. The timer expires at a timestamp accessible through the xref:api:governance.adoc#TimelockController-getTimestamp-bytes32-[`getTimestamp`] method. +* Once the timer expires, the operation automatically gets the `Ready` state. At this point, it can be executed. +* By calling xref:api:governance.adoc#TimelockController-TimelockController-execute-address-uint256-bytes-bytes32-bytes32-[`execute`] (or xref:api:governance.adoc#TimelockController-executeBatch-address---uint256---bytes---bytes32-bytes32-[`executeBatch`]), an executor triggers the operation's underlying transactions and moves it to the `Done` state. If the operation has a predecessor, it has to be in the `Done` state for this transition to succeed. +* xref:api:governance.adoc#TimelockController-TimelockController-cancel-bytes32-[`cancel`] allows proposers to cancel any `Pending` operation. This resets the operation to the `Unset` state. It is thus possible for a proposer to re-schedule an operation that has been cancelled. In this case, the timer restarts when the operation is re-scheduled. + +Operations status can be queried using the functions: + +* xref:api:governance.adoc#TimelockController-isOperationPending-bytes32-[`isOperationPending(bytes32)`] +* xref:api:governance.adoc#TimelockController-isOperationReady-bytes32-[`isOperationReady(bytes32)`] +* xref:api:governance.adoc#TimelockController-isOperationDone-bytes32-[`isOperationDone(bytes32)`] + +[[timelock-roles]] +==== Roles + +[[timelock-admin]] +===== Admin + +The admins are in charge of managing proposers and executors. For the timelock to be self-governed, this role should only be given to the timelock itself. Upon deployment, both the timelock and the deployer have this role. After further configuration and testing, the deployer can renounce this role such that all further maintenance operations have to go through the timelock process. + +This role is identified by the *TIMELOCK_ADMIN_ROLE* value: `0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5` + +[[timelock-proposer]] +===== Proposer + +The proposers are in charge of scheduling (and cancelling) operations. This is a critical role, that should be given to governing entities. This could be an EOA, a multisig, or a DAO. + +WARNING: *Proposer fight:* Having multiple proposers, while providing redundancy in case one becomes unavailable, can be dangerous. As proposer have their say on all operations, they could cancel operations they disagree with, including operations to remove them for the proposers. + +This role is identified by the *PROPOSER_ROLE* value: `0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1` + +[[timelock-executor]] +===== Executor + +The executors are in charge of executing the operations scheduled by the proposers once the timelock expires. Logic dictates that multisig or DAO that are proposers should also be executors in order to guarantee operations that have been scheduled will eventually be executed. However, having additional executors can reduce the cost (the executing transaction does not require validation by the multisig or DAO that proposed it), while ensuring whoever is in charge of execution cannot trigger actions that have not been scheduled by the proposers. Alternatively, it is possible to allow _any_ address to execute a proposal once the timelock has expired by granting the executor role to the zero address. + +This role is identified by the *EXECUTOR_ROLE* value: `0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63` + +WARNING: A live contract without at least one proposer and one executor is locked. Make sure these roles are filled by reliable entities before the deployer renounces its administrative rights in favour of the timelock contract itself. See the {AccessControl} documentation to learn more about role management. diff --git a/certora/munged/governance/TimelockController.sol b/certora/munged/governance/TimelockController.sol new file mode 100644 index 000000000..c375f0744 --- /dev/null +++ b/certora/munged/governance/TimelockController.sol @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/TimelockController.sol) + +pragma solidity ^0.8.0; + +import "../access/AccessControl.sol"; + +/** + * @dev Contract module which acts as a timelocked controller. When set as the + * owner of an `Ownable` smart contract, it enforces a timelock on all + * `onlyOwner` maintenance operations. This gives time for users of the + * controlled contract to exit before a potentially dangerous maintenance + * operation is applied. + * + * By default, this contract is self administered, meaning administration tasks + * have to go through the timelock process. The proposer (resp executor) role + * is in charge of proposing (resp executing) operations. A common use case is + * to position this {TimelockController} as the owner of a smart contract, with + * a multisig or a DAO as the sole proposer. + * + * _Available since v3.3._ + */ +contract TimelockController is AccessControl { + bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); + bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); + bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); + uint256 internal constant _DONE_TIMESTAMP = uint256(1); + + mapping(bytes32 => uint256) private _timestamps; + uint256 private _minDelay; + + /** + * @dev Emitted when a call is scheduled as part of operation `id`. + */ + event CallScheduled( + bytes32 indexed id, + uint256 indexed index, + address target, + uint256 value, + bytes data, + bytes32 predecessor, + uint256 delay + ); + + /** + * @dev Emitted when a call is performed as part of operation `id`. + */ + event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data); + + /** + * @dev Emitted when operation `id` is cancelled. + */ + event Cancelled(bytes32 indexed id); + + /** + * @dev Emitted when the minimum delay for future operations is modified. + */ + event MinDelayChange(uint256 oldDuration, uint256 newDuration); + + /** + * @dev Initializes the contract with a given `minDelay`. + */ + constructor( + uint256 minDelay, + address[] memory proposers, + address[] memory executors + ) { + _setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE); + _setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE); + _setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE); + + // deployer + self administration + _setupRole(TIMELOCK_ADMIN_ROLE, _msgSender()); + _setupRole(TIMELOCK_ADMIN_ROLE, address(this)); + + // register proposers + for (uint256 i = 0; i < proposers.length; ++i) { + _setupRole(PROPOSER_ROLE, proposers[i]); + } + + // register executors + for (uint256 i = 0; i < executors.length; ++i) { + _setupRole(EXECUTOR_ROLE, executors[i]); + } + + _minDelay = minDelay; + emit MinDelayChange(0, minDelay); + } + + /** + * @dev Modifier to make a function callable only by a certain role. In + * addition to checking the sender's role, `address(0)` 's role is also + * considered. Granting a role to `address(0)` is equivalent to enabling + * this role for everyone. + */ + modifier onlyRoleOrOpenRole(bytes32 role) { + if (!hasRole(role, address(0))) { + _checkRole(role, _msgSender()); + } + _; + } + + /** + * @dev Contract might receive/hold ETH as part of the maintenance process. + */ + receive() external payable {} + + /** + * @dev Returns whether an id correspond to a registered operation. This + * includes both Pending, Ready and Done operations. + */ + function isOperation(bytes32 id) public view virtual returns (bool pending) { + return getTimestamp(id) > 0; + } + + /** + * @dev Returns whether an operation is pending or not. + */ + function isOperationPending(bytes32 id) public view virtual returns (bool pending) { + return getTimestamp(id) > _DONE_TIMESTAMP; + } + + /** + * @dev Returns whether an operation is ready or not. + */ + function isOperationReady(bytes32 id) public view virtual returns (bool ready) { + uint256 timestamp = getTimestamp(id); + return timestamp > _DONE_TIMESTAMP && timestamp <= block.timestamp; + } + + /** + * @dev Returns whether an operation is done or not. + */ + function isOperationDone(bytes32 id) public view virtual returns (bool done) { + return getTimestamp(id) == _DONE_TIMESTAMP; + } + + /** + * @dev Returns the timestamp at with an operation becomes ready (0 for + * unset operations, 1 for done operations). + */ + function getTimestamp(bytes32 id) public view virtual returns (uint256 timestamp) { + return _timestamps[id]; + } + + /** + * @dev Returns the minimum delay for an operation to become valid. + * + * This value can be changed by executing an operation that calls `updateDelay`. + */ + function getMinDelay() public view virtual returns (uint256 duration) { + return _minDelay; + } + + /** + * @dev Returns the identifier of an operation containing a single + * transaction. + */ + function hashOperation( + address target, + uint256 value, + bytes calldata data, + bytes32 predecessor, + bytes32 salt + ) public pure virtual returns (bytes32 hash) { + return keccak256(abi.encode(target, value, data, predecessor, salt)); + } + + /** + * @dev Returns the identifier of an operation containing a batch of + * transactions. + */ + function hashOperationBatch( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata datas, + bytes32 predecessor, + bytes32 salt + ) public pure virtual returns (bytes32 hash) { + return keccak256(abi.encode(targets, values, datas, predecessor, salt)); + } + + /** + * @dev Schedule an operation containing a single transaction. + * + * Emits a {CallScheduled} event. + * + * Requirements: + * + * - the caller must have the 'proposer' role. + */ + function schedule( + address target, + uint256 value, + bytes calldata data, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ) public virtual onlyRole(PROPOSER_ROLE) { + bytes32 id = hashOperation(target, value, data, predecessor, salt); + _schedule(id, delay); + emit CallScheduled(id, 0, target, value, data, predecessor, delay); + } + + /** + * @dev Schedule an operation containing a batch of transactions. + * + * Emits one {CallScheduled} event per transaction in the batch. + * + * Requirements: + * + * - the caller must have the 'proposer' role. + */ + function scheduleBatch( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata datas, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ) public virtual onlyRole(PROPOSER_ROLE) { + require(targets.length == values.length, "TimelockController: length mismatch"); + require(targets.length == datas.length, "TimelockController: length mismatch"); + + bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt); + _schedule(id, delay); + for (uint256 i = 0; i < targets.length; ++i) { + emit CallScheduled(id, i, targets[i], values[i], datas[i], predecessor, delay); + } + } + + /** + * @dev Schedule an operation that is to becomes valid after a given delay. + */ + function _schedule(bytes32 id, uint256 delay) private { + require(!isOperation(id), "TimelockController: operation already scheduled"); + require(delay >= getMinDelay(), "TimelockController: insufficient delay"); + _timestamps[id] = block.timestamp + delay; + } + + /** + * @dev Cancel an operation. + * + * Requirements: + * + * - the caller must have the 'proposer' role. + */ + function cancel(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) { + require(isOperationPending(id), "TimelockController: operation cannot be cancelled"); + delete _timestamps[id]; + + emit Cancelled(id); + } + + /** + * @dev Execute an (ready) operation containing a single transaction. + * + * Emits a {CallExecuted} event. + * + * Requirements: + * + * - the caller must have the 'executor' role. + */ + // This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending, + // thus any modifications to the operation during reentrancy should be caught. + // slither-disable-next-line reentrancy-eth + function execute( + address target, + uint256 value, + bytes calldata data, + bytes32 predecessor, + bytes32 salt + ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) { + bytes32 id = hashOperation(target, value, data, predecessor, salt); + _beforeCall(id, predecessor); + _call(id, 0, target, value, data); + _afterCall(id); + } + + /** + * @dev Execute an (ready) operation containing a batch of transactions. + * + * Emits one {CallExecuted} event per transaction in the batch. + * + * Requirements: + * + * - the caller must have the 'executor' role. + */ + function executeBatch( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata datas, + bytes32 predecessor, + bytes32 salt + ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) { + require(targets.length == values.length, "TimelockController: length mismatch"); + require(targets.length == datas.length, "TimelockController: length mismatch"); + + bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt); + _beforeCall(id, predecessor); + for (uint256 i = 0; i < targets.length; ++i) { + _call(id, i, targets[i], values[i], datas[i]); + } + _afterCall(id); + } + + /** + * @dev Checks before execution of an operation's calls. + */ + function _beforeCall(bytes32 id, bytes32 predecessor) private view { + require(isOperationReady(id), "TimelockController: operation is not ready"); + require(predecessor == bytes32(0) || isOperationDone(predecessor), "TimelockController: missing dependency"); + } + + /** + * @dev Checks after execution of an operation's calls. + */ + function _afterCall(bytes32 id) private { + require(isOperationReady(id), "TimelockController: operation is not ready"); + _timestamps[id] = _DONE_TIMESTAMP; + } + + /** + * @dev Execute an operation's call. + * + * Emits a {CallExecuted} event. + */ + function _call( + bytes32 id, + uint256 index, + address target, + uint256 value, + bytes calldata data + ) private { + (bool success, ) = target.call{value: value}(data); + require(success, "TimelockController: underlying transaction reverted"); + + emit CallExecuted(id, index, target, value, data); + } + + /** + * @dev Changes the minimum timelock duration for future operations. + * + * Emits a {MinDelayChange} event. + * + * Requirements: + * + * - the caller must be the timelock itself. This can only be achieved by scheduling and later executing + * an operation where the timelock is the target and the data is the ABI-encoded call to this function. + */ + function updateDelay(uint256 newDelay) external virtual { + require(msg.sender == address(this), "TimelockController: caller must be timelock"); + emit MinDelayChange(_minDelay, newDelay); + _minDelay = newDelay; + } +} diff --git a/certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol b/certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol new file mode 100644 index 000000000..1d639f436 --- /dev/null +++ b/certora/munged/governance/compatibility/GovernorCompatibilityBravo.sol @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/compatibility/GovernorCompatibilityBravo.sol) + +pragma solidity ^0.8.0; + +import "../../utils/Counters.sol"; +import "../../utils/math/SafeCast.sol"; +import "../extensions/IGovernorTimelock.sol"; +import "../Governor.sol"; +import "./IGovernorCompatibilityBravo.sol"; + +/** + * @dev Compatibility layer that implements GovernorBravo compatibility on to of {Governor}. + * + * This compatibility layer includes a voting system and requires a {IGovernorTimelock} compatible module to be added + * through inheritance. It does not include token bindings, not does it include any variable upgrade patterns. + * + * NOTE: When using this module, you may need to enable the Solidity optimizer to avoid hitting the contract size limit. + * + * _Available since v4.3._ + */ +abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorCompatibilityBravo, Governor { + using Counters for Counters.Counter; + using Timers for Timers.BlockNumber; + + enum VoteType { + Against, + For, + Abstain + } + + struct ProposalDetails { + address proposer; + address[] targets; + uint256[] values; + string[] signatures; + bytes[] calldatas; + uint256 forVotes; + uint256 againstVotes; + uint256 abstainVotes; + mapping(address => Receipt) receipts; + bytes32 descriptionHash; + } + + mapping(uint256 => ProposalDetails) private _proposalDetails; + + // solhint-disable-next-line func-name-mixedcase + function COUNTING_MODE() public pure virtual override returns (string memory) { + return "support=bravo&quorum=bravo"; + } + + // ============================================== Proposal lifecycle ============================================== + /** + * @dev See {IGovernor-propose}. + */ + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override(IGovernor, Governor) returns (uint256) { + _storeProposal(_msgSender(), targets, values, new string[](calldatas.length), calldatas, description); + return super.propose(targets, values, calldatas, description); + } + + /** + * @dev See {IGovernorCompatibilityBravo-propose}. + */ + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + _storeProposal(_msgSender(), targets, values, signatures, calldatas, description); + return propose(targets, values, _encodeCalldata(signatures, calldatas), description); + } + + /** + * @dev See {IGovernorCompatibilityBravo-queue}. + */ + function queue(uint256 proposalId) public virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + queue( + details.targets, + details.values, + _encodeCalldata(details.signatures, details.calldatas), + details.descriptionHash + ); + } + + /** + * @dev See {IGovernorCompatibilityBravo-execute}. + */ + function execute(uint256 proposalId) public payable virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + execute( + details.targets, + details.values, + _encodeCalldata(details.signatures, details.calldatas), + details.descriptionHash + ); + } + + function cancel(uint256 proposalId) public virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + + require( + _msgSender() == details.proposer || getVotes(details.proposer, block.number - 1) < proposalThreshold(), + "GovernorBravo: proposer above threshold" + ); + + _cancel( + details.targets, + details.values, + _encodeCalldata(details.signatures, details.calldatas), + details.descriptionHash + ); + } + + /** + * @dev Encodes calldatas with optional function signature. + */ + function _encodeCalldata(string[] memory signatures, bytes[] memory calldatas) + private + pure + returns (bytes[] memory) + { + bytes[] memory fullcalldatas = new bytes[](calldatas.length); + + for (uint256 i = 0; i < signatures.length; ++i) { + fullcalldatas[i] = bytes(signatures[i]).length == 0 + ? calldatas[i] + : abi.encodePacked(bytes4(keccak256(bytes(signatures[i]))), calldatas[i]); + } + + return fullcalldatas; + } + + /** + * @dev Store proposal metadata for later lookup + */ + function _storeProposal( + address proposer, + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) private { + bytes32 descriptionHash = keccak256(bytes(description)); + uint256 proposalId = hashProposal(targets, values, _encodeCalldata(signatures, calldatas), descriptionHash); + + ProposalDetails storage details = _proposalDetails[proposalId]; + if (details.descriptionHash == bytes32(0)) { + details.proposer = proposer; + details.targets = targets; + details.values = values; + details.signatures = signatures; + details.calldatas = calldatas; + details.descriptionHash = descriptionHash; + } + } + + // ==================================================== Views ===================================================== + /** + * @dev See {IGovernorCompatibilityBravo-proposals}. + */ + function proposals(uint256 proposalId) + public + view + virtual + override + returns ( + uint256 id, + address proposer, + uint256 eta, + uint256 startBlock, + uint256 endBlock, + uint256 forVotes, + uint256 againstVotes, + uint256 abstainVotes, + bool canceled, + bool executed + ) + { + id = proposalId; + eta = proposalEta(proposalId); + startBlock = proposalSnapshot(proposalId); + endBlock = proposalDeadline(proposalId); + + ProposalDetails storage details = _proposalDetails[proposalId]; + proposer = details.proposer; + forVotes = details.forVotes; + againstVotes = details.againstVotes; + abstainVotes = details.abstainVotes; + + ProposalState status = state(proposalId); + canceled = status == ProposalState.Canceled; + executed = status == ProposalState.Executed; + } + + /** + * @dev See {IGovernorCompatibilityBravo-getActions}. + */ + function getActions(uint256 proposalId) + public + view + virtual + override + returns ( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas + ) + { + ProposalDetails storage details = _proposalDetails[proposalId]; + return (details.targets, details.values, details.signatures, details.calldatas); + } + + /** + * @dev See {IGovernorCompatibilityBravo-getReceipt}. + */ + function getReceipt(uint256 proposalId, address voter) public view virtual override returns (Receipt memory) { + return _proposalDetails[proposalId].receipts[voter]; + } + + /** + * @dev See {IGovernorCompatibilityBravo-quorumVotes}. + */ + function quorumVotes() public view virtual override returns (uint256) { + return quorum(block.number - 1); + } + + // ==================================================== Voting ==================================================== + /** + * @dev See {IGovernor-hasVoted}. + */ + function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) { + return _proposalDetails[proposalId].receipts[account].hasVoted; + } + + /** + * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. + */ + function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { + ProposalDetails storage details = _proposalDetails[proposalId]; + return quorum(proposalSnapshot(proposalId)) <= details.forVotes; + } + + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be scritly over the againstVotes. + */ + function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { + ProposalDetails storage details = _proposalDetails[proposalId]; + return details.forVotes > details.againstVotes; + } + + /** + * @dev See {Governor-_countVote}. In this module, the support follows Governor Bravo. + */ + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight, + bytes memory // params + ) internal virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + Receipt storage receipt = details.receipts[account]; + + require(!receipt.hasVoted, "GovernorCompatibilityBravo: vote already cast"); + receipt.hasVoted = true; + receipt.support = support; + receipt.votes = SafeCast.toUint96(weight); + + if (support == uint8(VoteType.Against)) { + details.againstVotes += weight; + } else if (support == uint8(VoteType.For)) { + details.forVotes += weight; + } else if (support == uint8(VoteType.Abstain)) { + details.abstainVotes += weight; + } else { + revert("GovernorCompatibilityBravo: invalid vote type"); + } + } +} diff --git a/certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol b/certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol new file mode 100644 index 000000000..83e4e1ae9 --- /dev/null +++ b/certora/munged/governance/compatibility/IGovernorCompatibilityBravo.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/compatibility/IGovernorCompatibilityBravo.sol) + +pragma solidity ^0.8.0; + +import "../IGovernor.sol"; + +/** + * @dev Interface extension that adds missing functions to the {Governor} core to provide `GovernorBravo` compatibility. + * + * _Available since v4.3._ + */ +abstract contract IGovernorCompatibilityBravo is IGovernor { + /** + * @dev Proposal structure from Compound Governor Bravo. Not actually used by the compatibility layer, as + * {{proposal}} returns a very different structure. + */ + struct Proposal { + uint256 id; + address proposer; + uint256 eta; + address[] targets; + uint256[] values; + string[] signatures; + bytes[] calldatas; + uint256 startBlock; + uint256 endBlock; + uint256 forVotes; + uint256 againstVotes; + uint256 abstainVotes; + bool canceled; + bool executed; + mapping(address => Receipt) receipts; + } + + /** + * @dev Receipt structure from Compound Governor Bravo + */ + struct Receipt { + bool hasVoted; + uint8 support; + uint96 votes; + } + + /** + * @dev Part of the Governor Bravo's interface. + */ + function quorumVotes() public view virtual returns (uint256); + + /** + * @dev Part of the Governor Bravo's interface: _"The official record of all proposals ever proposed"_. + */ + function proposals(uint256) + public + view + virtual + returns ( + uint256 id, + address proposer, + uint256 eta, + uint256 startBlock, + uint256 endBlock, + uint256 forVotes, + uint256 againstVotes, + uint256 abstainVotes, + bool canceled, + bool executed + ); + + /** + * @dev Part of the Governor Bravo's interface: _"Function used to propose a new proposal"_. + */ + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) public virtual returns (uint256); + + /** + * @dev Part of the Governor Bravo's interface: _"Queues a proposal of state succeeded"_. + */ + function queue(uint256 proposalId) public virtual; + + /** + * @dev Part of the Governor Bravo's interface: _"Executes a queued proposal if eta has passed"_. + */ + function execute(uint256 proposalId) public payable virtual; + + /** + * @dev Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold. + */ + function cancel(uint256 proposalId) public virtual; + + /** + * @dev Part of the Governor Bravo's interface: _"Gets actions of a proposal"_. + */ + function getActions(uint256 proposalId) + public + view + virtual + returns ( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas + ); + + /** + * @dev Part of the Governor Bravo's interface: _"Gets the receipt for a voter on a given proposal"_. + */ + function getReceipt(uint256 proposalId, address voter) public view virtual returns (Receipt memory); +} diff --git a/certora/munged/governance/extensions/GovernorCountingSimple.sol b/certora/munged/governance/extensions/GovernorCountingSimple.sol new file mode 100644 index 000000000..a262ce4d8 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorCountingSimple.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorCountingSimple.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; + +/** + * @dev Extension of {Governor} for simple, 3 options, vote counting. + * + * _Available since v4.3._ + */ +abstract contract GovernorCountingSimple is Governor { + /** + * @dev Supported vote types. Matches Governor Bravo ordering. + */ + enum VoteType { + Against, + For, + Abstain + } + + struct ProposalVote { + uint256 againstVotes; + uint256 forVotes; + uint256 abstainVotes; + mapping(address => bool) hasVoted; + } + + mapping(uint256 => ProposalVote) private _proposalVotes; + + /** + * @dev See {IGovernor-COUNTING_MODE}. + */ + // solhint-disable-next-line func-name-mixedcase + function COUNTING_MODE() public pure virtual override returns (string memory) { + return "support=bravo&quorum=for,abstain"; + } + + /** + * @dev See {IGovernor-hasVoted}. + */ + function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) { + return _proposalVotes[proposalId].hasVoted[account]; + } + + /** + * @dev Accessor to the internal vote counts. + */ + function proposalVotes(uint256 proposalId) + public + view + virtual + returns ( + uint256 againstVotes, + uint256 forVotes, + uint256 abstainVotes + ) + { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + return (proposalvote.againstVotes, proposalvote.forVotes, proposalvote.abstainVotes); + } + + /** + * @dev See {Governor-_quorumReached}. + */ + function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + + return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes; + } + + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. + */ + function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + + return proposalvote.forVotes > proposalvote.againstVotes; + } + + /** + * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo). + */ + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight, + bytes memory // params + ) internal virtual override { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + + require(!proposalvote.hasVoted[account], "GovernorVotingSimple: vote already cast"); + proposalvote.hasVoted[account] = true; + + if (support == uint8(VoteType.Against)) { + proposalvote.againstVotes += weight; + } else if (support == uint8(VoteType.For)) { + proposalvote.forVotes += weight; + } else if (support == uint8(VoteType.Abstain)) { + proposalvote.abstainVotes += weight; + } else { + revert("GovernorVotingSimple: invalid value for enum VoteType"); + } + } +} diff --git a/certora/munged/governance/extensions/GovernorPreventLateQuorum.sol b/certora/munged/governance/extensions/GovernorPreventLateQuorum.sol new file mode 100644 index 000000000..5b58f6032 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorPreventLateQuorum.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorPreventLateQuorum.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; +import "../../utils/math/Math.sol"; + +/** + * @dev A module that ensures there is a minimum voting period after quorum is reached. This prevents a large voter from + * swaying a vote and triggering quorum at the last minute, by ensuring there is always time for other voters to react + * and try to oppose the decision. + * + * If a vote causes quorum to be reached, the proposal's voting period may be extended so that it does not end before at + * least a given number of blocks have passed (the "vote extension" parameter). This parameter can be set by the + * governance executor (e.g. through a governance proposal). + * + * _Available since v4.5._ + */ +abstract contract GovernorPreventLateQuorum is Governor { + using SafeCast for uint256; + using Timers for Timers.BlockNumber; + + uint64 private _voteExtension; + mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines; + + /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. + event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); + + /// @dev Emitted when the {lateQuorumVoteExtension} parameter is changed. + event LateQuorumVoteExtensionSet(uint64 oldVoteExtension, uint64 newVoteExtension); + + /** + * @dev Initializes the vote extension parameter: the number of blocks that are required to pass since a proposal + * reaches quorum until its voting period ends. If necessary the voting period will be extended beyond the one set + * at proposal creation. + */ + constructor(uint64 initialVoteExtension) { + _setLateQuorumVoteExtension(initialVoteExtension); + } + + /** + * @dev Returns the proposal deadline, which may have been extended beyond that set at proposal creation, if the + * proposal reached quorum late in the voting period. See {Governor-proposalDeadline}. + */ + function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) { + return Math.max(super.proposalDeadline(proposalId), _extendedDeadlines[proposalId].getDeadline()); + } + + /** + * @dev Casts a vote and detects if it caused quorum to be reached, potentially extending the voting period. See + * {Governor-_castVote}. + * + * May emit a {ProposalExtended} event. + */ + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason, + bytes memory params + ) internal virtual override returns (uint256) { + uint256 result = super._castVote(proposalId, account, support, reason, params); + + Timers.BlockNumber storage extendedDeadline = _extendedDeadlines[proposalId]; + + if (extendedDeadline.isUnset() && _quorumReached(proposalId)) { + uint64 extendedDeadlineValue = block.number.toUint64() + lateQuorumVoteExtension(); + + if (extendedDeadlineValue > proposalDeadline(proposalId)) { + emit ProposalExtended(proposalId, extendedDeadlineValue); + } + + extendedDeadline.setDeadline(extendedDeadlineValue); + } + + return result; + } + + /** + * @dev Returns the current value of the vote extension parameter: the number of blocks that are required to pass + * from the time a proposal reaches quorum until its voting period ends. + */ + function lateQuorumVoteExtension() public view virtual returns (uint64) { + return _voteExtension; + } + + /** + * @dev Changes the {lateQuorumVoteExtension}. This operation can only be performed by the governance executor, + * generally through a governance proposal. + * + * Emits a {LateQuorumVoteExtensionSet} event. + */ + function setLateQuorumVoteExtension(uint64 newVoteExtension) public virtual onlyGovernance { + _setLateQuorumVoteExtension(newVoteExtension); + } + + /** + * @dev Changes the {lateQuorumVoteExtension}. This is an internal function that can be exposed in a public function + * like {setLateQuorumVoteExtension} if another access control mechanism is needed. + * + * Emits a {LateQuorumVoteExtensionSet} event. + */ + function _setLateQuorumVoteExtension(uint64 newVoteExtension) internal virtual { + emit LateQuorumVoteExtensionSet(_voteExtension, newVoteExtension); + _voteExtension = newVoteExtension; + } +} diff --git a/certora/munged/governance/extensions/GovernorProposalThreshold.sol b/certora/munged/governance/extensions/GovernorProposalThreshold.sol new file mode 100644 index 000000000..3feebace0 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorProposalThreshold.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorProposalThreshold.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; + +/** + * @dev Extension of {Governor} for proposal restriction to token holders with a minimum balance. + * + * _Available since v4.3._ + * _Deprecated since v4.4._ + */ +abstract contract GovernorProposalThreshold is Governor { + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + return super.propose(targets, values, calldatas, description); + } +} diff --git a/certora/munged/governance/extensions/GovernorSettings.sol b/certora/munged/governance/extensions/GovernorSettings.sol new file mode 100644 index 000000000..a3187c6e1 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorSettings.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorSettings.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; + +/** + * @dev Extension of {Governor} for settings updatable through governance. + * + * _Available since v4.4._ + */ +abstract contract GovernorSettings is Governor { + uint256 private _votingDelay; + uint256 private _votingPeriod; + uint256 private _proposalThreshold; + + event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay); + event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod); + event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold); + + /** + * @dev Initialize the governance parameters. + */ + constructor( + uint256 initialVotingDelay, + uint256 initialVotingPeriod, + uint256 initialProposalThreshold + ) { + _setVotingDelay(initialVotingDelay); + _setVotingPeriod(initialVotingPeriod); + _setProposalThreshold(initialProposalThreshold); + } + + /** + * @dev See {IGovernor-votingDelay}. + */ + function votingDelay() public view virtual override returns (uint256) { + return _votingDelay; + } + + /** + * @dev See {IGovernor-votingPeriod}. + */ + function votingPeriod() public view virtual override returns (uint256) { + return _votingPeriod; + } + + /** + * @dev See {Governor-proposalThreshold}. + */ + function proposalThreshold() public view virtual override returns (uint256) { + return _proposalThreshold; + } + + /** + * @dev Update the voting delay. This operation can only be performed through a governance proposal. + * + * Emits a {VotingDelaySet} event. + */ + function setVotingDelay(uint256 newVotingDelay) public virtual onlyGovernance { + _setVotingDelay(newVotingDelay); + } + + /** + * @dev Update the voting period. This operation can only be performed through a governance proposal. + * + * Emits a {VotingPeriodSet} event. + */ + function setVotingPeriod(uint256 newVotingPeriod) public virtual onlyGovernance { + _setVotingPeriod(newVotingPeriod); + } + + /** + * @dev Update the proposal threshold. This operation can only be performed through a governance proposal. + * + * Emits a {ProposalThresholdSet} event. + */ + function setProposalThreshold(uint256 newProposalThreshold) public virtual onlyGovernance { + _setProposalThreshold(newProposalThreshold); + } + + /** + * @dev Internal setter for the voting delay. + * + * Emits a {VotingDelaySet} event. + */ + function _setVotingDelay(uint256 newVotingDelay) internal virtual { + emit VotingDelaySet(_votingDelay, newVotingDelay); + _votingDelay = newVotingDelay; + } + + /** + * @dev Internal setter for the voting period. + * + * Emits a {VotingPeriodSet} event. + */ + function _setVotingPeriod(uint256 newVotingPeriod) internal virtual { + // voting period must be at least one block long + require(newVotingPeriod > 0, "GovernorSettings: voting period too low"); + emit VotingPeriodSet(_votingPeriod, newVotingPeriod); + _votingPeriod = newVotingPeriod; + } + + /** + * @dev Internal setter for the proposal threshold. + * + * Emits a {ProposalThresholdSet} event. + */ + function _setProposalThreshold(uint256 newProposalThreshold) internal virtual { + emit ProposalThresholdSet(_proposalThreshold, newProposalThreshold); + _proposalThreshold = newProposalThreshold; + } +} diff --git a/certora/munged/governance/extensions/GovernorTimelockCompound.sol b/certora/munged/governance/extensions/GovernorTimelockCompound.sol new file mode 100644 index 000000000..99aea98ba --- /dev/null +++ b/certora/munged/governance/extensions/GovernorTimelockCompound.sol @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorTimelockCompound.sol) + +pragma solidity ^0.8.0; + +import "./IGovernorTimelock.sol"; +import "../Governor.sol"; +import "../../utils/math/SafeCast.sol"; + +/** + * https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol[Compound's timelock] interface + */ +interface ICompoundTimelock { + receive() external payable; + + // solhint-disable-next-line func-name-mixedcase + function GRACE_PERIOD() external view returns (uint256); + + // solhint-disable-next-line func-name-mixedcase + function MINIMUM_DELAY() external view returns (uint256); + + // solhint-disable-next-line func-name-mixedcase + function MAXIMUM_DELAY() external view returns (uint256); + + function admin() external view returns (address); + + function pendingAdmin() external view returns (address); + + function delay() external view returns (uint256); + + function queuedTransactions(bytes32) external view returns (bool); + + function setDelay(uint256) external; + + function acceptAdmin() external; + + function setPendingAdmin(address) external; + + function queueTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) external returns (bytes32); + + function cancelTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) external; + + function executeTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) external payable returns (bytes memory); +} + +/** + * @dev Extension of {Governor} that binds the execution process to a Compound Timelock. This adds a delay, enforced by + * the external timelock to all successful proposal (in addition to the voting duration). The {Governor} needs to be + * the admin of the timelock for any operation to be performed. A public, unrestricted, + * {GovernorTimelockCompound-__acceptAdmin} is available to accept ownership of the timelock. + * + * Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus, + * the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be + * inaccessible. + * + * _Available since v4.3._ + */ +abstract contract GovernorTimelockCompound is IGovernorTimelock, Governor { + using SafeCast for uint256; + using Timers for Timers.Timestamp; + + struct ProposalTimelock { + Timers.Timestamp timer; + } + + ICompoundTimelock private _timelock; + + mapping(uint256 => ProposalTimelock) private _proposalTimelocks; + + /** + * @dev Emitted when the timelock controller used for proposal execution is modified. + */ + event TimelockChange(address oldTimelock, address newTimelock); + + /** + * @dev Set the timelock. + */ + constructor(ICompoundTimelock timelockAddress) { + _updateTimelock(timelockAddress); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, Governor) returns (bool) { + return interfaceId == type(IGovernorTimelock).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Overriden version of the {Governor-state} function with added support for the `Queued` and `Expired` status. + */ + function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) { + ProposalState status = super.state(proposalId); + + if (status != ProposalState.Succeeded) { + return status; + } + + uint256 eta = proposalEta(proposalId); + if (eta == 0) { + return status; + } else if (block.timestamp >= eta + _timelock.GRACE_PERIOD()) { + return ProposalState.Expired; + } else { + return ProposalState.Queued; + } + } + + /** + * @dev Public accessor to check the address of the timelock + */ + function timelock() public view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Public accessor to check the eta of a queued proposal + */ + function proposalEta(uint256 proposalId) public view virtual override returns (uint256) { + return _proposalTimelocks[proposalId].timer.getDeadline(); + } + + /** + * @dev Function to queue a proposal to the timelock. + */ + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public virtual override returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + + require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful"); + + uint256 eta = block.timestamp + _timelock.delay(); + _proposalTimelocks[proposalId].timer.setDeadline(eta.toUint64()); + for (uint256 i = 0; i < targets.length; ++i) { + require( + !_timelock.queuedTransactions(keccak256(abi.encode(targets[i], values[i], "", calldatas[i], eta))), + "GovernorTimelockCompound: identical proposal action already queued" + ); + _timelock.queueTransaction(targets[i], values[i], "", calldatas[i], eta); + } + + emit ProposalQueued(proposalId, eta); + + return proposalId; + } + + /** + * @dev Overriden execute function that run the already queued proposal through the timelock. + */ + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 /*descriptionHash*/ + ) internal virtual override { + uint256 eta = proposalEta(proposalId); + require(eta > 0, "GovernorTimelockCompound: proposal not yet queued"); + Address.sendValue(payable(_timelock), msg.value); + for (uint256 i = 0; i < targets.length; ++i) { + _timelock.executeTransaction(targets[i], values[i], "", calldatas[i], eta); + } + } + + /** + * @dev Overriden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already + * been queued. + */ + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override returns (uint256) { + uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash); + + uint256 eta = proposalEta(proposalId); + if (eta > 0) { + for (uint256 i = 0; i < targets.length; ++i) { + _timelock.cancelTransaction(targets[i], values[i], "", calldatas[i], eta); + } + _proposalTimelocks[proposalId].timer.reset(); + } + + return proposalId; + } + + /** + * @dev Address through which the governor executes action. In this case, the timelock. + */ + function _executor() internal view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Accept admin right over the timelock. + */ + // solhint-disable-next-line private-vars-leading-underscore + function __acceptAdmin() public { + _timelock.acceptAdmin(); + } + + /** + * @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates + * must be proposed, scheduled, and executed through governance proposals. + * + * For security reasons, the timelock must be handed over to another admin before setting up a new one. The two + * operations (hand over the timelock) and do the update can be batched in a single proposal. + * + * Note that if the timelock admin has been handed over in a previous operation, we refuse updates made through the + * timelock if admin of the timelock has already been accepted and the operation is executed outside the scope of + * governance. + + * CAUTION: It is not recommended to change the timelock while there are other queued governance proposals. + */ + function updateTimelock(ICompoundTimelock newTimelock) external virtual onlyGovernance { + _updateTimelock(newTimelock); + } + + function _updateTimelock(ICompoundTimelock newTimelock) private { + emit TimelockChange(address(_timelock), address(newTimelock)); + _timelock = newTimelock; + } +} diff --git a/certora/munged/governance/extensions/GovernorTimelockControl.sol b/certora/munged/governance/extensions/GovernorTimelockControl.sol new file mode 100644 index 000000000..2b3022324 --- /dev/null +++ b/certora/munged/governance/extensions/GovernorTimelockControl.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorTimelockControl.sol) + +pragma solidity ^0.8.0; + +import "./IGovernorTimelock.sol"; +import "../Governor.sol"; +import "../TimelockController.sol"; + +/** + * @dev Extension of {Governor} that binds the execution process to an instance of {TimelockController}. This adds a + * delay, enforced by the {TimelockController} to all successful proposal (in addition to the voting duration). The + * {Governor} needs the proposer (and ideally the executor) roles for the {Governor} to work properly. + * + * Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus, + * the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be + * inaccessible. + * + * WARNING: Setting up the TimelockController to have additional proposers besides the governor is very risky, as it + * grants them powers that they must be trusted or known not to use: 1) {onlyGovernance} functions like {relay} are + * available to them through the timelock, and 2) approved governance proposals can be blocked by them, effectively + * executing a Denial of Service attack. This risk will be mitigated in a future release. + * + * _Available since v4.3._ + */ +abstract contract GovernorTimelockControl is IGovernorTimelock, Governor { + TimelockController private _timelock; + mapping(uint256 => bytes32) private _timelockIds; + + /** + * @dev Emitted when the timelock controller used for proposal execution is modified. + */ + event TimelockChange(address oldTimelock, address newTimelock); + + /** + * @dev Set the timelock. + */ + constructor(TimelockController timelockAddress) { + _updateTimelock(timelockAddress); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, Governor) returns (bool) { + return interfaceId == type(IGovernorTimelock).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Overriden version of the {Governor-state} function with added support for the `Queued` status. + */ + function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) { + ProposalState status = super.state(proposalId); + + if (status != ProposalState.Succeeded) { + return status; + } + + // core tracks execution, so we just have to check if successful proposal have been queued. + bytes32 queueid = _timelockIds[proposalId]; + if (queueid == bytes32(0)) { + return status; + } else if (_timelock.isOperationDone(queueid)) { + return ProposalState.Executed; + } else if (_timelock.isOperationPending(queueid)) { + return ProposalState.Queued; + } else { + return ProposalState.Canceled; + } + } + + /** + * @dev Public accessor to check the address of the timelock + */ + function timelock() public view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Public accessor to check the eta of a queued proposal + */ + function proposalEta(uint256 proposalId) public view virtual override returns (uint256) { + uint256 eta = _timelock.getTimestamp(_timelockIds[proposalId]); + return eta == 1 ? 0 : eta; // _DONE_TIMESTAMP (1) should be replaced with a 0 value + } + + /** + * @dev Function to queue a proposal to the timelock. + */ + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public virtual override returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + + require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful"); + + uint256 delay = _timelock.getMinDelay(); + _timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash); + _timelock.scheduleBatch(targets, values, calldatas, 0, descriptionHash, delay); + + emit ProposalQueued(proposalId, block.timestamp + delay); + + return proposalId; + } + + /** + * @dev Overriden execute function that run the already queued proposal through the timelock. + */ + function _execute( + uint256, /* proposalId */ + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override { + _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); + } + + /** + * @dev Overriden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already + * been queued. + */ + // This function can reenter through the external call to the timelock, but we assume the timelock is trusted and + // well behaved (according to TimelockController) and this will not happen. + // slither-disable-next-line reentrancy-no-eth + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override returns (uint256) { + uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash); + + if (_timelockIds[proposalId] != 0) { + _timelock.cancel(_timelockIds[proposalId]); + delete _timelockIds[proposalId]; + } + + return proposalId; + } + + /** + * @dev Address through which the governor executes action. In this case, the timelock. + */ + function _executor() internal view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates + * must be proposed, scheduled, and executed through governance proposals. + * + * CAUTION: It is not recommended to change the timelock while there are other queued governance proposals. + */ + function updateTimelock(TimelockController newTimelock) external virtual onlyGovernance { + _updateTimelock(newTimelock); + } + + function _updateTimelock(TimelockController newTimelock) private { + emit TimelockChange(address(_timelock), address(newTimelock)); + _timelock = newTimelock; + } +} diff --git a/certora/munged/governance/extensions/GovernorVotes.sol b/certora/munged/governance/extensions/GovernorVotes.sol new file mode 100644 index 000000000..f0a2276fb --- /dev/null +++ b/certora/munged/governance/extensions/GovernorVotes.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorVotes.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; +import "../utils/IVotes.sol"; + +/** + * @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token, or since v4.5 an {ERC721Votes} token. + * + * _Available since v4.3._ + */ +abstract contract GovernorVotes is Governor { + IVotes public immutable token; + + constructor(IVotes tokenAddress) { + token = tokenAddress; + } + + /** + * Read the voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}). + */ + function _getVotes( + address account, + uint256 blockNumber, + bytes memory /*params*/ + ) internal view virtual override returns (uint256) { + return token.getPastVotes(account, blockNumber); + } +} diff --git a/certora/munged/governance/extensions/GovernorVotesComp.sol b/certora/munged/governance/extensions/GovernorVotesComp.sol new file mode 100644 index 000000000..c31c9583b --- /dev/null +++ b/certora/munged/governance/extensions/GovernorVotesComp.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorVotesComp.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; +import "../../token/ERC20/extensions/ERC20VotesComp.sol"; + +/** + * @dev Extension of {Governor} for voting weight extraction from a Comp token. + * + * _Available since v4.3._ + */ +abstract contract GovernorVotesComp is Governor { + ERC20VotesComp public immutable token; + + constructor(ERC20VotesComp token_) { + token = token_; + } + + /** + * Read the voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}). + */ + function _getVotes( + address account, + uint256 blockNumber, + bytes memory /*params*/ + ) internal view virtual override returns (uint256) { + return token.getPriorVotes(account, blockNumber); + } +} diff --git a/certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol b/certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol new file mode 100644 index 000000000..40f912cbf --- /dev/null +++ b/certora/munged/governance/extensions/GovernorVotesQuorumFraction.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorVotesQuorumFraction.sol) + +pragma solidity ^0.8.0; + +import "./GovernorVotes.sol"; + +/** + * @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token and a quorum expressed as a + * fraction of the total supply. + * + * _Available since v4.3._ + */ +abstract contract GovernorVotesQuorumFraction is GovernorVotes { + uint256 private _quorumNumerator; + + event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator); + + /** + * @dev Initialize quorum as a fraction of the token's total supply. + * + * The fraction is specified as `numerator / denominator`. By default the denominator is 100, so quorum is + * specified as a percent: a numerator of 10 corresponds to quorum being 10% of total supply. The denominator can be + * customized by overriding {quorumDenominator}. + */ + constructor(uint256 quorumNumeratorValue) { + _updateQuorumNumerator(quorumNumeratorValue); + } + + /** + * @dev Returns the current quorum numerator. See {quorumDenominator}. + */ + function quorumNumerator() public view virtual returns (uint256) { + return _quorumNumerator; + } + + /** + * @dev Returns the quorum denominator. Defaults to 100, but may be overridden. + */ + function quorumDenominator() public view virtual returns (uint256) { + return 100; + } + + /** + * @dev Returns the quorum for a block number, in terms of number of votes: `supply * numerator / denominator`. + */ + function quorum(uint256 blockNumber) public view virtual override returns (uint256) { + return (token.getPastTotalSupply(blockNumber) * quorumNumerator()) / quorumDenominator(); + } + + /** + * @dev Changes the quorum numerator. + * + * Emits a {QuorumNumeratorUpdated} event. + * + * Requirements: + * + * - Must be called through a governance proposal. + * - New numerator must be smaller or equal to the denominator. + */ + function updateQuorumNumerator(uint256 newQuorumNumerator) external virtual onlyGovernance { + _updateQuorumNumerator(newQuorumNumerator); + } + + /** + * @dev Changes the quorum numerator. + * + * Emits a {QuorumNumeratorUpdated} event. + * + * Requirements: + * + * - New numerator must be smaller or equal to the denominator. + */ + function _updateQuorumNumerator(uint256 newQuorumNumerator) internal virtual { + require( + newQuorumNumerator <= quorumDenominator(), + "GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator" + ); + + uint256 oldQuorumNumerator = _quorumNumerator; + _quorumNumerator = newQuorumNumerator; + + emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator); + } +} diff --git a/certora/munged/governance/extensions/IGovernorTimelock.sol b/certora/munged/governance/extensions/IGovernorTimelock.sol new file mode 100644 index 000000000..40402f614 --- /dev/null +++ b/certora/munged/governance/extensions/IGovernorTimelock.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/extensions/IGovernorTimelock.sol) + +pragma solidity ^0.8.0; + +import "../IGovernor.sol"; + +/** + * @dev Extension of the {IGovernor} for timelock supporting modules. + * + * _Available since v4.3._ + */ +abstract contract IGovernorTimelock is IGovernor { + event ProposalQueued(uint256 proposalId, uint256 eta); + + function timelock() public view virtual returns (address); + + function proposalEta(uint256 proposalId) public view virtual returns (uint256); + + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public virtual returns (uint256 proposalId); +} diff --git a/certora/munged/governance/utils/IVotes.sol b/certora/munged/governance/utils/IVotes.sol new file mode 100644 index 000000000..6317d7752 --- /dev/null +++ b/certora/munged/governance/utils/IVotes.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol) +pragma solidity ^0.8.0; + +/** + * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts. + * + * _Available since v4.5._ + */ +interface IVotes { + /** + * @dev Emitted when an account changes their delegate. + */ + event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); + + /** + * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes. + */ + event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); + + /** + * @dev Returns the current amount of votes that `account` has. + */ + function getVotes(address account) external view returns (uint256); + + /** + * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`). + */ + function getPastVotes(address account, uint256 blockNumber) external view returns (uint256); + + /** + * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`). + * + * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. + * Votes that have not been delegated are still part of total supply, even though they would not participate in a + * vote. + */ + function getPastTotalSupply(uint256 blockNumber) external view returns (uint256); + + /** + * @dev Returns the delegate that `account` has chosen. + */ + function delegates(address account) external view returns (address); + + /** + * @dev Delegates votes from the sender to `delegatee`. + */ + function delegate(address delegatee) external; + + /** + * @dev Delegates votes from signer to `delegatee`. + */ + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external; +} diff --git a/certora/munged/governance/utils/Votes.sol b/certora/munged/governance/utils/Votes.sol new file mode 100644 index 000000000..6afdc7cfe --- /dev/null +++ b/certora/munged/governance/utils/Votes.sol @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/Votes.sol) +pragma solidity ^0.8.0; + +import "../../utils/Context.sol"; +import "../../utils/Counters.sol"; +import "../../utils/Checkpoints.sol"; +import "../../utils/cryptography/draft-EIP712.sol"; +import "./IVotes.sol"; + +/** + * @dev This is a base abstract contract that tracks voting units, which are a measure of voting power that can be + * transferred, and provides a system of vote delegation, where an account can delegate its voting units to a sort of + * "representative" that will pool delegated voting units from different accounts and can then use it to vote in + * decisions. In fact, voting units _must_ be delegated in order to count as actual votes, and an account has to + * delegate those votes to itself if it wishes to participate in decisions and does not have a trusted representative. + * + * This contract is often combined with a token contract such that voting units correspond to token units. For an + * example, see {ERC721Votes}. + * + * The full history of delegate votes is tracked on-chain so that governance protocols can consider votes as distributed + * at a particular block number to protect against flash loans and double voting. The opt-in delegate system makes the + * cost of this history tracking optional. + * + * When using this module the derived contract must implement {_getVotingUnits} (for example, make it return + * {ERC721-balanceOf}), and can use {_transferVotingUnits} to track a change in the distribution of those units (in the + * previous example, it would be included in {ERC721-_beforeTokenTransfer}). + * + * _Available since v4.5._ + */ +abstract contract Votes is IVotes, Context, EIP712 { + using Checkpoints for Checkpoints.History; + using Counters for Counters.Counter; + + bytes32 private constant _DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + + mapping(address => address) private _delegation; + mapping(address => Checkpoints.History) private _delegateCheckpoints; + Checkpoints.History private _totalCheckpoints; + + mapping(address => Counters.Counter) private _nonces; + + /** + * @dev Returns the current amount of votes that `account` has. + */ + function getVotes(address account) public view virtual override returns (uint256) { + return _delegateCheckpoints[account].latest(); + } + + /** + * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`). + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + return _delegateCheckpoints[account].getAtBlock(blockNumber); + } + + /** + * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`). + * + * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. + * Votes that have not been delegated are still part of total supply, even though they would not participate in a + * vote. + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) { + require(blockNumber < block.number, "Votes: block not yet mined"); + return _totalCheckpoints.getAtBlock(blockNumber); + } + + /** + * @dev Returns the current total supply of votes. + */ + function _getTotalSupply() internal view virtual returns (uint256) { + return _totalCheckpoints.latest(); + } + + /** + * @dev Returns the delegate that `account` has chosen. + */ + function delegates(address account) public view virtual override returns (address) { + return _delegation[account]; + } + + /** + * @dev Delegates votes from the sender to `delegatee`. + */ + function delegate(address delegatee) public virtual override { + address account = _msgSender(); + _delegate(account, delegatee); + } + + /** + * @dev Delegates votes from signer to `delegatee`. + */ + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + require(block.timestamp <= expiry, "Votes: signature expired"); + address signer = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), + v, + r, + s + ); + require(nonce == _useNonce(signer), "Votes: invalid nonce"); + _delegate(signer, delegatee); + } + + /** + * @dev Delegate all of `account`'s voting units to `delegatee`. + * + * Emits events {DelegateChanged} and {DelegateVotesChanged}. + */ + function _delegate(address account, address delegatee) internal virtual { + address oldDelegate = delegates(account); + _delegation[account] = delegatee; + + emit DelegateChanged(account, oldDelegate, delegatee); + _moveDelegateVotes(oldDelegate, delegatee, _getVotingUnits(account)); + } + + /** + * @dev Transfers, mints, or burns voting units. To register a mint, `from` should be zero. To register a burn, `to` + * should be zero. Total supply of voting units will be adjusted with mints and burns. + */ + function _transferVotingUnits( + address from, + address to, + uint256 amount + ) internal virtual { + if (from == address(0)) { + _totalCheckpoints.push(_add, amount); + } + if (to == address(0)) { + _totalCheckpoints.push(_subtract, amount); + } + _moveDelegateVotes(delegates(from), delegates(to), amount); + } + + /** + * @dev Moves delegated votes from one delegate to another. + */ + function _moveDelegateVotes( + address from, + address to, + uint256 amount + ) private { + if (from != to && amount > 0) { + if (from != address(0)) { + (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_subtract, amount); + emit DelegateVotesChanged(from, oldValue, newValue); + } + if (to != address(0)) { + (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_add, amount); + emit DelegateVotesChanged(to, oldValue, newValue); + } + } + } + + function _add(uint256 a, uint256 b) private pure returns (uint256) { + return a + b; + } + + function _subtract(uint256 a, uint256 b) private pure returns (uint256) { + return a - b; + } + + /** + * @dev Consumes a nonce. + * + * Returns the current value and increments nonce. + */ + function _useNonce(address owner) internal virtual returns (uint256 current) { + Counters.Counter storage nonce = _nonces[owner]; + current = nonce.current(); + nonce.increment(); + } + + /** + * @dev Returns an address nonce. + */ + function nonces(address owner) public view virtual returns (uint256) { + return _nonces[owner].current(); + } + + /** + * @dev Returns the contract's {EIP712} domain separator. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _domainSeparatorV4(); + } + + /** + * @dev Must return the voting units held by an account. + */ + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public +} diff --git a/certora/munged/interfaces/IERC1155.sol b/certora/munged/interfaces/IERC1155.sol new file mode 100644 index 000000000..f89113212 --- /dev/null +++ b/certora/munged/interfaces/IERC1155.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC1155/IERC1155.sol"; diff --git a/certora/munged/interfaces/IERC1155MetadataURI.sol b/certora/munged/interfaces/IERC1155MetadataURI.sol new file mode 100644 index 000000000..2aa885feb --- /dev/null +++ b/certora/munged/interfaces/IERC1155MetadataURI.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155MetadataURI.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC1155/extensions/IERC1155MetadataURI.sol"; diff --git a/certora/munged/interfaces/IERC1155Receiver.sol b/certora/munged/interfaces/IERC1155Receiver.sol new file mode 100644 index 000000000..a6d4ead16 --- /dev/null +++ b/certora/munged/interfaces/IERC1155Receiver.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155Receiver.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC1155/IERC1155Receiver.sol"; diff --git a/certora/munged/interfaces/IERC1271.sol b/certora/munged/interfaces/IERC1271.sol new file mode 100644 index 000000000..5ec44c721 --- /dev/null +++ b/certora/munged/interfaces/IERC1271.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC1271 standard signature validation method for + * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. + * + * _Available since v4.1._ + */ +interface IERC1271 { + /** + * @dev Should return whether the signature provided is valid for the provided data + * @param hash Hash of the data to be signed + * @param signature Signature byte array associated with _data + */ + function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); +} diff --git a/certora/munged/interfaces/IERC1363.sol b/certora/munged/interfaces/IERC1363.sol new file mode 100644 index 000000000..5fad104c2 --- /dev/null +++ b/certora/munged/interfaces/IERC1363.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1363.sol) + +pragma solidity ^0.8.0; + +import "./IERC20.sol"; +import "./IERC165.sol"; + +interface IERC1363 is IERC165, IERC20 { + /* + * Note: the ERC-165 identifier for this interface is 0x4bbee2df. + * 0x4bbee2df === + * bytes4(keccak256('transferAndCall(address,uint256)')) ^ + * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ + * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ + * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) + */ + + /* + * Note: the ERC-165 identifier for this interface is 0xfb9ec8ce. + * 0xfb9ec8ce === + * bytes4(keccak256('approveAndCall(address,uint256)')) ^ + * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) + */ + + /** + * @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @return true unless throwing + */ + function transferAndCall(address to, uint256 value) external returns (bool); + + /** + * @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @param data bytes Additional data with no specified format, sent in call to `to` + * @return true unless throwing + */ + function transferAndCall( + address to, + uint256 value, + bytes memory data + ) external returns (bool); + + /** + * @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @return true unless throwing + */ + function transferFromAndCall( + address from, + address to, + uint256 value + ) external returns (bool); + + /** + * @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @param data bytes Additional data with no specified format, sent in call to `to` + * @return true unless throwing + */ + function transferFromAndCall( + address from, + address to, + uint256 value, + bytes memory data + ) external returns (bool); + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender + * and then call `onApprovalReceived` on spender. + * @param spender address The address which will spend the funds + * @param value uint256 The amount of tokens to be spent + */ + function approveAndCall(address spender, uint256 value) external returns (bool); + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender + * and then call `onApprovalReceived` on spender. + * @param spender address The address which will spend the funds + * @param value uint256 The amount of tokens to be spent + * @param data bytes Additional data with no specified format, sent in call to `spender` + */ + function approveAndCall( + address spender, + uint256 value, + bytes memory data + ) external returns (bool); +} diff --git a/certora/munged/interfaces/IERC1363Receiver.sol b/certora/munged/interfaces/IERC1363Receiver.sol new file mode 100644 index 000000000..bc5eaddb0 --- /dev/null +++ b/certora/munged/interfaces/IERC1363Receiver.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1363Receiver.sol) + +pragma solidity ^0.8.0; + +interface IERC1363Receiver { + /* + * Note: the ERC-165 identifier for this interface is 0x88a7ca5c. + * 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)")) + */ + + /** + * @notice Handle the receipt of ERC1363 tokens + * @dev Any ERC1363 smart contract calls this function on the recipient + * after a `transfer` or a `transferFrom`. This function MAY throw to revert and reject the + * transfer. Return of other than the magic value MUST result in the + * transaction being reverted. + * Note: the token contract address is always the message sender. + * @param operator address The address which called `transferAndCall` or `transferFromAndCall` function + * @param from address The address which are token transferred from + * @param value uint256 The amount of tokens transferred + * @param data bytes Additional data with no specified format + * @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))` + * unless throwing + */ + function onTransferReceived( + address operator, + address from, + uint256 value, + bytes memory data + ) external returns (bytes4); +} diff --git a/certora/munged/interfaces/IERC1363Spender.sol b/certora/munged/interfaces/IERC1363Spender.sol new file mode 100644 index 000000000..48f6fd56d --- /dev/null +++ b/certora/munged/interfaces/IERC1363Spender.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1363Spender.sol) + +pragma solidity ^0.8.0; + +interface IERC1363Spender { + /* + * Note: the ERC-165 identifier for this interface is 0x7b04a2d0. + * 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)")) + */ + + /** + * @notice Handle the approval of ERC1363 tokens + * @dev Any ERC1363 smart contract calls this function on the recipient + * after an `approve`. This function MAY throw to revert and reject the + * approval. Return of other than the magic value MUST result in the + * transaction being reverted. + * Note: the token contract address is always the message sender. + * @param owner address The address which called `approveAndCall` function + * @param value uint256 The amount of tokens to be spent + * @param data bytes Additional data with no specified format + * @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))` + * unless throwing + */ + function onApprovalReceived( + address owner, + uint256 value, + bytes memory data + ) external returns (bytes4); +} diff --git a/certora/munged/interfaces/IERC165.sol b/certora/munged/interfaces/IERC165.sol new file mode 100644 index 000000000..b97c4daa2 --- /dev/null +++ b/certora/munged/interfaces/IERC165.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC165.sol"; diff --git a/certora/munged/interfaces/IERC1820Implementer.sol b/certora/munged/interfaces/IERC1820Implementer.sol new file mode 100644 index 000000000..a83a7a304 --- /dev/null +++ b/certora/munged/interfaces/IERC1820Implementer.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1820Implementer.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC1820Implementer.sol"; diff --git a/certora/munged/interfaces/IERC1820Registry.sol b/certora/munged/interfaces/IERC1820Registry.sol new file mode 100644 index 000000000..1b1ba9fcf --- /dev/null +++ b/certora/munged/interfaces/IERC1820Registry.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1820Registry.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC1820Registry.sol"; diff --git a/certora/munged/interfaces/IERC20.sol b/certora/munged/interfaces/IERC20.sol new file mode 100644 index 000000000..a819316d1 --- /dev/null +++ b/certora/munged/interfaces/IERC20.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/IERC20.sol"; diff --git a/certora/munged/interfaces/IERC20Metadata.sol b/certora/munged/interfaces/IERC20Metadata.sol new file mode 100644 index 000000000..aa5c63910 --- /dev/null +++ b/certora/munged/interfaces/IERC20Metadata.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20Metadata.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/IERC20Metadata.sol"; diff --git a/certora/munged/interfaces/IERC2981.sol b/certora/munged/interfaces/IERC2981.sol new file mode 100644 index 000000000..f02139869 --- /dev/null +++ b/certora/munged/interfaces/IERC2981.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/IERC2981.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC165.sol"; + +/** + * @dev Interface for the NFT Royalty Standard. + * + * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal + * support for royalty payments across all NFT marketplaces and ecosystem participants. + * + * _Available since v4.5._ + */ +interface IERC2981 is IERC165 { + /** + * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of + * exchange. The royalty amount is denominated and should be payed in that same unit of exchange. + */ + function royaltyInfo(uint256 tokenId, uint256 salePrice) + external + view + returns (address receiver, uint256 royaltyAmount); +} diff --git a/certora/munged/interfaces/IERC3156.sol b/certora/munged/interfaces/IERC3156.sol new file mode 100644 index 000000000..12381906d --- /dev/null +++ b/certora/munged/interfaces/IERC3156.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156.sol) + +pragma solidity ^0.8.0; + +import "./IERC3156FlashBorrower.sol"; +import "./IERC3156FlashLender.sol"; diff --git a/certora/munged/interfaces/IERC3156FlashBorrower.sol b/certora/munged/interfaces/IERC3156FlashBorrower.sol new file mode 100644 index 000000000..68d0dacf4 --- /dev/null +++ b/certora/munged/interfaces/IERC3156FlashBorrower.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashBorrower.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC3156 FlashBorrower, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * _Available since v4.1._ + */ +interface IERC3156FlashBorrower { + /** + * @dev Receive a flash loan. + * @param initiator The initiator of the loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param fee The additional amount of tokens to repay. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" + */ + function onFlashLoan( + address initiator, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) external returns (bytes32); +} diff --git a/certora/munged/interfaces/IERC3156FlashLender.sol b/certora/munged/interfaces/IERC3156FlashLender.sol new file mode 100644 index 000000000..31012830f --- /dev/null +++ b/certora/munged/interfaces/IERC3156FlashLender.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol) + +pragma solidity ^0.8.0; + +import "./IERC3156FlashBorrower.sol"; + +/** + * @dev Interface of the ERC3156 FlashLender, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * _Available since v4.1._ + */ +interface IERC3156FlashLender { + /** + * @dev The amount of currency available to be lended. + * @param token The loan currency. + * @return The amount of `token` that can be borrowed. + */ + function maxFlashLoan(address token) external view returns (uint256); + + /** + * @dev The fee to be charged for a given loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @return The amount of `token` to be charged for the loan, on top of the returned principal. + */ + function flashFee(address token, uint256 amount) external view returns (uint256); + + /** + * @dev Initiate a flash loan. + * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + */ + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) external returns (bool); +} diff --git a/certora/munged/interfaces/IERC721.sol b/certora/munged/interfaces/IERC721.sol new file mode 100644 index 000000000..822b311c5 --- /dev/null +++ b/certora/munged/interfaces/IERC721.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/IERC721.sol"; diff --git a/certora/munged/interfaces/IERC721Enumerable.sol b/certora/munged/interfaces/IERC721Enumerable.sol new file mode 100644 index 000000000..e39a5a01b --- /dev/null +++ b/certora/munged/interfaces/IERC721Enumerable.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/IERC721Enumerable.sol"; diff --git a/certora/munged/interfaces/IERC721Metadata.sol b/certora/munged/interfaces/IERC721Metadata.sol new file mode 100644 index 000000000..afe2707c9 --- /dev/null +++ b/certora/munged/interfaces/IERC721Metadata.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Metadata.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/IERC721Metadata.sol"; diff --git a/certora/munged/interfaces/IERC721Receiver.sol b/certora/munged/interfaces/IERC721Receiver.sol new file mode 100644 index 000000000..c9c153a24 --- /dev/null +++ b/certora/munged/interfaces/IERC721Receiver.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Receiver.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/IERC721Receiver.sol"; diff --git a/certora/munged/interfaces/IERC777.sol b/certora/munged/interfaces/IERC777.sol new file mode 100644 index 000000000..b97ba7b80 --- /dev/null +++ b/certora/munged/interfaces/IERC777.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777.sol"; diff --git a/certora/munged/interfaces/IERC777Recipient.sol b/certora/munged/interfaces/IERC777Recipient.sol new file mode 100644 index 000000000..0ce2704a8 --- /dev/null +++ b/certora/munged/interfaces/IERC777Recipient.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777Recipient.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777Recipient.sol"; diff --git a/certora/munged/interfaces/IERC777Sender.sol b/certora/munged/interfaces/IERC777Sender.sol new file mode 100644 index 000000000..f1f17a22e --- /dev/null +++ b/certora/munged/interfaces/IERC777Sender.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777Sender.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777Sender.sol"; diff --git a/certora/munged/interfaces/README.adoc b/certora/munged/interfaces/README.adoc new file mode 100644 index 000000000..b6b96ffef --- /dev/null +++ b/certora/munged/interfaces/README.adoc @@ -0,0 +1,50 @@ += Interfaces + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/interfaces + +== List of standardized interfaces +These interfaces are available as `.sol` files, and also as compiler `.json` ABI files (through the npm package). These +are useful to interact with third party contracts that implement them. + +- {IERC20} +- {IERC20Metadata} +- {IERC165} +- {IERC721} +- {IERC721Receiver} +- {IERC721Enumerable} +- {IERC721Metadata} +- {IERC777} +- {IERC777Recipient} +- {IERC777Sender} +- {IERC1155} +- {IERC1155Receiver} +- {IERC1155MetadataURI} +- {IERC1271} +- {IERC1363} +- {IERC1820Implementer} +- {IERC1820Registry} +- {IERC2612} +- {IERC2981} +- {IERC3156FlashLender} +- {IERC3156FlashBorrower} + +== Detailed ABI + +{{IERC1271}} + +{{IERC1363}} + +{{IERC1363Receiver}} + +{{IERC1820Implementer}} + +{{IERC1820Registry}} + +{{IERC2612}} + +{{IERC2981}} + +{{IERC3156FlashLender}} + +{{IERC3156FlashBorrower}} diff --git a/certora/munged/interfaces/draft-IERC1822.sol b/certora/munged/interfaces/draft-IERC1822.sol new file mode 100644 index 000000000..3b73d744c --- /dev/null +++ b/certora/munged/interfaces/draft-IERC1822.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol) + +pragma solidity ^0.8.0; + +/** + * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified + * proxy whose upgrades are fully controlled by the current implementation. + */ +interface IERC1822Proxiable { + /** + * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation + * address. + * + * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks + * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this + * function revert if invoked through a proxy. + */ + function proxiableUUID() external view returns (bytes32); +} diff --git a/certora/munged/interfaces/draft-IERC2612.sol b/certora/munged/interfaces/draft-IERC2612.sol new file mode 100644 index 000000000..1b3ae55f9 --- /dev/null +++ b/certora/munged/interfaces/draft-IERC2612.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/draft-IERC2612.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/draft-IERC20Permit.sol"; + +interface IERC2612 is IERC20Permit {} diff --git a/certora/munged/metatx/ERC2771Context.sol b/certora/munged/metatx/ERC2771Context.sol new file mode 100644 index 000000000..b5d16e560 --- /dev/null +++ b/certora/munged/metatx/ERC2771Context.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (metatx/ERC2771Context.sol) + +pragma solidity ^0.8.9; + +import "../utils/Context.sol"; + +/** + * @dev Context variant with ERC2771 support. + */ +abstract contract ERC2771Context is Context { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + address private immutable _trustedForwarder; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address trustedForwarder) { + _trustedForwarder = trustedForwarder; + } + + function isTrustedForwarder(address forwarder) public view virtual returns (bool) { + return forwarder == _trustedForwarder; + } + + function _msgSender() internal view virtual override returns (address sender) { + if (isTrustedForwarder(msg.sender)) { + // The assembly code is more direct than the Solidity version using `abi.decode`. + assembly { + sender := shr(96, calldataload(sub(calldatasize(), 20))) + } + } else { + return super._msgSender(); + } + } + + function _msgData() internal view virtual override returns (bytes calldata) { + if (isTrustedForwarder(msg.sender)) { + return msg.data[:msg.data.length - 20]; + } else { + return super._msgData(); + } + } +} diff --git a/certora/munged/metatx/MinimalForwarder.sol b/certora/munged/metatx/MinimalForwarder.sol new file mode 100644 index 000000000..a7a1899f4 --- /dev/null +++ b/certora/munged/metatx/MinimalForwarder.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (metatx/MinimalForwarder.sol) + +pragma solidity ^0.8.0; + +import "../utils/cryptography/ECDSA.sol"; +import "../utils/cryptography/draft-EIP712.sol"; + +/** + * @dev Simple minimal forwarder to be used together with an ERC2771 compatible contract. See {ERC2771Context}. + */ +contract MinimalForwarder is EIP712 { + using ECDSA for bytes32; + + struct ForwardRequest { + address from; + address to; + uint256 value; + uint256 gas; + uint256 nonce; + bytes data; + } + + bytes32 private constant _TYPEHASH = + keccak256("ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)"); + + mapping(address => uint256) private _nonces; + + constructor() EIP712("MinimalForwarder", "0.0.1") {} + + function getNonce(address from) public view returns (uint256) { + return _nonces[from]; + } + + function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) { + address signer = _hashTypedDataV4( + keccak256(abi.encode(_TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data))) + ).recover(signature); + return _nonces[req.from] == req.nonce && signer == req.from; + } + + function execute(ForwardRequest calldata req, bytes calldata signature) + public + payable + returns (bool, bytes memory) + { + require(verify(req, signature), "MinimalForwarder: signature does not match request"); + _nonces[req.from] = req.nonce + 1; + + (bool success, bytes memory returndata) = req.to.call{gas: req.gas, value: req.value}( + abi.encodePacked(req.data, req.from) + ); + + // Validate that the relayer has sent enough gas for the call. + // See https://ronan.eth.link/blog/ethereum-gas-dangers/ + if (gasleft() <= req.gas / 63) { + // We explicitly trigger invalid opcode to consume all gas and bubble-up the effects, since + // neither revert or assert consume all gas since Solidity 0.8.0 + // https://docs.soliditylang.org/en/v0.8.0/control-structures.html#panic-via-assert-and-error-via-require + assembly { + invalid() + } + } + + return (success, returndata); + } +} diff --git a/certora/munged/metatx/README.adoc b/certora/munged/metatx/README.adoc new file mode 100644 index 000000000..eccdeaf97 --- /dev/null +++ b/certora/munged/metatx/README.adoc @@ -0,0 +1,12 @@ += Meta Transactions + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/metatx + +== Core + +{{ERC2771Context}} + +== Utils + +{{MinimalForwarder}} diff --git a/certora/munged/mocks/AccessControlEnumerableMock.sol b/certora/munged/mocks/AccessControlEnumerableMock.sol new file mode 100644 index 000000000..7b15e3602 --- /dev/null +++ b/certora/munged/mocks/AccessControlEnumerableMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/AccessControlEnumerable.sol"; + +contract AccessControlEnumerableMock is AccessControlEnumerable { + constructor() { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public { + _setRoleAdmin(roleId, adminRoleId); + } + + function senderProtected(bytes32 roleId) public onlyRole(roleId) {} +} diff --git a/certora/munged/mocks/AccessControlMock.sol b/certora/munged/mocks/AccessControlMock.sol new file mode 100644 index 000000000..86f51477e --- /dev/null +++ b/certora/munged/mocks/AccessControlMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/AccessControl.sol"; + +contract AccessControlMock is AccessControl { + constructor() { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public { + _setRoleAdmin(roleId, adminRoleId); + } + + function senderProtected(bytes32 roleId) public onlyRole(roleId) {} +} diff --git a/certora/munged/mocks/AddressImpl.sol b/certora/munged/mocks/AddressImpl.sol new file mode 100644 index 000000000..702093c73 --- /dev/null +++ b/certora/munged/mocks/AddressImpl.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Address.sol"; + +contract AddressImpl { + string public sharedAnswer; + + event CallReturnValue(string data); + + function isContract(address account) external view returns (bool) { + return Address.isContract(account); + } + + function sendValue(address payable receiver, uint256 amount) external { + Address.sendValue(receiver, amount); + } + + function functionCall(address target, bytes calldata data) external { + bytes memory returnData = Address.functionCall(target, data); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + function functionCallWithValue( + address target, + bytes calldata data, + uint256 value + ) external payable { + bytes memory returnData = Address.functionCallWithValue(target, data, value); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + function functionStaticCall(address target, bytes calldata data) external { + bytes memory returnData = Address.functionStaticCall(target, data); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + function functionDelegateCall(address target, bytes calldata data) external { + bytes memory returnData = Address.functionDelegateCall(target, data); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + // sendValue's tests require the contract to hold Ether + receive() external payable {} +} diff --git a/certora/munged/mocks/ArraysImpl.sol b/certora/munged/mocks/ArraysImpl.sol new file mode 100644 index 000000000..f720524b8 --- /dev/null +++ b/certora/munged/mocks/ArraysImpl.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Arrays.sol"; + +contract ArraysImpl { + using Arrays for uint256[]; + + uint256[] private _array; + + constructor(uint256[] memory array) { + _array = array; + } + + function findUpperBound(uint256 element) external view returns (uint256) { + return _array.findUpperBound(element); + } +} diff --git a/certora/munged/mocks/BadBeacon.sol b/certora/munged/mocks/BadBeacon.sol new file mode 100644 index 000000000..bedcfed84 --- /dev/null +++ b/certora/munged/mocks/BadBeacon.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract BadBeaconNoImpl {} + +contract BadBeaconNotContract { + function implementation() external pure returns (address) { + return address(0x1); + } +} diff --git a/certora/munged/mocks/Base64Mock.sol b/certora/munged/mocks/Base64Mock.sol new file mode 100644 index 000000000..b255487bc --- /dev/null +++ b/certora/munged/mocks/Base64Mock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Base64.sol"; + +contract Base64Mock { + function encode(bytes memory value) external pure returns (string memory) { + return Base64.encode(value); + } +} diff --git a/certora/munged/mocks/BitmapMock.sol b/certora/munged/mocks/BitmapMock.sol new file mode 100644 index 000000000..ccf8486f5 --- /dev/null +++ b/certora/munged/mocks/BitmapMock.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/structs/BitMaps.sol"; + +contract BitMapMock { + using BitMaps for BitMaps.BitMap; + + BitMaps.BitMap private _bitmap; + + function get(uint256 index) public view returns (bool) { + return _bitmap.get(index); + } + + function setTo(uint256 index, bool value) public { + _bitmap.setTo(index, value); + } + + function set(uint256 index) public { + _bitmap.set(index); + } + + function unset(uint256 index) public { + _bitmap.unset(index); + } +} diff --git a/certora/munged/mocks/CallReceiverMock.sol b/certora/munged/mocks/CallReceiverMock.sol new file mode 100644 index 000000000..926db68bf --- /dev/null +++ b/certora/munged/mocks/CallReceiverMock.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract CallReceiverMock { + string public sharedAnswer; + + event MockFunctionCalled(); + event MockFunctionCalledWithArgs(uint256 a, uint256 b); + + uint256[] private _array; + + function mockFunction() public payable returns (string memory) { + emit MockFunctionCalled(); + + return "0x1234"; + } + + function mockFunctionWithArgs(uint256 a, uint256 b) public payable returns (string memory) { + emit MockFunctionCalledWithArgs(a, b); + + return "0x1234"; + } + + function mockFunctionNonPayable() public returns (string memory) { + emit MockFunctionCalled(); + + return "0x1234"; + } + + function mockStaticFunction() public pure returns (string memory) { + return "0x1234"; + } + + function mockFunctionRevertsNoReason() public payable { + revert(); + } + + function mockFunctionRevertsReason() public payable { + revert("CallReceiverMock: reverting"); + } + + function mockFunctionThrows() public payable { + assert(false); + } + + function mockFunctionOutOfGas() public payable { + for (uint256 i = 0; ; ++i) { + _array.push(i); + } + } + + function mockFunctionWritesStorage() public returns (string memory) { + sharedAnswer = "42"; + return "0x1234"; + } +} diff --git a/certora/munged/mocks/CheckpointsImpl.sol b/certora/munged/mocks/CheckpointsImpl.sol new file mode 100644 index 000000000..5b9ec0acb --- /dev/null +++ b/certora/munged/mocks/CheckpointsImpl.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Checkpoints.sol"; + +contract CheckpointsImpl { + using Checkpoints for Checkpoints.History; + + Checkpoints.History private _totalCheckpoints; + + function latest() public view returns (uint256) { + return _totalCheckpoints.latest(); + } + + function getAtBlock(uint256 blockNumber) public view returns (uint256) { + return _totalCheckpoints.getAtBlock(blockNumber); + } + + function push(uint256 value) public returns (uint256, uint256) { + return _totalCheckpoints.push(value); + } +} diff --git a/certora/munged/mocks/ClashingImplementation.sol b/certora/munged/mocks/ClashingImplementation.sol new file mode 100644 index 000000000..80aca0c29 --- /dev/null +++ b/certora/munged/mocks/ClashingImplementation.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @dev Implementation contract with an admin() function made to clash with + * @dev TransparentUpgradeableProxy's to test correct functioning of the + * @dev Transparent Proxy feature. + */ +contract ClashingImplementation { + function admin() external pure returns (address) { + return 0x0000000000000000000000000000000011111142; + } + + function delegatedFunction() external pure returns (bool) { + return true; + } +} diff --git a/certora/munged/mocks/ClonesMock.sol b/certora/munged/mocks/ClonesMock.sol new file mode 100644 index 000000000..3719b0a78 --- /dev/null +++ b/certora/munged/mocks/ClonesMock.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/Clones.sol"; +import "../utils/Address.sol"; + +contract ClonesMock { + using Address for address; + using Clones for address; + + event NewInstance(address instance); + + function clone(address implementation, bytes calldata initdata) public payable { + _initAndEmit(implementation.clone(), initdata); + } + + function cloneDeterministic( + address implementation, + bytes32 salt, + bytes calldata initdata + ) public payable { + _initAndEmit(implementation.cloneDeterministic(salt), initdata); + } + + function predictDeterministicAddress(address implementation, bytes32 salt) public view returns (address predicted) { + return implementation.predictDeterministicAddress(salt); + } + + function _initAndEmit(address instance, bytes memory initdata) private { + if (initdata.length > 0) { + instance.functionCallWithValue(initdata, msg.value); + } + emit NewInstance(instance); + } +} diff --git a/certora/munged/mocks/ConditionalEscrowMock.sol b/certora/munged/mocks/ConditionalEscrowMock.sol new file mode 100644 index 000000000..ececf0521 --- /dev/null +++ b/certora/munged/mocks/ConditionalEscrowMock.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/escrow/ConditionalEscrow.sol"; + +// mock class using ConditionalEscrow +contract ConditionalEscrowMock is ConditionalEscrow { + mapping(address => bool) private _allowed; + + function setAllowed(address payee, bool allowed) public { + _allowed[payee] = allowed; + } + + function withdrawalAllowed(address payee) public view override returns (bool) { + return _allowed[payee]; + } +} diff --git a/certora/munged/mocks/ContextMock.sol b/certora/munged/mocks/ContextMock.sol new file mode 100644 index 000000000..f17af38a4 --- /dev/null +++ b/certora/munged/mocks/ContextMock.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +contract ContextMock is Context { + event Sender(address sender); + + function msgSender() public { + emit Sender(_msgSender()); + } + + event Data(bytes data, uint256 integerValue, string stringValue); + + function msgData(uint256 integerValue, string memory stringValue) public { + emit Data(_msgData(), integerValue, stringValue); + } +} + +contract ContextMockCaller { + function callSender(ContextMock context) public { + context.msgSender(); + } + + function callData( + ContextMock context, + uint256 integerValue, + string memory stringValue + ) public { + context.msgData(integerValue, stringValue); + } +} diff --git a/certora/munged/mocks/CountersImpl.sol b/certora/munged/mocks/CountersImpl.sol new file mode 100644 index 000000000..651b50baf --- /dev/null +++ b/certora/munged/mocks/CountersImpl.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Counters.sol"; + +contract CountersImpl { + using Counters for Counters.Counter; + + Counters.Counter private _counter; + + function current() public view returns (uint256) { + return _counter.current(); + } + + function increment() public { + _counter.increment(); + } + + function decrement() public { + _counter.decrement(); + } + + function reset() public { + _counter.reset(); + } +} diff --git a/certora/munged/mocks/Create2Impl.sol b/certora/munged/mocks/Create2Impl.sol new file mode 100644 index 000000000..070ad3671 --- /dev/null +++ b/certora/munged/mocks/Create2Impl.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Create2.sol"; +import "../utils/introspection/ERC1820Implementer.sol"; + +contract Create2Impl { + function deploy( + uint256 value, + bytes32 salt, + bytes memory code + ) public { + Create2.deploy(value, salt, code); + } + + function deployERC1820Implementer(uint256 value, bytes32 salt) public { + Create2.deploy(value, salt, type(ERC1820Implementer).creationCode); + } + + function computeAddress(bytes32 salt, bytes32 codeHash) public view returns (address) { + return Create2.computeAddress(salt, codeHash); + } + + function computeAddressWithDeployer( + bytes32 salt, + bytes32 codeHash, + address deployer + ) public pure returns (address) { + return Create2.computeAddress(salt, codeHash, deployer); + } + + receive() external payable {} +} diff --git a/certora/munged/mocks/DoubleEndedQueueMock.sol b/certora/munged/mocks/DoubleEndedQueueMock.sol new file mode 100644 index 000000000..a9436b005 --- /dev/null +++ b/certora/munged/mocks/DoubleEndedQueueMock.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/structs/DoubleEndedQueue.sol"; + +// Bytes32Deque +contract Bytes32DequeMock { + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + + event OperationResult(bytes32 value); + + DoubleEndedQueue.Bytes32Deque private _vector; + + function pushBack(bytes32 value) public { + _vector.pushBack(value); + } + + function pushFront(bytes32 value) public { + _vector.pushFront(value); + } + + function popFront() public returns (bytes32) { + bytes32 value = _vector.popFront(); + emit OperationResult(value); + return value; + } + + function popBack() public returns (bytes32) { + bytes32 value = _vector.popBack(); + emit OperationResult(value); + return value; + } + + function front() public view returns (bytes32) { + return _vector.front(); + } + + function back() public view returns (bytes32) { + return _vector.back(); + } + + function at(uint256 i) public view returns (bytes32) { + return _vector.at(i); + } + + function clear() public { + _vector.clear(); + } + + function length() public view returns (uint256) { + return _vector.length(); + } + + function empty() public view returns (bool) { + return _vector.empty(); + } +} diff --git a/certora/munged/mocks/DummyImplementation.sol b/certora/munged/mocks/DummyImplementation.sol new file mode 100644 index 000000000..d8651340d --- /dev/null +++ b/certora/munged/mocks/DummyImplementation.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +abstract contract Impl { + function version() public pure virtual returns (string memory); +} + +contract DummyImplementation { + uint256 public value; + string public text; + uint256[] public values; + + function initializeNonPayable() public { + value = 10; + } + + function initializePayable() public payable { + value = 100; + } + + function initializeNonPayableWithValue(uint256 _value) public { + value = _value; + } + + function initializePayableWithValue(uint256 _value) public payable { + value = _value; + } + + function initialize( + uint256 _value, + string memory _text, + uint256[] memory _values + ) public { + value = _value; + text = _text; + values = _values; + } + + function get() public pure returns (bool) { + return true; + } + + function version() public pure virtual returns (string memory) { + return "V1"; + } + + function reverts() public pure { + require(false, "DummyImplementation reverted"); + } +} + +contract DummyImplementationV2 is DummyImplementation { + function migrate(uint256 newVal) public payable { + value = newVal; + } + + function version() public pure override returns (string memory) { + return "V2"; + } +} diff --git a/certora/munged/mocks/ECDSAMock.sol b/certora/munged/mocks/ECDSAMock.sol new file mode 100644 index 000000000..97bd46669 --- /dev/null +++ b/certora/munged/mocks/ECDSAMock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/ECDSA.sol"; + +contract ECDSAMock { + using ECDSA for bytes32; + using ECDSA for bytes; + + function recover(bytes32 hash, bytes memory signature) public pure returns (address) { + return hash.recover(signature); + } + + // solhint-disable-next-line func-name-mixedcase + function recover_v_r_s( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) public pure returns (address) { + return hash.recover(v, r, s); + } + + // solhint-disable-next-line func-name-mixedcase + function recover_r_vs( + bytes32 hash, + bytes32 r, + bytes32 vs + ) public pure returns (address) { + return hash.recover(r, vs); + } + + function toEthSignedMessageHash(bytes32 hash) public pure returns (bytes32) { + return hash.toEthSignedMessageHash(); + } + + function toEthSignedMessageHash(bytes memory s) public pure returns (bytes32) { + return s.toEthSignedMessageHash(); + } +} diff --git a/certora/munged/mocks/EIP712External.sol b/certora/munged/mocks/EIP712External.sol new file mode 100644 index 000000000..6f2446900 --- /dev/null +++ b/certora/munged/mocks/EIP712External.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/draft-EIP712.sol"; +import "../utils/cryptography/ECDSA.sol"; + +contract EIP712External is EIP712 { + constructor(string memory name, string memory version) EIP712(name, version) {} + + function domainSeparator() external view returns (bytes32) { + return _domainSeparatorV4(); + } + + function verify( + bytes memory signature, + address signer, + address mailTo, + string memory mailContents + ) external view { + bytes32 digest = _hashTypedDataV4( + keccak256(abi.encode(keccak256("Mail(address to,string contents)"), mailTo, keccak256(bytes(mailContents)))) + ); + address recoveredSigner = ECDSA.recover(digest, signature); + require(recoveredSigner == signer); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/certora/munged/mocks/ERC1155BurnableMock.sol b/certora/munged/mocks/ERC1155BurnableMock.sol new file mode 100644 index 000000000..62138f28d --- /dev/null +++ b/certora/munged/mocks/ERC1155BurnableMock.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC1155/extensions/ERC1155Burnable.sol"; + +contract ERC1155BurnableMock is ERC1155Burnable { + constructor(string memory uri) ERC1155(uri) {} + + function mint( + address to, + uint256 id, + uint256 value, + bytes memory data + ) public { + _mint(to, id, value, data); + } +} diff --git a/certora/munged/mocks/ERC1155Mock.sol b/certora/munged/mocks/ERC1155Mock.sol new file mode 100644 index 000000000..0518ac26c --- /dev/null +++ b/certora/munged/mocks/ERC1155Mock.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC1155/ERC1155.sol"; + +/** + * @title ERC1155Mock + * This mock just publicizes internal functions for testing purposes + */ +contract ERC1155Mock is ERC1155 { + constructor(string memory uri) ERC1155(uri) {} + + function setURI(string memory newuri) public { + _setURI(newuri); + } + + function mint( + address to, + uint256 id, + uint256 value, + bytes memory data + ) public { + _mint(to, id, value, data); + } + + function mintBatch( + address to, + uint256[] memory ids, + uint256[] memory values, + bytes memory data + ) public { + _mintBatch(to, ids, values, data); + } + + function burn( + address owner, + uint256 id, + uint256 value + ) public { + _burn(owner, id, value); + } + + function burnBatch( + address owner, + uint256[] memory ids, + uint256[] memory values + ) public { + _burnBatch(owner, ids, values); + } +} diff --git a/certora/munged/mocks/ERC1155PausableMock.sol b/certora/munged/mocks/ERC1155PausableMock.sol new file mode 100644 index 000000000..b1a4a8e1e --- /dev/null +++ b/certora/munged/mocks/ERC1155PausableMock.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ERC1155Mock.sol"; +import "../token/ERC1155/extensions/ERC1155Pausable.sol"; + +contract ERC1155PausableMock is ERC1155Mock, ERC1155Pausable { + constructor(string memory uri) ERC1155Mock(uri) {} + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } + + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override(ERC1155, ERC1155Pausable) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + } +} diff --git a/certora/munged/mocks/ERC1155ReceiverMock.sol b/certora/munged/mocks/ERC1155ReceiverMock.sol new file mode 100644 index 000000000..6443a56c7 --- /dev/null +++ b/certora/munged/mocks/ERC1155ReceiverMock.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC1155/IERC1155Receiver.sol"; +import "../utils/introspection/ERC165.sol"; + +contract ERC1155ReceiverMock is ERC165, IERC1155Receiver { + bytes4 private _recRetval; + bool private _recReverts; + bytes4 private _batRetval; + bool private _batReverts; + + event Received(address operator, address from, uint256 id, uint256 value, bytes data, uint256 gas); + event BatchReceived(address operator, address from, uint256[] ids, uint256[] values, bytes data, uint256 gas); + + constructor( + bytes4 recRetval, + bool recReverts, + bytes4 batRetval, + bool batReverts + ) { + _recRetval = recRetval; + _recReverts = recReverts; + _batRetval = batRetval; + _batReverts = batReverts; + } + + function onERC1155Received( + address operator, + address from, + uint256 id, + uint256 value, + bytes calldata data + ) external override returns (bytes4) { + require(!_recReverts, "ERC1155ReceiverMock: reverting on receive"); + emit Received(operator, from, id, value, data, gasleft()); + return _recRetval; + } + + function onERC1155BatchReceived( + address operator, + address from, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external override returns (bytes4) { + require(!_batReverts, "ERC1155ReceiverMock: reverting on batch receive"); + emit BatchReceived(operator, from, ids, values, data, gasleft()); + return _batRetval; + } +} diff --git a/certora/munged/mocks/ERC1155SupplyMock.sol b/certora/munged/mocks/ERC1155SupplyMock.sol new file mode 100644 index 000000000..44b208007 --- /dev/null +++ b/certora/munged/mocks/ERC1155SupplyMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ERC1155Mock.sol"; +import "../token/ERC1155/extensions/ERC1155Supply.sol"; + +contract ERC1155SupplyMock is ERC1155Mock, ERC1155Supply { + constructor(string memory uri) ERC1155Mock(uri) {} + + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override(ERC1155, ERC1155Supply) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + } +} diff --git a/certora/munged/mocks/ERC1271WalletMock.sol b/certora/munged/mocks/ERC1271WalletMock.sol new file mode 100644 index 000000000..c92acdba6 --- /dev/null +++ b/certora/munged/mocks/ERC1271WalletMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/Ownable.sol"; +import "../interfaces/IERC1271.sol"; +import "../utils/cryptography/ECDSA.sol"; + +contract ERC1271WalletMock is Ownable, IERC1271 { + constructor(address originalOwner) { + transferOwnership(originalOwner); + } + + function isValidSignature(bytes32 hash, bytes memory signature) public view override returns (bytes4 magicValue) { + return ECDSA.recover(hash, signature) == owner() ? this.isValidSignature.selector : bytes4(0); + } +} diff --git a/certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol b/certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol new file mode 100644 index 000000000..7a5e5bc67 --- /dev/null +++ b/certora/munged/mocks/ERC165/ERC165InterfacesSupported.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * https://eips.ethereum.org/EIPS/eip-214#specification + * From the specification: + * > Any attempts to make state-changing operations inside an execution instance with STATIC set to true will instead + * throw an exception. + * > These operations include [...], LOG0, LOG1, LOG2, [...] + * + * therefore, because this contract is staticcall'd we need to not emit events (which is how solidity-coverage works) + * solidity-coverage ignores the /mocks folder, so we duplicate its implementation here to avoid instrumenting it + */ +contract SupportsInterfaceWithLookupMock is IERC165 { + /* + * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 + */ + bytes4 public constant INTERFACE_ID_ERC165 = 0x01ffc9a7; + + /** + * @dev A mapping of interface id to whether or not it's supported. + */ + mapping(bytes4 => bool) private _supportedInterfaces; + + /** + * @dev A contract implementing SupportsInterfaceWithLookup + * implement ERC165 itself. + */ + constructor() { + _registerInterface(INTERFACE_ID_ERC165); + } + + /** + * @dev Implement supportsInterface(bytes4) using a lookup table. + */ + function supportsInterface(bytes4 interfaceId) public view override returns (bool) { + return _supportedInterfaces[interfaceId]; + } + + /** + * @dev Private method for registering an interface. + */ + function _registerInterface(bytes4 interfaceId) internal { + require(interfaceId != 0xffffffff, "ERC165InterfacesSupported: invalid interface id"); + _supportedInterfaces[interfaceId] = true; + } +} + +contract ERC165InterfacesSupported is SupportsInterfaceWithLookupMock { + constructor(bytes4[] memory interfaceIds) { + for (uint256 i = 0; i < interfaceIds.length; i++) { + _registerInterface(interfaceIds[i]); + } + } +} diff --git a/certora/munged/mocks/ERC165/ERC165MissingData.sol b/certora/munged/mocks/ERC165/ERC165MissingData.sol new file mode 100644 index 000000000..59cd51ae6 --- /dev/null +++ b/certora/munged/mocks/ERC165/ERC165MissingData.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract ERC165MissingData { + function supportsInterface(bytes4 interfaceId) public view {} // missing return +} diff --git a/certora/munged/mocks/ERC165/ERC165NotSupported.sol b/certora/munged/mocks/ERC165/ERC165NotSupported.sol new file mode 100644 index 000000000..486c7f0a4 --- /dev/null +++ b/certora/munged/mocks/ERC165/ERC165NotSupported.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract ERC165NotSupported {} diff --git a/certora/munged/mocks/ERC165CheckerMock.sol b/certora/munged/mocks/ERC165CheckerMock.sol new file mode 100644 index 000000000..bda5cfc78 --- /dev/null +++ b/certora/munged/mocks/ERC165CheckerMock.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165Checker.sol"; + +contract ERC165CheckerMock { + using ERC165Checker for address; + + function supportsERC165(address account) public view returns (bool) { + return account.supportsERC165(); + } + + function supportsInterface(address account, bytes4 interfaceId) public view returns (bool) { + return account.supportsInterface(interfaceId); + } + + function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool) { + return account.supportsAllInterfaces(interfaceIds); + } + + function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool[] memory) { + return account.getSupportedInterfaces(interfaceIds); + } +} diff --git a/certora/munged/mocks/ERC165Mock.sol b/certora/munged/mocks/ERC165Mock.sol new file mode 100644 index 000000000..c123d0ab2 --- /dev/null +++ b/certora/munged/mocks/ERC165Mock.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165.sol"; + +contract ERC165Mock is ERC165 {} diff --git a/certora/munged/mocks/ERC165StorageMock.sol b/certora/munged/mocks/ERC165StorageMock.sol new file mode 100644 index 000000000..4b0bae908 --- /dev/null +++ b/certora/munged/mocks/ERC165StorageMock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165Storage.sol"; + +contract ERC165StorageMock is ERC165Storage { + function registerInterface(bytes4 interfaceId) public { + _registerInterface(interfaceId); + } +} diff --git a/certora/munged/mocks/ERC1820ImplementerMock.sol b/certora/munged/mocks/ERC1820ImplementerMock.sol new file mode 100644 index 000000000..a6012d7ff --- /dev/null +++ b/certora/munged/mocks/ERC1820ImplementerMock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC1820Implementer.sol"; + +contract ERC1820ImplementerMock is ERC1820Implementer { + function registerInterfaceForAddress(bytes32 interfaceHash, address account) public { + _registerInterfaceForAddress(interfaceHash, account); + } +} diff --git a/certora/munged/mocks/ERC20BurnableMock.sol b/certora/munged/mocks/ERC20BurnableMock.sol new file mode 100644 index 000000000..0ed6c0c98 --- /dev/null +++ b/certora/munged/mocks/ERC20BurnableMock.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Burnable.sol"; + +contract ERC20BurnableMock is ERC20Burnable { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } +} diff --git a/certora/munged/mocks/ERC20CappedMock.sol b/certora/munged/mocks/ERC20CappedMock.sol new file mode 100644 index 000000000..edb36f205 --- /dev/null +++ b/certora/munged/mocks/ERC20CappedMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Capped.sol"; + +contract ERC20CappedMock is ERC20Capped { + constructor( + string memory name, + string memory symbol, + uint256 cap + ) ERC20(name, symbol) ERC20Capped(cap) {} + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } +} diff --git a/certora/munged/mocks/ERC20DecimalsMock.sol b/certora/munged/mocks/ERC20DecimalsMock.sol new file mode 100644 index 000000000..924c3af31 --- /dev/null +++ b/certora/munged/mocks/ERC20DecimalsMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/ERC20.sol"; + +contract ERC20DecimalsMock is ERC20 { + uint8 private immutable _decimals; + + constructor( + string memory name_, + string memory symbol_, + uint8 decimals_ + ) ERC20(name_, symbol_) { + _decimals = decimals_; + } + + function decimals() public view virtual override returns (uint8) { + return _decimals; + } +} diff --git a/certora/munged/mocks/ERC20FlashMintMock.sol b/certora/munged/mocks/ERC20FlashMintMock.sol new file mode 100644 index 000000000..0bb7871fc --- /dev/null +++ b/certora/munged/mocks/ERC20FlashMintMock.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20FlashMint.sol"; + +contract ERC20FlashMintMock is ERC20FlashMint { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } +} diff --git a/certora/munged/mocks/ERC20Mock.sol b/certora/munged/mocks/ERC20Mock.sol new file mode 100644 index 000000000..fd7f991ba --- /dev/null +++ b/certora/munged/mocks/ERC20Mock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/ERC20.sol"; + +// mock class using ERC20 +contract ERC20Mock is ERC20 { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) payable ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } + + function transferInternal( + address from, + address to, + uint256 value + ) public { + _transfer(from, to, value); + } + + function approveInternal( + address owner, + address spender, + uint256 value + ) public { + _approve(owner, spender, value); + } +} diff --git a/certora/munged/mocks/ERC20PausableMock.sol b/certora/munged/mocks/ERC20PausableMock.sol new file mode 100644 index 000000000..19160ba6c --- /dev/null +++ b/certora/munged/mocks/ERC20PausableMock.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Pausable.sol"; + +// mock class using ERC20Pausable +contract ERC20PausableMock is ERC20Pausable { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } + + function mint(address to, uint256 amount) public { + _mint(to, amount); + } + + function burn(address from, uint256 amount) public { + _burn(from, amount); + } +} diff --git a/certora/munged/mocks/ERC20PermitMock.sol b/certora/munged/mocks/ERC20PermitMock.sol new file mode 100644 index 000000000..20302bfa0 --- /dev/null +++ b/certora/munged/mocks/ERC20PermitMock.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/draft-ERC20Permit.sol"; + +contract ERC20PermitMock is ERC20Permit { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) payable ERC20(name, symbol) ERC20Permit(name) { + _mint(initialAccount, initialBalance); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/certora/munged/mocks/ERC20SnapshotMock.sol b/certora/munged/mocks/ERC20SnapshotMock.sol new file mode 100644 index 000000000..cb3048322 --- /dev/null +++ b/certora/munged/mocks/ERC20SnapshotMock.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Snapshot.sol"; + +contract ERC20SnapshotMock is ERC20Snapshot { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } + + function snapshot() public { + _snapshot(); + } + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } +} diff --git a/certora/munged/mocks/ERC20VotesCompMock.sol b/certora/munged/mocks/ERC20VotesCompMock.sol new file mode 100644 index 000000000..171071fd5 --- /dev/null +++ b/certora/munged/mocks/ERC20VotesCompMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20VotesComp.sol"; + +contract ERC20VotesCompMock is ERC20VotesComp { + constructor(string memory name, string memory symbol) ERC20(name, symbol) ERC20Permit(name) {} + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/certora/munged/mocks/ERC20VotesMock.sol b/certora/munged/mocks/ERC20VotesMock.sol new file mode 100644 index 000000000..0975e8b9f --- /dev/null +++ b/certora/munged/mocks/ERC20VotesMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Votes.sol"; + +contract ERC20VotesMock is ERC20Votes { + constructor(string memory name, string memory symbol) ERC20(name, symbol) ERC20Permit(name) {} + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/certora/munged/mocks/ERC20WrapperMock.sol b/certora/munged/mocks/ERC20WrapperMock.sol new file mode 100644 index 000000000..cf34a7a52 --- /dev/null +++ b/certora/munged/mocks/ERC20WrapperMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Wrapper.sol"; + +contract ERC20WrapperMock is ERC20Wrapper { + constructor( + IERC20 _underlyingToken, + string memory name, + string memory symbol + ) ERC20(name, symbol) ERC20Wrapper(_underlyingToken) {} + + function recover(address account) public returns (uint256) { + return _recover(account); + } +} diff --git a/certora/munged/mocks/ERC2771ContextMock.sol b/certora/munged/mocks/ERC2771ContextMock.sol new file mode 100644 index 000000000..ee111d1ae --- /dev/null +++ b/certora/munged/mocks/ERC2771ContextMock.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.9; + +import "./ContextMock.sol"; +import "../metatx/ERC2771Context.sol"; + +// By inheriting from ERC2771Context, Context's internal functions are overridden automatically +contract ERC2771ContextMock is ContextMock, ERC2771Context { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address trustedForwarder) ERC2771Context(trustedForwarder) { + emit Sender(_msgSender()); // _msgSender() should be accessible during construction + } + + function _msgSender() internal view virtual override(Context, ERC2771Context) returns (address) { + return ERC2771Context._msgSender(); + } + + function _msgData() internal view virtual override(Context, ERC2771Context) returns (bytes calldata) { + return ERC2771Context._msgData(); + } +} diff --git a/certora/munged/mocks/ERC3156FlashBorrowerMock.sol b/certora/munged/mocks/ERC3156FlashBorrowerMock.sol new file mode 100644 index 000000000..288a278fb --- /dev/null +++ b/certora/munged/mocks/ERC3156FlashBorrowerMock.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/IERC20.sol"; +import "../interfaces/IERC3156.sol"; +import "../utils/Address.sol"; + +/** + * @dev WARNING: this IERC3156FlashBorrower mock implementation is for testing purposes ONLY. + * Writing a secure flash lock borrower is not an easy task, and should be done with the utmost care. + * This is not an example of how it should be done, and no pattern present in this mock should be considered secure. + * Following best practices, always have your contract properly audited before using them to manipulate important funds on + * live networks. + */ +contract ERC3156FlashBorrowerMock is IERC3156FlashBorrower { + bytes32 internal constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); + + bool immutable _enableApprove; + bool immutable _enableReturn; + + event BalanceOf(address token, address account, uint256 value); + event TotalSupply(address token, uint256 value); + + constructor(bool enableReturn, bool enableApprove) { + _enableApprove = enableApprove; + _enableReturn = enableReturn; + } + + function onFlashLoan( + address, /*initiator*/ + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) public override returns (bytes32) { + require(msg.sender == token); + + emit BalanceOf(token, address(this), IERC20(token).balanceOf(address(this))); + emit TotalSupply(token, IERC20(token).totalSupply()); + + if (data.length > 0) { + // WARNING: This code is for testing purposes only! Do not use. + Address.functionCall(token, data); + } + + if (_enableApprove) { + IERC20(token).approve(token, amount + fee); + } + + return _enableReturn ? _RETURN_VALUE : bytes32(0); + } +} diff --git a/certora/munged/mocks/ERC721BurnableMock.sol b/certora/munged/mocks/ERC721BurnableMock.sol new file mode 100644 index 000000000..b30dbf53d --- /dev/null +++ b/certora/munged/mocks/ERC721BurnableMock.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Burnable.sol"; + +contract ERC721BurnableMock is ERC721Burnable { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) public { + _safeMint(to, tokenId, _data); + } +} diff --git a/certora/munged/mocks/ERC721EnumerableMock.sol b/certora/munged/mocks/ERC721EnumerableMock.sol new file mode 100644 index 000000000..73aee9d04 --- /dev/null +++ b/certora/munged/mocks/ERC721EnumerableMock.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Enumerable.sol"; + +/** + * @title ERC721Mock + * This mock just provides a public safeMint, mint, and burn functions for testing purposes + */ +contract ERC721EnumerableMock is ERC721Enumerable { + string private _baseTokenURI; + + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function _baseURI() internal view virtual override returns (string memory) { + return _baseTokenURI; + } + + function setBaseURI(string calldata newBaseTokenURI) public { + _baseTokenURI = newBaseTokenURI; + } + + function baseURI() public view returns (string memory) { + return _baseURI(); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/certora/munged/mocks/ERC721Mock.sol b/certora/munged/mocks/ERC721Mock.sol new file mode 100644 index 000000000..74a092334 --- /dev/null +++ b/certora/munged/mocks/ERC721Mock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/ERC721.sol"; + +/** + * @title ERC721Mock + * This mock just provides a public safeMint, mint, and burn functions for testing purposes + */ +contract ERC721Mock is ERC721 { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function baseURI() public view returns (string memory) { + return _baseURI(); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/certora/munged/mocks/ERC721PausableMock.sol b/certora/munged/mocks/ERC721PausableMock.sol new file mode 100644 index 000000000..8d8e818fb --- /dev/null +++ b/certora/munged/mocks/ERC721PausableMock.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Pausable.sol"; + +/** + * @title ERC721PausableMock + * This mock just provides a public mint, burn and exists functions for testing purposes + */ +contract ERC721PausableMock is ERC721Pausable { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/certora/munged/mocks/ERC721ReceiverMock.sol b/certora/munged/mocks/ERC721ReceiverMock.sol new file mode 100644 index 000000000..a4923bfd5 --- /dev/null +++ b/certora/munged/mocks/ERC721ReceiverMock.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/IERC721Receiver.sol"; + +contract ERC721ReceiverMock is IERC721Receiver { + enum Error { + None, + RevertWithMessage, + RevertWithoutMessage, + Panic + } + + bytes4 private immutable _retval; + Error private immutable _error; + + event Received(address operator, address from, uint256 tokenId, bytes data, uint256 gas); + + constructor(bytes4 retval, Error error) { + _retval = retval; + _error = error; + } + + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes memory data + ) public override returns (bytes4) { + if (_error == Error.RevertWithMessage) { + revert("ERC721ReceiverMock: reverting"); + } else if (_error == Error.RevertWithoutMessage) { + revert(); + } else if (_error == Error.Panic) { + uint256 a = uint256(0) / uint256(0); + a; + } + emit Received(operator, from, tokenId, data, gasleft()); + return _retval; + } +} diff --git a/certora/munged/mocks/ERC721RoyaltyMock.sol b/certora/munged/mocks/ERC721RoyaltyMock.sol new file mode 100644 index 000000000..83a9074e2 --- /dev/null +++ b/certora/munged/mocks/ERC721RoyaltyMock.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Royalty.sol"; + +contract ERC721RoyaltyMock is ERC721Royalty { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function setTokenRoyalty( + uint256 tokenId, + address recipient, + uint96 fraction + ) public { + _setTokenRoyalty(tokenId, recipient, fraction); + } + + function setDefaultRoyalty(address recipient, uint96 fraction) public { + _setDefaultRoyalty(recipient, fraction); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } + + function deleteDefaultRoyalty() public { + _deleteDefaultRoyalty(); + } +} diff --git a/certora/munged/mocks/ERC721URIStorageMock.sol b/certora/munged/mocks/ERC721URIStorageMock.sol new file mode 100644 index 000000000..9c3480f71 --- /dev/null +++ b/certora/munged/mocks/ERC721URIStorageMock.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721URIStorage.sol"; + +/** + * @title ERC721Mock + * This mock just provides a public safeMint, mint, and burn functions for testing purposes + */ +contract ERC721URIStorageMock is ERC721URIStorage { + string private _baseTokenURI; + + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function _baseURI() internal view virtual override returns (string memory) { + return _baseTokenURI; + } + + function setBaseURI(string calldata newBaseTokenURI) public { + _baseTokenURI = newBaseTokenURI; + } + + function baseURI() public view returns (string memory) { + return _baseURI(); + } + + function setTokenURI(uint256 tokenId, string memory _tokenURI) public { + _setTokenURI(tokenId, _tokenURI); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/certora/munged/mocks/ERC721VotesMock.sol b/certora/munged/mocks/ERC721VotesMock.sol new file mode 100644 index 000000000..0755ace66 --- /dev/null +++ b/certora/munged/mocks/ERC721VotesMock.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/draft-ERC721Votes.sol"; + +contract ERC721VotesMock is ERC721Votes { + constructor(string memory name, string memory symbol) ERC721(name, symbol) EIP712(name, "1") {} + + function getTotalSupply() public view returns (uint256) { + return _getTotalSupply(); + } + + function mint(address account, uint256 tokenId) public { + _mint(account, tokenId); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/certora/munged/mocks/ERC777Mock.sol b/certora/munged/mocks/ERC777Mock.sol new file mode 100644 index 000000000..f8a3b6784 --- /dev/null +++ b/certora/munged/mocks/ERC777Mock.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; +import "../token/ERC777/ERC777.sol"; + +contract ERC777Mock is Context, ERC777 { + event BeforeTokenTransfer(); + + constructor( + address initialHolder, + uint256 initialBalance, + string memory name, + string memory symbol, + address[] memory defaultOperators + ) ERC777(name, symbol, defaultOperators) { + _mint(initialHolder, initialBalance, "", ""); + } + + function mintInternal( + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData + ) public { + _mint(to, amount, userData, operatorData); + } + + function mintInternalExtended( + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) public { + _mint(to, amount, userData, operatorData, requireReceptionAck); + } + + function approveInternal( + address holder, + address spender, + uint256 value + ) public { + _approve(holder, spender, value); + } + + function _beforeTokenTransfer( + address, + address, + address, + uint256 + ) internal override { + emit BeforeTokenTransfer(); + } +} diff --git a/certora/munged/mocks/ERC777SenderRecipientMock.sol b/certora/munged/mocks/ERC777SenderRecipientMock.sol new file mode 100644 index 000000000..169912f69 --- /dev/null +++ b/certora/munged/mocks/ERC777SenderRecipientMock.sol @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777.sol"; +import "../token/ERC777/IERC777Sender.sol"; +import "../token/ERC777/IERC777Recipient.sol"; +import "../utils/Context.sol"; +import "../utils/introspection/IERC1820Registry.sol"; +import "../utils/introspection/ERC1820Implementer.sol"; + +contract ERC777SenderRecipientMock is Context, IERC777Sender, IERC777Recipient, ERC1820Implementer { + event TokensToSendCalled( + address operator, + address from, + address to, + uint256 amount, + bytes data, + bytes operatorData, + address token, + uint256 fromBalance, + uint256 toBalance + ); + + event TokensReceivedCalled( + address operator, + address from, + address to, + uint256 amount, + bytes data, + bytes operatorData, + address token, + uint256 fromBalance, + uint256 toBalance + ); + + // Emitted in ERC777Mock. Here for easier decoding + event BeforeTokenTransfer(); + + bool private _shouldRevertSend; + bool private _shouldRevertReceive; + + IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + + bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); + bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + + function tokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external override { + if (_shouldRevertSend) { + revert(); + } + + IERC777 token = IERC777(_msgSender()); + + uint256 fromBalance = token.balanceOf(from); + // when called due to burn, to will be the zero address, which will have a balance of 0 + uint256 toBalance = token.balanceOf(to); + + emit TokensToSendCalled( + operator, + from, + to, + amount, + userData, + operatorData, + address(token), + fromBalance, + toBalance + ); + } + + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external override { + if (_shouldRevertReceive) { + revert(); + } + + IERC777 token = IERC777(_msgSender()); + + uint256 fromBalance = token.balanceOf(from); + // when called due to burn, to will be the zero address, which will have a balance of 0 + uint256 toBalance = token.balanceOf(to); + + emit TokensReceivedCalled( + operator, + from, + to, + amount, + userData, + operatorData, + address(token), + fromBalance, + toBalance + ); + } + + function senderFor(address account) public { + _registerInterfaceForAddress(_TOKENS_SENDER_INTERFACE_HASH, account); + + address self = address(this); + if (account == self) { + registerSender(self); + } + } + + function registerSender(address sender) public { + _erc1820.setInterfaceImplementer(address(this), _TOKENS_SENDER_INTERFACE_HASH, sender); + } + + function recipientFor(address account) public { + _registerInterfaceForAddress(_TOKENS_RECIPIENT_INTERFACE_HASH, account); + + address self = address(this); + if (account == self) { + registerRecipient(self); + } + } + + function registerRecipient(address recipient) public { + _erc1820.setInterfaceImplementer(address(this), _TOKENS_RECIPIENT_INTERFACE_HASH, recipient); + } + + function setShouldRevertSend(bool shouldRevert) public { + _shouldRevertSend = shouldRevert; + } + + function setShouldRevertReceive(bool shouldRevert) public { + _shouldRevertReceive = shouldRevert; + } + + function send( + IERC777 token, + address to, + uint256 amount, + bytes memory data + ) public { + // This is 777's send function, not the Solidity send function + token.send(to, amount, data); // solhint-disable-line check-send-result + } + + function burn( + IERC777 token, + uint256 amount, + bytes memory data + ) public { + token.burn(amount, data); + } +} diff --git a/certora/munged/mocks/EnumerableMapMock.sol b/certora/munged/mocks/EnumerableMapMock.sol new file mode 100644 index 000000000..21c1fe362 --- /dev/null +++ b/certora/munged/mocks/EnumerableMapMock.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/structs/EnumerableMap.sol"; + +// UintToAddressMap +contract UintToAddressMapMock { + using EnumerableMap for EnumerableMap.UintToAddressMap; + + event OperationResult(bool result); + + EnumerableMap.UintToAddressMap private _map; + + function contains(uint256 key) public view returns (bool) { + return _map.contains(key); + } + + function set(uint256 key, address value) public { + bool result = _map.set(key, value); + emit OperationResult(result); + } + + function remove(uint256 key) public { + bool result = _map.remove(key); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _map.length(); + } + + function at(uint256 index) public view returns (uint256 key, address value) { + return _map.at(index); + } + + function tryGet(uint256 key) public view returns (bool, address) { + return _map.tryGet(key); + } + + function get(uint256 key) public view returns (address) { + return _map.get(key); + } + + function getWithMessage(uint256 key, string calldata errorMessage) public view returns (address) { + return _map.get(key, errorMessage); + } +} + +// AddressToUintMap +contract AddressToUintMapMock { + using EnumerableMap for EnumerableMap.AddressToUintMap; + + event OperationResult(bool result); + + EnumerableMap.AddressToUintMap private _map; + + function contains(address key) public view returns (bool) { + return _map.contains(key); + } + + function set(address key, uint256 value) public { + bool result = _map.set(key, value); + emit OperationResult(result); + } + + function remove(address key) public { + bool result = _map.remove(key); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _map.length(); + } + + function at(uint256 index) public view returns (address key, uint256 value) { + return _map.at(index); + } + + function tryGet(address key) public view returns (bool, uint256) { + return _map.tryGet(key); + } + + function get(address key) public view returns (uint256) { + return _map.get(key); + } +} diff --git a/certora/munged/mocks/EnumerableSetMock.sol b/certora/munged/mocks/EnumerableSetMock.sol new file mode 100644 index 000000000..922ce46d2 --- /dev/null +++ b/certora/munged/mocks/EnumerableSetMock.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/structs/EnumerableSet.sol"; + +// Bytes32Set +contract EnumerableBytes32SetMock { + using EnumerableSet for EnumerableSet.Bytes32Set; + + event OperationResult(bool result); + + EnumerableSet.Bytes32Set private _set; + + function contains(bytes32 value) public view returns (bool) { + return _set.contains(value); + } + + function add(bytes32 value) public { + bool result = _set.add(value); + emit OperationResult(result); + } + + function remove(bytes32 value) public { + bool result = _set.remove(value); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _set.length(); + } + + function at(uint256 index) public view returns (bytes32) { + return _set.at(index); + } + + function values() public view returns (bytes32[] memory) { + return _set.values(); + } +} + +// AddressSet +contract EnumerableAddressSetMock { + using EnumerableSet for EnumerableSet.AddressSet; + + event OperationResult(bool result); + + EnumerableSet.AddressSet private _set; + + function contains(address value) public view returns (bool) { + return _set.contains(value); + } + + function add(address value) public { + bool result = _set.add(value); + emit OperationResult(result); + } + + function remove(address value) public { + bool result = _set.remove(value); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _set.length(); + } + + function at(uint256 index) public view returns (address) { + return _set.at(index); + } + + function values() public view returns (address[] memory) { + return _set.values(); + } +} + +// UintSet +contract EnumerableUintSetMock { + using EnumerableSet for EnumerableSet.UintSet; + + event OperationResult(bool result); + + EnumerableSet.UintSet private _set; + + function contains(uint256 value) public view returns (bool) { + return _set.contains(value); + } + + function add(uint256 value) public { + bool result = _set.add(value); + emit OperationResult(result); + } + + function remove(uint256 value) public { + bool result = _set.remove(value); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _set.length(); + } + + function at(uint256 index) public view returns (uint256) { + return _set.at(index); + } + + function values() public view returns (uint256[] memory) { + return _set.values(); + } +} diff --git a/certora/munged/mocks/EtherReceiverMock.sol b/certora/munged/mocks/EtherReceiverMock.sol new file mode 100644 index 000000000..a11e646fb --- /dev/null +++ b/certora/munged/mocks/EtherReceiverMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract EtherReceiverMock { + bool private _acceptEther; + + function setAcceptEther(bool acceptEther) public { + _acceptEther = acceptEther; + } + + receive() external payable { + if (!_acceptEther) { + revert(); + } + } +} diff --git a/certora/munged/mocks/GovernorCompMock.sol b/certora/munged/mocks/GovernorCompMock.sol new file mode 100644 index 000000000..c2d8733e0 --- /dev/null +++ b/certora/munged/mocks/GovernorCompMock.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesComp.sol"; + +contract GovernorCompMock is GovernorVotesComp, GovernorCountingSimple { + constructor(string memory name_, ERC20VotesComp token_) Governor(name_) GovernorVotesComp(token_) {} + + function quorum(uint256) public pure override returns (uint256) { + return 0; + } + + function votingDelay() public pure override returns (uint256) { + return 4; + } + + function votingPeriod() public pure override returns (uint256) { + return 16; + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } +} diff --git a/certora/munged/mocks/GovernorCompatibilityBravoMock.sol b/certora/munged/mocks/GovernorCompatibilityBravoMock.sol new file mode 100644 index 000000000..8f295f62f --- /dev/null +++ b/certora/munged/mocks/GovernorCompatibilityBravoMock.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/compatibility/GovernorCompatibilityBravo.sol"; +import "../governance/extensions/GovernorTimelockCompound.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorVotesComp.sol"; + +contract GovernorCompatibilityBravoMock is + GovernorCompatibilityBravo, + GovernorSettings, + GovernorTimelockCompound, + GovernorVotesComp +{ + constructor( + string memory name_, + ERC20VotesComp token_, + uint256 votingDelay_, + uint256 votingPeriod_, + uint256 proposalThreshold_, + ICompoundTimelock timelock_ + ) + Governor(name_) + GovernorTimelockCompound(timelock_) + GovernorSettings(votingDelay_, votingPeriod_, proposalThreshold_) + GovernorVotesComp(token_) + {} + + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(IERC165, Governor, GovernorTimelockCompound) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function quorum(uint256) public pure override returns (uint256) { + return 0; + } + + function state(uint256 proposalId) + public + view + virtual + override(IGovernor, Governor, GovernorTimelockCompound) + returns (ProposalState) + { + return super.state(proposalId); + } + + function proposalEta(uint256 proposalId) + public + view + virtual + override(IGovernorTimelock, GovernorTimelockCompound) + returns (uint256) + { + return super.proposalEta(proposalId); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override(IGovernor, Governor, GovernorCompatibilityBravo) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public virtual override(IGovernorTimelock, GovernorTimelockCompound) returns (uint256) { + return super.queue(targets, values, calldatas, salt); + } + + function execute( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public payable virtual override(IGovernor, Governor) returns (uint256) { + return super.execute(targets, values, calldatas, salt); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override(Governor, GovernorTimelockCompound) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + /** + * @notice WARNING: this is for mock purposes only. Ability to the _cancel function should be restricted for live + * deployments. + */ + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) internal virtual override(Governor, GovernorTimelockCompound) returns (uint256 proposalId) { + return super._cancel(targets, values, calldatas, salt); + } + + function _executor() internal view virtual override(Governor, GovernorTimelockCompound) returns (address) { + return super._executor(); + } +} diff --git a/certora/munged/mocks/GovernorMock.sol b/certora/munged/mocks/GovernorMock.sol new file mode 100644 index 000000000..dafb0a0a7 --- /dev/null +++ b/certora/munged/mocks/GovernorMock.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorProposalThreshold.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesQuorumFraction.sol"; + +contract GovernorMock is + GovernorProposalThreshold, + GovernorSettings, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + string memory name_, + IVotes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + uint256 quorumNumerator_ + ) + Governor(name_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorVotesQuorumFraction(quorumNumerator_) + {} + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override(Governor, GovernorProposalThreshold) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } +} diff --git a/certora/munged/mocks/GovernorPreventLateQuorumMock.sol b/certora/munged/mocks/GovernorPreventLateQuorumMock.sol new file mode 100644 index 000000000..35bddc03b --- /dev/null +++ b/certora/munged/mocks/GovernorPreventLateQuorumMock.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorPreventLateQuorum.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotes.sol"; + +contract GovernorPreventLateQuorumMock is + GovernorSettings, + GovernorVotes, + GovernorCountingSimple, + GovernorPreventLateQuorum +{ + uint256 private _quorum; + + constructor( + string memory name_, + IVotes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + uint256 quorum_, + uint64 voteExtension_ + ) + Governor(name_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorPreventLateQuorum(voteExtension_) + { + _quorum = quorum_; + } + + function quorum(uint256) public view virtual override returns (uint256) { + return _quorum; + } + + function proposalDeadline(uint256 proposalId) + public + view + virtual + override(Governor, GovernorPreventLateQuorum) + returns (uint256) + { + return super.proposalDeadline(proposalId); + } + + function proposalThreshold() public view virtual override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason, + bytes memory params + ) internal virtual override(Governor, GovernorPreventLateQuorum) returns (uint256) { + return super._castVote(proposalId, account, support, reason, params); + } +} diff --git a/certora/munged/mocks/GovernorTimelockCompoundMock.sol b/certora/munged/mocks/GovernorTimelockCompoundMock.sol new file mode 100644 index 000000000..88a882928 --- /dev/null +++ b/certora/munged/mocks/GovernorTimelockCompoundMock.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorTimelockCompound.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesQuorumFraction.sol"; + +contract GovernorTimelockCompoundMock is + GovernorSettings, + GovernorTimelockCompound, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + string memory name_, + IVotes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + ICompoundTimelock timelock_, + uint256 quorumNumerator_ + ) + Governor(name_) + GovernorTimelockCompound(timelock_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorVotesQuorumFraction(quorumNumerator_) + {} + + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(Governor, GovernorTimelockCompound) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } + + /** + * Overriding nightmare + */ + function state(uint256 proposalId) + public + view + virtual + override(Governor, GovernorTimelockCompound) + returns (ProposalState) + { + return super.state(proposalId); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override(Governor, GovernorTimelockCompound) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) internal virtual override(Governor, GovernorTimelockCompound) returns (uint256 proposalId) { + return super._cancel(targets, values, calldatas, salt); + } + + function _executor() internal view virtual override(Governor, GovernorTimelockCompound) returns (address) { + return super._executor(); + } +} diff --git a/certora/munged/mocks/GovernorTimelockControlMock.sol b/certora/munged/mocks/GovernorTimelockControlMock.sol new file mode 100644 index 000000000..455eac9ba --- /dev/null +++ b/certora/munged/mocks/GovernorTimelockControlMock.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorTimelockControl.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesQuorumFraction.sol"; + +contract GovernorTimelockControlMock is + GovernorSettings, + GovernorTimelockControl, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + string memory name_, + IVotes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + TimelockController timelock_, + uint256 quorumNumerator_ + ) + Governor(name_) + GovernorTimelockControl(timelock_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorVotesQuorumFraction(quorumNumerator_) + {} + + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(Governor, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, descriptionHash); + } + + /** + * Overriding nightmare + */ + function state(uint256 proposalId) + public + view + virtual + override(Governor, GovernorTimelockControl) + returns (ProposalState) + { + return super.state(proposalId); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override(Governor, GovernorTimelockControl) returns (uint256 proposalId) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view virtual override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function nonGovernanceFunction() external {} +} diff --git a/certora/munged/mocks/GovernorVoteMock.sol b/certora/munged/mocks/GovernorVoteMock.sol new file mode 100644 index 000000000..60a3d4135 --- /dev/null +++ b/certora/munged/mocks/GovernorVoteMock.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotes.sol"; + +contract GovernorVoteMocks is GovernorVotes, GovernorCountingSimple { + constructor(string memory name_, IVotes token_) Governor(name_) GovernorVotes(token_) {} + + function quorum(uint256) public pure override returns (uint256) { + return 0; + } + + function votingDelay() public pure override returns (uint256) { + return 4; + } + + function votingPeriod() public pure override returns (uint256) { + return 16; + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } +} diff --git a/certora/munged/mocks/GovernorWithParamsMock.sol b/certora/munged/mocks/GovernorWithParamsMock.sol new file mode 100644 index 000000000..35eb7ade2 --- /dev/null +++ b/certora/munged/mocks/GovernorWithParamsMock.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotes.sol"; + +contract GovernorWithParamsMock is GovernorVotes, GovernorCountingSimple { + event CountParams(uint256 uintParam, string strParam); + + constructor(string memory name_, IVotes token_) Governor(name_) GovernorVotes(token_) {} + + function quorum(uint256) public pure override returns (uint256) { + return 0; + } + + function votingDelay() public pure override returns (uint256) { + return 4; + } + + function votingPeriod() public pure override returns (uint256) { + return 16; + } + + function _getVotes( + address account, + uint256 blockNumber, + bytes memory params + ) internal view virtual override(Governor, GovernorVotes) returns (uint256) { + uint256 reduction = 0; + // If the user provides parameters, we reduce the voting weight by the amount of the integer param + if (params.length > 0) { + (reduction, ) = abi.decode(params, (uint256, string)); + } + // reverts on overflow + return super._getVotes(account, blockNumber, params) - reduction; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight, + bytes memory params + ) internal virtual override(Governor, GovernorCountingSimple) { + if (params.length > 0) { + (uint256 _uintParam, string memory _strParam) = abi.decode(params, (uint256, string)); + emit CountParams(_uintParam, _strParam); + } + return super._countVote(proposalId, account, support, weight, params); + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } +} diff --git a/certora/munged/mocks/InitializableMock.sol b/certora/munged/mocks/InitializableMock.sol new file mode 100644 index 000000000..630e8bbfa --- /dev/null +++ b/certora/munged/mocks/InitializableMock.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +/** + * @title InitializableMock + * @dev This contract is a mock to test initializable functionality + */ +contract InitializableMock is Initializable { + bool public initializerRan; + bool public onlyInitializingRan; + uint256 public x; + + function initialize() public initializer { + initializerRan = true; + } + + function initializeOnlyInitializing() public onlyInitializing { + onlyInitializingRan = true; + } + + function initializerNested() public initializer { + initialize(); + } + + function onlyInitializingNested() public initializer { + initializeOnlyInitializing(); + } + + function initializeWithX(uint256 _x) public payable initializer { + x = _x; + } + + function nonInitializable(uint256 _x) public payable { + x = _x; + } + + function fail() public pure { + require(false, "InitializableMock forced failure"); + } +} + +contract ConstructorInitializableMock is Initializable { + bool public initializerRan; + bool public onlyInitializingRan; + + constructor() initializer { + initialize(); + initializeOnlyInitializing(); + } + + function initialize() public initializer { + initializerRan = true; + } + + function initializeOnlyInitializing() public onlyInitializing { + onlyInitializingRan = true; + } +} diff --git a/certora/munged/mocks/MathMock.sol b/certora/munged/mocks/MathMock.sol new file mode 100644 index 000000000..c651b6bb1 --- /dev/null +++ b/certora/munged/mocks/MathMock.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/Math.sol"; + +contract MathMock { + function max(uint256 a, uint256 b) public pure returns (uint256) { + return Math.max(a, b); + } + + function min(uint256 a, uint256 b) public pure returns (uint256) { + return Math.min(a, b); + } + + function average(uint256 a, uint256 b) public pure returns (uint256) { + return Math.average(a, b); + } + + function ceilDiv(uint256 a, uint256 b) public pure returns (uint256) { + return Math.ceilDiv(a, b); + } +} diff --git a/certora/munged/mocks/MerkleProofWrapper.sol b/certora/munged/mocks/MerkleProofWrapper.sol new file mode 100644 index 000000000..1e188df36 --- /dev/null +++ b/certora/munged/mocks/MerkleProofWrapper.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/MerkleProof.sol"; + +contract MerkleProofWrapper { + function verify( + bytes32[] memory proof, + bytes32 root, + bytes32 leaf + ) public pure returns (bool) { + return MerkleProof.verify(proof, root, leaf); + } + + function processProof(bytes32[] memory proof, bytes32 leaf) public pure returns (bytes32) { + return MerkleProof.processProof(proof, leaf); + } +} diff --git a/certora/munged/mocks/MulticallTest.sol b/certora/munged/mocks/MulticallTest.sol new file mode 100644 index 000000000..f1a3a9cfe --- /dev/null +++ b/certora/munged/mocks/MulticallTest.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./MulticallTokenMock.sol"; + +contract MulticallTest { + function testReturnValues( + MulticallTokenMock multicallToken, + address[] calldata recipients, + uint256[] calldata amounts + ) external { + bytes[] memory calls = new bytes[](recipients.length); + for (uint256 i = 0; i < recipients.length; i++) { + calls[i] = abi.encodeWithSignature("transfer(address,uint256)", recipients[i], amounts[i]); + } + + bytes[] memory results = multicallToken.multicall(calls); + for (uint256 i = 0; i < results.length; i++) { + require(abi.decode(results[i], (bool))); + } + } +} diff --git a/certora/munged/mocks/MulticallTokenMock.sol b/certora/munged/mocks/MulticallTokenMock.sol new file mode 100644 index 000000000..de379681b --- /dev/null +++ b/certora/munged/mocks/MulticallTokenMock.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Multicall.sol"; +import "./ERC20Mock.sol"; + +contract MulticallTokenMock is ERC20Mock, Multicall { + constructor(uint256 initialBalance) ERC20Mock("MulticallToken", "BCT", msg.sender, initialBalance) {} +} diff --git a/certora/munged/mocks/MultipleInheritanceInitializableMocks.sol b/certora/munged/mocks/MultipleInheritanceInitializableMocks.sol new file mode 100644 index 000000000..f8b6e465e --- /dev/null +++ b/certora/munged/mocks/MultipleInheritanceInitializableMocks.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +// Sample contracts showing upgradeability with multiple inheritance. +// Child contract inherits from Father and Mother contracts, and Father extends from Gramps. +// +// Human +// / \ +// | Gramps +// | | +// Mother Father +// | | +// -- Child -- + +/** + * Sample base intializable contract that is a human + */ +contract SampleHuman is Initializable { + bool public isHuman; + + function initialize() public initializer { + __SampleHuman_init(); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleHuman_init() internal onlyInitializing { + __SampleHuman_init_unchained(); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleHuman_init_unchained() internal onlyInitializing { + isHuman = true; + } +} + +/** + * Sample base intializable contract that defines a field mother + */ +contract SampleMother is Initializable, SampleHuman { + uint256 public mother; + + function initialize(uint256 value) public virtual initializer { + __SampleMother_init(value); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleMother_init(uint256 value) internal onlyInitializing { + __SampleHuman_init(); + __SampleMother_init_unchained(value); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleMother_init_unchained(uint256 value) internal onlyInitializing { + mother = value; + } +} + +/** + * Sample base intializable contract that defines a field gramps + */ +contract SampleGramps is Initializable, SampleHuman { + string public gramps; + + function initialize(string memory value) public virtual initializer { + __SampleGramps_init(value); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleGramps_init(string memory value) internal onlyInitializing { + __SampleHuman_init(); + __SampleGramps_init_unchained(value); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleGramps_init_unchained(string memory value) internal onlyInitializing { + gramps = value; + } +} + +/** + * Sample base intializable contract that defines a field father and extends from gramps + */ +contract SampleFather is Initializable, SampleGramps { + uint256 public father; + + function initialize(string memory _gramps, uint256 _father) public initializer { + __SampleFather_init(_gramps, _father); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleFather_init(string memory _gramps, uint256 _father) internal onlyInitializing { + __SampleGramps_init(_gramps); + __SampleFather_init_unchained(_father); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleFather_init_unchained(uint256 _father) internal onlyInitializing { + father = _father; + } +} + +/** + * Child extends from mother, father (gramps) + */ +contract SampleChild is Initializable, SampleMother, SampleFather { + uint256 public child; + + function initialize( + uint256 _mother, + string memory _gramps, + uint256 _father, + uint256 _child + ) public initializer { + __SampleChild_init(_mother, _gramps, _father, _child); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleChild_init( + uint256 _mother, + string memory _gramps, + uint256 _father, + uint256 _child + ) internal onlyInitializing { + __SampleMother_init(_mother); + __SampleFather_init(_gramps, _father); + __SampleChild_init_unchained(_child); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleChild_init_unchained(uint256 _child) internal onlyInitializing { + child = _child; + } +} diff --git a/certora/munged/mocks/OwnableMock.sol b/certora/munged/mocks/OwnableMock.sol new file mode 100644 index 000000000..d60f1c40d --- /dev/null +++ b/certora/munged/mocks/OwnableMock.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/Ownable.sol"; + +contract OwnableMock is Ownable {} diff --git a/certora/munged/mocks/PausableMock.sol b/certora/munged/mocks/PausableMock.sol new file mode 100644 index 000000000..98bcfd593 --- /dev/null +++ b/certora/munged/mocks/PausableMock.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../security/Pausable.sol"; + +contract PausableMock is Pausable { + bool public drasticMeasureTaken; + uint256 public count; + + constructor() { + drasticMeasureTaken = false; + count = 0; + } + + function normalProcess() external whenNotPaused { + count++; + } + + function drasticMeasure() external whenPaused { + drasticMeasureTaken = true; + } + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } +} diff --git a/certora/munged/mocks/PullPaymentMock.sol b/certora/munged/mocks/PullPaymentMock.sol new file mode 100644 index 000000000..8a708e30c --- /dev/null +++ b/certora/munged/mocks/PullPaymentMock.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../security/PullPayment.sol"; + +// mock class using PullPayment +contract PullPaymentMock is PullPayment { + constructor() payable {} + + // test helper function to call asyncTransfer + function callTransfer(address dest, uint256 amount) public { + _asyncTransfer(dest, amount); + } +} diff --git a/certora/munged/mocks/ReentrancyAttack.sol b/certora/munged/mocks/ReentrancyAttack.sol new file mode 100644 index 000000000..4de181205 --- /dev/null +++ b/certora/munged/mocks/ReentrancyAttack.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +contract ReentrancyAttack is Context { + function callSender(bytes4 data) public { + (bool success, ) = _msgSender().call(abi.encodeWithSelector(data)); + require(success, "ReentrancyAttack: failed call"); + } +} diff --git a/certora/munged/mocks/ReentrancyMock.sol b/certora/munged/mocks/ReentrancyMock.sol new file mode 100644 index 000000000..43425dd6e --- /dev/null +++ b/certora/munged/mocks/ReentrancyMock.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../security/ReentrancyGuard.sol"; +import "./ReentrancyAttack.sol"; + +contract ReentrancyMock is ReentrancyGuard { + uint256 public counter; + + constructor() { + counter = 0; + } + + function callback() external nonReentrant { + _count(); + } + + function countLocalRecursive(uint256 n) public nonReentrant { + if (n > 0) { + _count(); + countLocalRecursive(n - 1); + } + } + + function countThisRecursive(uint256 n) public nonReentrant { + if (n > 0) { + _count(); + (bool success, ) = address(this).call(abi.encodeWithSignature("countThisRecursive(uint256)", n - 1)); + require(success, "ReentrancyMock: failed call"); + } + } + + function countAndCall(ReentrancyAttack attacker) public nonReentrant { + _count(); + bytes4 func = bytes4(keccak256("callback()")); + attacker.callSender(func); + } + + function _count() private { + counter += 1; + } +} diff --git a/certora/munged/mocks/RegressionImplementation.sol b/certora/munged/mocks/RegressionImplementation.sol new file mode 100644 index 000000000..be6b501c1 --- /dev/null +++ b/certora/munged/mocks/RegressionImplementation.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +contract Implementation1 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } +} + +contract Implementation2 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue() public view returns (uint256) { + return _value; + } +} + +contract Implementation3 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue(uint256 _number) public view returns (uint256) { + return _value + _number; + } +} + +contract Implementation4 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue() public view returns (uint256) { + return _value; + } + + fallback() external { + _value = 1; + } +} diff --git a/certora/munged/mocks/SafeCastMock.sol b/certora/munged/mocks/SafeCastMock.sol new file mode 100644 index 000000000..d1f1aaaba --- /dev/null +++ b/certora/munged/mocks/SafeCastMock.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SafeCast.sol"; + +contract SafeCastMock { + using SafeCast for uint256; + using SafeCast for int256; + + function toUint256(int256 a) public pure returns (uint256) { + return a.toUint256(); + } + + function toUint224(uint256 a) public pure returns (uint224) { + return a.toUint224(); + } + + function toUint128(uint256 a) public pure returns (uint128) { + return a.toUint128(); + } + + function toUint96(uint256 a) public pure returns (uint96) { + return a.toUint96(); + } + + function toUint64(uint256 a) public pure returns (uint64) { + return a.toUint64(); + } + + function toUint32(uint256 a) public pure returns (uint32) { + return a.toUint32(); + } + + function toUint16(uint256 a) public pure returns (uint16) { + return a.toUint16(); + } + + function toUint8(uint256 a) public pure returns (uint8) { + return a.toUint8(); + } + + function toInt256(uint256 a) public pure returns (int256) { + return a.toInt256(); + } + + function toInt128(int256 a) public pure returns (int128) { + return a.toInt128(); + } + + function toInt64(int256 a) public pure returns (int64) { + return a.toInt64(); + } + + function toInt32(int256 a) public pure returns (int32) { + return a.toInt32(); + } + + function toInt16(int256 a) public pure returns (int16) { + return a.toInt16(); + } + + function toInt8(int256 a) public pure returns (int8) { + return a.toInt8(); + } +} diff --git a/certora/munged/mocks/SafeERC20Helper.sol b/certora/munged/mocks/SafeERC20Helper.sol new file mode 100644 index 000000000..f3bcc3972 --- /dev/null +++ b/certora/munged/mocks/SafeERC20Helper.sol @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; +import "../token/ERC20/IERC20.sol"; +import "../token/ERC20/utils/SafeERC20.sol"; + +contract ERC20ReturnFalseMock is Context { + uint256 private _allowance; + + // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, + // we write to a dummy state variable. + uint256 private _dummy; + + function transfer(address, uint256) public returns (bool) { + _dummy = 0; + return false; + } + + function transferFrom( + address, + address, + uint256 + ) public returns (bool) { + _dummy = 0; + return false; + } + + function approve(address, uint256) public returns (bool) { + _dummy = 0; + return false; + } + + function allowance(address, address) public view returns (uint256) { + require(_dummy == 0); // Dummy read from a state variable so that the function is view + return 0; + } +} + +contract ERC20ReturnTrueMock is Context { + mapping(address => uint256) private _allowances; + + // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, + // we write to a dummy state variable. + uint256 private _dummy; + + function transfer(address, uint256) public returns (bool) { + _dummy = 0; + return true; + } + + function transferFrom( + address, + address, + uint256 + ) public returns (bool) { + _dummy = 0; + return true; + } + + function approve(address, uint256) public returns (bool) { + _dummy = 0; + return true; + } + + function setAllowance(uint256 allowance_) public { + _allowances[_msgSender()] = allowance_; + } + + function allowance(address owner, address) public view returns (uint256) { + return _allowances[owner]; + } +} + +contract ERC20NoReturnMock is Context { + mapping(address => uint256) private _allowances; + + // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, + // we write to a dummy state variable. + uint256 private _dummy; + + function transfer(address, uint256) public { + _dummy = 0; + } + + function transferFrom( + address, + address, + uint256 + ) public { + _dummy = 0; + } + + function approve(address, uint256) public { + _dummy = 0; + } + + function setAllowance(uint256 allowance_) public { + _allowances[_msgSender()] = allowance_; + } + + function allowance(address owner, address) public view returns (uint256) { + return _allowances[owner]; + } +} + +contract SafeERC20Wrapper is Context { + using SafeERC20 for IERC20; + + IERC20 private _token; + + constructor(IERC20 token) { + _token = token; + } + + function transfer() public { + _token.safeTransfer(address(0), 0); + } + + function transferFrom() public { + _token.safeTransferFrom(address(0), address(0), 0); + } + + function approve(uint256 amount) public { + _token.safeApprove(address(0), amount); + } + + function increaseAllowance(uint256 amount) public { + _token.safeIncreaseAllowance(address(0), amount); + } + + function decreaseAllowance(uint256 amount) public { + _token.safeDecreaseAllowance(address(0), amount); + } + + function setAllowance(uint256 allowance_) public { + ERC20ReturnTrueMock(address(_token)).setAllowance(allowance_); + } + + function allowance() public view returns (uint256) { + return _token.allowance(address(0), address(0)); + } +} diff --git a/certora/munged/mocks/SafeMathMock.sol b/certora/munged/mocks/SafeMathMock.sol new file mode 100644 index 000000000..3d1f4727e --- /dev/null +++ b/certora/munged/mocks/SafeMathMock.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SafeMath.sol"; + +contract SafeMathMock { + function tryAdd(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryAdd(a, b); + } + + function trySub(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.trySub(a, b); + } + + function tryMul(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryMul(a, b); + } + + function tryDiv(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryDiv(a, b); + } + + function tryMod(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryMod(a, b); + } + + // using the do* naming convention to avoid warnings due to clashing opcode names + + function doAdd(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.add(a, b); + } + + function doSub(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.sub(a, b); + } + + function doMul(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.mul(a, b); + } + + function doDiv(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.div(a, b); + } + + function doMod(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.mod(a, b); + } + + function subWithMessage( + uint256 a, + uint256 b, + string memory errorMessage + ) public pure returns (uint256) { + return SafeMath.sub(a, b, errorMessage); + } + + function divWithMessage( + uint256 a, + uint256 b, + string memory errorMessage + ) public pure returns (uint256) { + return SafeMath.div(a, b, errorMessage); + } + + function modWithMessage( + uint256 a, + uint256 b, + string memory errorMessage + ) public pure returns (uint256) { + return SafeMath.mod(a, b, errorMessage); + } + + function addMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.add(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function subMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.sub(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function mulMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.mul(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function divMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.div(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function modMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.mod(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } +} diff --git a/certora/munged/mocks/SignatureCheckerMock.sol b/certora/munged/mocks/SignatureCheckerMock.sol new file mode 100644 index 000000000..3b399c1ae --- /dev/null +++ b/certora/munged/mocks/SignatureCheckerMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/SignatureChecker.sol"; + +contract SignatureCheckerMock { + using SignatureChecker for address; + + function isValidSignatureNow( + address signer, + bytes32 hash, + bytes memory signature + ) public view returns (bool) { + return signer.isValidSignatureNow(hash, signature); + } +} diff --git a/certora/munged/mocks/SignedMathMock.sol b/certora/munged/mocks/SignedMathMock.sol new file mode 100644 index 000000000..5a0b27096 --- /dev/null +++ b/certora/munged/mocks/SignedMathMock.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SignedMath.sol"; + +contract SignedMathMock { + function max(int256 a, int256 b) public pure returns (int256) { + return SignedMath.max(a, b); + } + + function min(int256 a, int256 b) public pure returns (int256) { + return SignedMath.min(a, b); + } + + function average(int256 a, int256 b) public pure returns (int256) { + return SignedMath.average(a, b); + } + + function abs(int256 n) public pure returns (uint256) { + return SignedMath.abs(n); + } +} diff --git a/certora/munged/mocks/SignedSafeMathMock.sol b/certora/munged/mocks/SignedSafeMathMock.sol new file mode 100644 index 000000000..8d1021798 --- /dev/null +++ b/certora/munged/mocks/SignedSafeMathMock.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SignedSafeMath.sol"; + +contract SignedSafeMathMock { + function mul(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.mul(a, b); + } + + function div(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.div(a, b); + } + + function sub(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.sub(a, b); + } + + function add(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.add(a, b); + } +} diff --git a/certora/munged/mocks/SingleInheritanceInitializableMocks.sol b/certora/munged/mocks/SingleInheritanceInitializableMocks.sol new file mode 100644 index 000000000..6c82dd20c --- /dev/null +++ b/certora/munged/mocks/SingleInheritanceInitializableMocks.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +/** + * @title MigratableMockV1 + * @dev This contract is a mock to test initializable functionality through migrations + */ +contract MigratableMockV1 is Initializable { + uint256 public x; + + function initialize(uint256 value) public payable initializer { + x = value; + } +} + +/** + * @title MigratableMockV2 + * @dev This contract is a mock to test migratable functionality with params + */ +contract MigratableMockV2 is MigratableMockV1 { + bool internal _migratedV2; + uint256 public y; + + function migrate(uint256 value, uint256 anotherValue) public payable { + require(!_migratedV2); + x = value; + y = anotherValue; + _migratedV2 = true; + } +} + +/** + * @title MigratableMockV3 + * @dev This contract is a mock to test migratable functionality without params + */ +contract MigratableMockV3 is MigratableMockV2 { + bool internal _migratedV3; + + function migrate() public payable { + require(!_migratedV3); + uint256 oldX = x; + x = y; + y = oldX; + _migratedV3 = true; + } +} diff --git a/certora/munged/mocks/StorageSlotMock.sol b/certora/munged/mocks/StorageSlotMock.sol new file mode 100644 index 000000000..5d099fca8 --- /dev/null +++ b/certora/munged/mocks/StorageSlotMock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/StorageSlot.sol"; + +contract StorageSlotMock { + using StorageSlot for bytes32; + + function setBoolean(bytes32 slot, bool value) public { + slot.getBooleanSlot().value = value; + } + + function setAddress(bytes32 slot, address value) public { + slot.getAddressSlot().value = value; + } + + function setBytes32(bytes32 slot, bytes32 value) public { + slot.getBytes32Slot().value = value; + } + + function setUint256(bytes32 slot, uint256 value) public { + slot.getUint256Slot().value = value; + } + + function getBoolean(bytes32 slot) public view returns (bool) { + return slot.getBooleanSlot().value; + } + + function getAddress(bytes32 slot) public view returns (address) { + return slot.getAddressSlot().value; + } + + function getBytes32(bytes32 slot) public view returns (bytes32) { + return slot.getBytes32Slot().value; + } + + function getUint256(bytes32 slot) public view returns (uint256) { + return slot.getUint256Slot().value; + } +} diff --git a/certora/munged/mocks/StringsMock.sol b/certora/munged/mocks/StringsMock.sol new file mode 100644 index 000000000..f257734e7 --- /dev/null +++ b/certora/munged/mocks/StringsMock.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Strings.sol"; + +contract StringsMock { + function fromUint256(uint256 value) public pure returns (string memory) { + return Strings.toString(value); + } + + function fromUint256Hex(uint256 value) public pure returns (string memory) { + return Strings.toHexString(value); + } + + function fromUint256HexFixed(uint256 value, uint256 length) public pure returns (string memory) { + return Strings.toHexString(value, length); + } +} diff --git a/certora/munged/mocks/TimersBlockNumberImpl.sol b/certora/munged/mocks/TimersBlockNumberImpl.sol new file mode 100644 index 000000000..84633e6f8 --- /dev/null +++ b/certora/munged/mocks/TimersBlockNumberImpl.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Timers.sol"; + +contract TimersBlockNumberImpl { + using Timers for Timers.BlockNumber; + + Timers.BlockNumber private _timer; + + function getDeadline() public view returns (uint64) { + return _timer.getDeadline(); + } + + function setDeadline(uint64 timestamp) public { + _timer.setDeadline(timestamp); + } + + function reset() public { + _timer.reset(); + } + + function isUnset() public view returns (bool) { + return _timer.isUnset(); + } + + function isStarted() public view returns (bool) { + return _timer.isStarted(); + } + + function isPending() public view returns (bool) { + return _timer.isPending(); + } + + function isExpired() public view returns (bool) { + return _timer.isExpired(); + } +} diff --git a/certora/munged/mocks/TimersTimestampImpl.sol b/certora/munged/mocks/TimersTimestampImpl.sol new file mode 100644 index 000000000..07f9a1b3f --- /dev/null +++ b/certora/munged/mocks/TimersTimestampImpl.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Timers.sol"; + +contract TimersTimestampImpl { + using Timers for Timers.Timestamp; + + Timers.Timestamp private _timer; + + function getDeadline() public view returns (uint64) { + return _timer.getDeadline(); + } + + function setDeadline(uint64 timestamp) public { + _timer.setDeadline(timestamp); + } + + function reset() public { + _timer.reset(); + } + + function isUnset() public view returns (bool) { + return _timer.isUnset(); + } + + function isStarted() public view returns (bool) { + return _timer.isStarted(); + } + + function isPending() public view returns (bool) { + return _timer.isPending(); + } + + function isExpired() public view returns (bool) { + return _timer.isExpired(); + } +} diff --git a/certora/munged/mocks/UUPS/UUPSLegacy.sol b/certora/munged/mocks/UUPS/UUPSLegacy.sol new file mode 100644 index 000000000..550a6221b --- /dev/null +++ b/certora/munged/mocks/UUPS/UUPSLegacy.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./UUPSUpgradeableMock.sol"; + +// This contract implements the pre-4.5 UUPS upgrade function with a rollback test. +// It's used to test that newer UUPS contracts are considered valid upgrades by older UUPS contracts. +contract UUPSUpgradeableLegacyMock is UUPSUpgradeableMock { + // Inlined from ERC1967Upgrade + bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + + // ERC1967Upgrade._setImplementation is private so we reproduce it here. + // An extra underscore prevents a name clash error. + function __setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + } + + function _upgradeToAndCallSecureLegacyV1( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + address oldImplementation = _getImplementation(); + + // Initial upgrade and setup call + __setImplementation(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + + // Perform rollback test if not already in progress + StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT); + if (!rollbackTesting.value) { + // Trigger rollback using upgradeTo from the new implementation + rollbackTesting.value = true; + Address.functionDelegateCall( + newImplementation, + abi.encodeWithSignature("upgradeTo(address)", oldImplementation) + ); + rollbackTesting.value = false; + // Check rollback was effective + require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades"); + // Finally reset to the new implementation and log the upgrade + _upgradeTo(newImplementation); + } + } + + // hooking into the old mechanism + function upgradeTo(address newImplementation) external virtual override { + _upgradeToAndCallSecureLegacyV1(newImplementation, bytes(""), false); + } + + function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual override { + _upgradeToAndCallSecureLegacyV1(newImplementation, data, false); + } +} diff --git a/certora/munged/mocks/UUPS/UUPSUpgradeableMock.sol b/certora/munged/mocks/UUPS/UUPSUpgradeableMock.sol new file mode 100644 index 000000000..367303ec0 --- /dev/null +++ b/certora/munged/mocks/UUPS/UUPSUpgradeableMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../CountersImpl.sol"; +import "../../proxy/utils/UUPSUpgradeable.sol"; + +contract UUPSUpgradeableMock is CountersImpl, UUPSUpgradeable { + // Not having any checks in this function is dangerous! Do not do this outside tests! + function _authorizeUpgrade(address) internal virtual override {} +} + +contract UUPSUpgradeableUnsafeMock is UUPSUpgradeableMock { + function upgradeTo(address newImplementation) external virtual override { + ERC1967Upgrade._upgradeToAndCall(newImplementation, bytes(""), false); + } + + function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual override { + ERC1967Upgrade._upgradeToAndCall(newImplementation, data, false); + } +} diff --git a/certora/munged/mocks/VotesMock.sol b/certora/munged/mocks/VotesMock.sol new file mode 100644 index 000000000..db06ee9a5 --- /dev/null +++ b/certora/munged/mocks/VotesMock.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/utils/Votes.sol"; + +contract VotesMock is Votes { + mapping(address => uint256) private _balances; + mapping(uint256 => address) private _owners; + + constructor(string memory name) EIP712(name, "1") {} + + function getTotalSupply() public view returns (uint256) { + return _getTotalSupply(); + } + + function delegate(address account, address newDelegation) public { + return _delegate(account, newDelegation); + } + + function _getVotingUnits(address account) internal virtual override returns (uint256) { + return _balances[account]; + } + + function mint(address account, uint256 voteId) external { + _balances[account] += 1; + _owners[voteId] = account; + _transferVotingUnits(address(0), account, 1); + } + + function burn(uint256 voteId) external { + address owner = _owners[voteId]; + _balances[owner] -= 1; + _transferVotingUnits(owner, address(0), 1); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/certora/munged/mocks/compound/CompTimelock.sol b/certora/munged/mocks/compound/CompTimelock.sol new file mode 100644 index 000000000..49ffa4b77 --- /dev/null +++ b/certora/munged/mocks/compound/CompTimelock.sol @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: BSD-3-Clause +// solhint-disable private-vars-leading-underscore +/** + * Copyright 2020 Compound Labs, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the + * following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +pragma solidity ^0.8.0; + +contract CompTimelock { + event NewAdmin(address indexed newAdmin); + event NewPendingAdmin(address indexed newPendingAdmin); + event NewDelay(uint256 indexed newDelay); + event CancelTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + event ExecuteTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + event QueueTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + + uint256 public constant GRACE_PERIOD = 14 days; + uint256 public constant MINIMUM_DELAY = 2 days; + uint256 public constant MAXIMUM_DELAY = 30 days; + + address public admin; + address public pendingAdmin; + uint256 public delay; + + mapping(bytes32 => bool) public queuedTransactions; + + constructor(address admin_, uint256 delay_) { + require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); + require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); + + admin = admin_; + delay = delay_; + } + + receive() external payable {} + + function setDelay(uint256 delay_) public { + require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); + require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); + require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); + delay = delay_; + + emit NewDelay(delay); + } + + function acceptAdmin() public { + require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); + admin = msg.sender; + pendingAdmin = address(0); + + emit NewAdmin(admin); + } + + function setPendingAdmin(address pendingAdmin_) public { + require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); + pendingAdmin = pendingAdmin_; + + emit NewPendingAdmin(pendingAdmin); + } + + function queueTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) public returns (bytes32) { + require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); + require( + eta >= getBlockTimestamp() + delay, + "Timelock::queueTransaction: Estimated execution block must satisfy delay." + ); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + queuedTransactions[txHash] = true; + + emit QueueTransaction(txHash, target, value, signature, data, eta); + return txHash; + } + + function cancelTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) public { + require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + queuedTransactions[txHash] = false; + + emit CancelTransaction(txHash, target, value, signature, data, eta); + } + + function executeTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) public payable returns (bytes memory) { + require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); + require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); + require(getBlockTimestamp() <= eta + GRACE_PERIOD, "Timelock::executeTransaction: Transaction is stale."); + + queuedTransactions[txHash] = false; + + bytes memory callData; + + if (bytes(signature).length == 0) { + callData = data; + } else { + callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); + } + + // solium-disable-next-line security/no-call-value + (bool success, bytes memory returnData) = target.call{value: value}(callData); + require(success, "Timelock::executeTransaction: Transaction execution reverted."); + + emit ExecuteTransaction(txHash, target, value, signature, data, eta); + + return returnData; + } + + function getBlockTimestamp() internal view returns (uint256) { + // solium-disable-next-line security/no-block-members + return block.timestamp; + } +} diff --git a/certora/munged/mocks/wizard/MyGovernor1.sol b/certora/munged/mocks/wizard/MyGovernor1.sol new file mode 100644 index 000000000..a80d8400c --- /dev/null +++ b/certora/munged/mocks/wizard/MyGovernor1.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../governance/Governor.sol"; +import "../../governance/extensions/GovernorCountingSimple.sol"; +import "../../governance/extensions/GovernorVotes.sol"; +import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../governance/extensions/GovernorTimelockControl.sol"; + +contract MyGovernor1 is + Governor, + GovernorTimelockControl, + GovernorVotes, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor(IVotes _token, TimelockController _timelock) + Governor("MyGovernor") + GovernorVotes(_token) + GovernorVotesQuorumFraction(4) + GovernorTimelockControl(_timelock) + {} + + function votingDelay() public pure override returns (uint256) { + return 1; // 1 block + } + + function votingPeriod() public pure override returns (uint256) { + return 45818; // 1 week + } + + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/munged/mocks/wizard/MyGovernor2.sol b/certora/munged/mocks/wizard/MyGovernor2.sol new file mode 100644 index 000000000..34c608c5c --- /dev/null +++ b/certora/munged/mocks/wizard/MyGovernor2.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../governance/Governor.sol"; +import "../../governance/extensions/GovernorProposalThreshold.sol"; +import "../../governance/extensions/GovernorCountingSimple.sol"; +import "../../governance/extensions/GovernorVotes.sol"; +import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../governance/extensions/GovernorTimelockControl.sol"; + +contract MyGovernor2 is + Governor, + GovernorTimelockControl, + GovernorProposalThreshold, + GovernorVotes, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor(IVotes _token, TimelockController _timelock) + Governor("MyGovernor") + GovernorVotes(_token) + GovernorVotesQuorumFraction(4) + GovernorTimelockControl(_timelock) + {} + + function votingDelay() public pure override returns (uint256) { + return 1; // 1 block + } + + function votingPeriod() public pure override returns (uint256) { + return 45818; // 1 week + } + + function proposalThreshold() public pure override returns (uint256) { + return 1000e18; + } + + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, GovernorProposalThreshold, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/munged/mocks/wizard/MyGovernor3.sol b/certora/munged/mocks/wizard/MyGovernor3.sol new file mode 100644 index 000000000..70e4e87f0 --- /dev/null +++ b/certora/munged/mocks/wizard/MyGovernor3.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../governance/Governor.sol"; +import "../../governance/compatibility/GovernorCompatibilityBravo.sol"; +import "../../governance/extensions/GovernorVotes.sol"; +import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../governance/extensions/GovernorTimelockControl.sol"; + +contract MyGovernor is + Governor, + GovernorTimelockControl, + GovernorCompatibilityBravo, + GovernorVotes, + GovernorVotesQuorumFraction +{ + constructor(IVotes _token, TimelockController _timelock) + Governor("MyGovernor") + GovernorVotes(_token) + GovernorVotesQuorumFraction(4) + GovernorTimelockControl(_timelock) + {} + + function votingDelay() public pure override returns (uint256) { + return 1; // 1 block + } + + function votingPeriod() public pure override returns (uint256) { + return 45818; // 1 week + } + + function proposalThreshold() public pure override returns (uint256) { + return 1000e18; + } + + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function state(uint256 proposalId) + public + view + override(Governor, IGovernor, GovernorTimelockControl) + returns (ProposalState) + { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, IERC165, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/munged/package.json b/certora/munged/package.json new file mode 100644 index 000000000..6f4fcf35a --- /dev/null +++ b/certora/munged/package.json @@ -0,0 +1,32 @@ +{ + "name": "@openzeppelin/contracts", + "description": "Secure Smart Contract library for Solidity", + "version": "4.5.0", + "files": [ + "**/*.sol", + "/build/contracts/*.json", + "!/mocks/**/*" + ], + "scripts": { + "prepare": "bash ../scripts/prepare-contracts-package.sh", + "prepare-docs": "cd ..; npm run prepare-docs" + }, + "repository": { + "type": "git", + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts.git" + }, + "keywords": [ + "solidity", + "ethereum", + "smart", + "contracts", + "security", + "zeppelin" + ], + "author": "OpenZeppelin Community ", + "license": "MIT", + "bugs": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts/issues" + }, + "homepage": "https://openzeppelin.com/contracts/" +} diff --git a/certora/munged/proxy/Clones.sol b/certora/munged/proxy/Clones.sol new file mode 100644 index 000000000..31ece8f81 --- /dev/null +++ b/certora/munged/proxy/Clones.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (proxy/Clones.sol) + +pragma solidity ^0.8.0; + +/** + * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for + * deploying minimal proxy contracts, also known as "clones". + * + * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies + * > a minimal bytecode implementation that delegates all calls to a known, fixed address. + * + * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` + * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the + * deterministic method. + * + * _Available since v3.4._ + */ +library Clones { + /** + * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. + * + * This function uses the create opcode, which should never revert. + */ + function clone(address implementation) internal returns (address instance) { + assembly { + let ptr := mload(0x40) + mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) + mstore(add(ptr, 0x14), shl(0x60, implementation)) + mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) + instance := create(0, ptr, 0x37) + } + require(instance != address(0), "ERC1167: create failed"); + } + + /** + * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. + * + * This function uses the create2 opcode and a `salt` to deterministically deploy + * the clone. Using the same `implementation` and `salt` multiple time will revert, since + * the clones cannot be deployed twice at the same address. + */ + function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { + assembly { + let ptr := mload(0x40) + mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) + mstore(add(ptr, 0x14), shl(0x60, implementation)) + mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) + instance := create2(0, ptr, 0x37, salt) + } + require(instance != address(0), "ERC1167: create2 failed"); + } + + /** + * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. + */ + function predictDeterministicAddress( + address implementation, + bytes32 salt, + address deployer + ) internal pure returns (address predicted) { + assembly { + let ptr := mload(0x40) + mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) + mstore(add(ptr, 0x14), shl(0x60, implementation)) + mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000) + mstore(add(ptr, 0x38), shl(0x60, deployer)) + mstore(add(ptr, 0x4c), salt) + mstore(add(ptr, 0x6c), keccak256(ptr, 0x37)) + predicted := keccak256(add(ptr, 0x37), 0x55) + } + } + + /** + * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. + */ + function predictDeterministicAddress(address implementation, bytes32 salt) + internal + view + returns (address predicted) + { + return predictDeterministicAddress(implementation, salt, address(this)); + } +} diff --git a/certora/munged/proxy/ERC1967/ERC1967Proxy.sol b/certora/munged/proxy/ERC1967/ERC1967Proxy.sol new file mode 100644 index 000000000..64e9d9f6f --- /dev/null +++ b/certora/munged/proxy/ERC1967/ERC1967Proxy.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol) + +pragma solidity ^0.8.0; + +import "../Proxy.sol"; +import "./ERC1967Upgrade.sol"; + +/** + * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an + * implementation address that can be changed. This address is stored in storage in the location specified by + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the + * implementation behind the proxy. + */ +contract ERC1967Proxy is Proxy, ERC1967Upgrade { + /** + * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. + * + * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded + * function call, and allows initializating the storage of the proxy like a Solidity constructor. + */ + constructor(address _logic, bytes memory _data) payable { + assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); + _upgradeToAndCall(_logic, _data, false); + } + + /** + * @dev Returns the current implementation address. + */ + function _implementation() internal view virtual override returns (address impl) { + return ERC1967Upgrade._getImplementation(); + } +} diff --git a/certora/munged/proxy/ERC1967/ERC1967Upgrade.sol b/certora/munged/proxy/ERC1967/ERC1967Upgrade.sol new file mode 100644 index 000000000..77fbdd165 --- /dev/null +++ b/certora/munged/proxy/ERC1967/ERC1967Upgrade.sol @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol) + +pragma solidity ^0.8.2; + +import "../beacon/IBeacon.sol"; +import "../../interfaces/draft-IERC1822.sol"; +import "../../utils/Address.sol"; +import "../../utils/StorageSlot.sol"; + +/** + * @dev This abstract contract provides getters and event emitting update functions for + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. + * + * _Available since v4.1._ + * + * @custom:oz-upgrades-unsafe-allow delegatecall + */ +abstract contract ERC1967Upgrade { + // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 + bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @dev Emitted when the implementation is upgraded. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Returns the current implementation address. + */ + function _getImplementation() internal view returns (address) { + return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 implementation slot. + */ + function _setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + } + + /** + * @dev Perform implementation upgrade + * + * Emits an {Upgraded} event. + */ + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Perform implementation upgrade with additional setup call. + * + * Emits an {Upgraded} event. + */ + function _upgradeToAndCall( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + _upgradeTo(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + } + + /** + * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. + * + * Emits an {Upgraded} event. + */ + function _upgradeToAndCallUUPS( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + // Upgrades from old implementations will perform a rollback test. This test requires the new + // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing + // this special case will break upgrade paths from old UUPS implementation to new ones. + if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) { + _setImplementation(newImplementation); + } else { + try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { + require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID"); + } catch { + revert("ERC1967Upgrade: new implementation is not UUPS"); + } + _upgradeToAndCall(newImplementation, data, forceCall); + } + } + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + /** + * @dev Emitted when the admin account has changed. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Returns the current admin. + */ + function _getAdmin() internal view returns (address) { + return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 admin slot. + */ + function _setAdmin(address newAdmin) private { + require(newAdmin != address(0), "ERC1967: new admin is the zero address"); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } + + /** + * @dev Changes the admin of the proxy. + * + * Emits an {AdminChanged} event. + */ + function _changeAdmin(address newAdmin) internal { + emit AdminChanged(_getAdmin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. + * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. + */ + bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; + + /** + * @dev Emitted when the beacon is upgraded. + */ + event BeaconUpgraded(address indexed beacon); + + /** + * @dev Returns the current beacon. + */ + function _getBeacon() internal view returns (address) { + return StorageSlot.getAddressSlot(_BEACON_SLOT).value; + } + + /** + * @dev Stores a new beacon in the EIP1967 beacon slot. + */ + function _setBeacon(address newBeacon) private { + require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); + require( + Address.isContract(IBeacon(newBeacon).implementation()), + "ERC1967: beacon implementation is not a contract" + ); + StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; + } + + /** + * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does + * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). + * + * Emits a {BeaconUpgraded} event. + */ + function _upgradeBeaconToAndCall( + address newBeacon, + bytes memory data, + bool forceCall + ) internal { + _setBeacon(newBeacon); + emit BeaconUpgraded(newBeacon); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); + } + } +} diff --git a/certora/munged/proxy/Proxy.sol b/certora/munged/proxy/Proxy.sol new file mode 100644 index 000000000..9e8aa9124 --- /dev/null +++ b/certora/munged/proxy/Proxy.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (proxy/Proxy.sol) + +pragma solidity ^0.8.0; + +/** + * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM + * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to + * be specified by overriding the virtual {_implementation} function. + * + * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a + * different contract through the {_delegate} function. + * + * The success and return data of the delegated call will be returned back to the caller of the proxy. + */ +abstract contract Proxy { + /** + * @dev Delegates the current call to `implementation`. + * + * This function does not return to its internal call site, it will return directly to the external caller. + */ + function _delegate(address implementation) internal virtual { + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize()) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + + /** + * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function + * and {_fallback} should delegate. + */ + function _implementation() internal view virtual returns (address); + + /** + * @dev Delegates the current call to the address returned by `_implementation()`. + * + * This function does not return to its internall call site, it will return directly to the external caller. + */ + function _fallback() internal virtual { + _beforeFallback(); + _delegate(_implementation()); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other + * function in the contract matches the call data. + */ + fallback() external payable virtual { + _fallback(); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data + * is empty. + */ + receive() external payable virtual { + _fallback(); + } + + /** + * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` + * call, or as part of the Solidity `fallback` or `receive` functions. + * + * If overriden should call `super._beforeFallback()`. + */ + function _beforeFallback() internal virtual {} +} diff --git a/certora/munged/proxy/README.adoc b/certora/munged/proxy/README.adoc new file mode 100644 index 000000000..9f37b267f --- /dev/null +++ b/certora/munged/proxy/README.adoc @@ -0,0 +1,85 @@ += Proxies + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/proxy + +This is a low-level set of contracts implementing different proxy patterns with and without upgradeability. For an in-depth overview of this pattern check out the xref:upgrades-plugins::proxies.adoc[Proxy Upgrade Pattern] page. + +Most of the proxies below are built on an abstract base contract. + +- {Proxy}: Abstract contract implementing the core delegation functionality. + +In order to avoid clashes with the storage variables of the implementation contract behind a proxy, we use https://eips.ethereum.org/EIPS/eip-1967[EIP1967] storage slots. + +- {ERC1967Upgrade}: Internal functions to get and set the storage slots defined in EIP1967. +- {ERC1967Proxy}: A proxy using EIP1967 storage slots. Not upgradeable by default. + +There are two alternative ways to add upgradeability to an ERC1967 proxy. Their differences are explained below in <>. + +- {TransparentUpgradeableProxy}: A proxy with a built in admin and upgrade interface. +- {UUPSUpgradeable}: An upgradeability mechanism to be included in the implementation contract. + +CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Truffle and Hardhat. + +A different family of proxies are beacon proxies. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. + +- {BeaconProxy}: A proxy that retreives its implementation from a beacon contract. +- {UpgradeableBeacon}: A beacon contract with a built in admin that can upgrade the {BeaconProxy} pointing to it. + +In this pattern, the proxy contract doesn't hold the implementation address in storage like an ERC1967 proxy, instead the address is stored in a separate beacon contract. The `upgrade` operations that are sent to the beacon instead of to the proxy contract, and all proxies that follow that beacon are automatically upgraded. + +Outside the realm of upgradeability, proxies can also be useful to make cheap contract clones, such as those created by an on-chain factory contract that creates many instances of the same contract. These instances are designed to be both cheap to deploy, and cheap to call. + +- {Clones}: A library that can deploy cheap minimal non-upgradeable proxies. + +[[transparent-vs-uups]] +== Transparent vs UUPS Proxies + +The original proxies included in OpenZeppelin followed the https://blog.openzeppelin.com/the-transparent-proxy-pattern/[Transparent Proxy Pattern]. While this pattern is still provided, our recommendation is now shifting towards UUPS proxies, which are both lightweight and versatile. The name UUPS comes from https://eips.ethereum.org/EIPS/eip-1822[EIP1822], which first documented the pattern. + +While both of these share the same interface for upgrades, in UUPS proxies the upgrade is handled by the implementation, and can eventually be removed. Transparent proxies, on the other hand, include the upgrade and admin logic in the proxy itself. This means {TransparentUpgradeableProxy} is more expensive to deploy than what is possible with UUPS proxies. + +UUPS proxies are implemented using an {ERC1967Proxy}. Note that this proxy is not by itself upgradeable. It is the role of the implementation to include, alongside the contract's logic, all the code necessary to update the implementation's address that is stored at a specific slot in the proxy's storage space. This is where the {UUPSUpgradeable} contract comes in. Inheriting from it (and overriding the {xref-UUPSUpgradeable-_authorizeUpgrade-address-}[`_authorizeUpgrade`] function with the relevant access control mechanism) will turn your contract into a UUPS compliant implementation. + +Note that since both proxies use the same storage slot for the implementation address, using a UUPS compliant implementation with a {TransparentUpgradeableProxy} might allow non-admins to perform upgrade operations. + +By default, the upgrade functionality included in {UUPSUpgradeable} contains a security mechanism that will prevent any upgrades to a non UUPS compliant implementation. This prevents upgrades to an implementation contract that wouldn't contain the necessary upgrade mechanism, as it would lock the upgradeability of the proxy forever. This security mechanism can be bypassed by either of: + +- Adding a flag mechanism in the implementation that will disable the upgrade function when triggered. +- Upgrading to an implementation that features an upgrade mechanism without the additional security check, and then upgrading again to another implementation without the upgrade mechanism. + +The current implementation of this security mechanism uses https://eips.ethereum.org/EIPS/eip-1822[EIP1822] to detect the storage slot used by the implementation. A previous implementation, now deprecated, relied on a rollback check. It is possible to upgrade from a contract using the old mechanism to a new one. The inverse is however not possible, as old implementations (before version 4.5) did not include the `ERC1822` interface. + +== Core + +{{Proxy}} + +== ERC1967 + +{{ERC1967Proxy}} + +{{ERC1967Upgrade}} + +== Transparent Proxy + +{{TransparentUpgradeableProxy}} + +{{ProxyAdmin}} + +== Beacon + +{{BeaconProxy}} + +{{IBeacon}} + +{{UpgradeableBeacon}} + +== Minimal Clones + +{{Clones}} + +== Utils + +{{Initializable}} + +{{UUPSUpgradeable}} diff --git a/certora/munged/proxy/beacon/BeaconProxy.sol b/certora/munged/proxy/beacon/BeaconProxy.sol new file mode 100644 index 000000000..32eaa8e67 --- /dev/null +++ b/certora/munged/proxy/beacon/BeaconProxy.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (proxy/beacon/BeaconProxy.sol) + +pragma solidity ^0.8.0; + +import "./IBeacon.sol"; +import "../Proxy.sol"; +import "../ERC1967/ERC1967Upgrade.sol"; + +/** + * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}. + * + * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't + * conflict with the storage layout of the implementation behind the proxy. + * + * _Available since v3.4._ + */ +contract BeaconProxy is Proxy, ERC1967Upgrade { + /** + * @dev Initializes the proxy with `beacon`. + * + * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This + * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity + * constructor. + * + * Requirements: + * + * - `beacon` must be a contract with the interface {IBeacon}. + */ + constructor(address beacon, bytes memory data) payable { + assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1)); + _upgradeBeaconToAndCall(beacon, data, false); + } + + /** + * @dev Returns the current beacon address. + */ + function _beacon() internal view virtual returns (address) { + return _getBeacon(); + } + + /** + * @dev Returns the current implementation address of the associated beacon. + */ + function _implementation() internal view virtual override returns (address) { + return IBeacon(_getBeacon()).implementation(); + } + + /** + * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}. + * + * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. + * + * Requirements: + * + * - `beacon` must be a contract. + * - The implementation returned by `beacon` must be a contract. + */ + function _setBeacon(address beacon, bytes memory data) internal virtual { + _upgradeBeaconToAndCall(beacon, data, false); + } +} diff --git a/certora/munged/proxy/beacon/IBeacon.sol b/certora/munged/proxy/beacon/IBeacon.sol new file mode 100644 index 000000000..fba3ee2ab --- /dev/null +++ b/certora/munged/proxy/beacon/IBeacon.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol) + +pragma solidity ^0.8.0; + +/** + * @dev This is the interface that {BeaconProxy} expects of its beacon. + */ +interface IBeacon { + /** + * @dev Must return an address that can be used as a delegate call target. + * + * {BeaconProxy} will check that this address is a contract. + */ + function implementation() external view returns (address); +} diff --git a/certora/munged/proxy/beacon/UpgradeableBeacon.sol b/certora/munged/proxy/beacon/UpgradeableBeacon.sol new file mode 100644 index 000000000..5d83ceb3b --- /dev/null +++ b/certora/munged/proxy/beacon/UpgradeableBeacon.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol) + +pragma solidity ^0.8.0; + +import "./IBeacon.sol"; +import "../../access/Ownable.sol"; +import "../../utils/Address.sol"; + +/** + * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their + * implementation contract, which is where they will delegate all function calls. + * + * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon. + */ +contract UpgradeableBeacon is IBeacon, Ownable { + address private _implementation; + + /** + * @dev Emitted when the implementation returned by the beacon is changed. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the + * beacon. + */ + constructor(address implementation_) { + _setImplementation(implementation_); + } + + /** + * @dev Returns the current implementation address. + */ + function implementation() public view virtual override returns (address) { + return _implementation; + } + + /** + * @dev Upgrades the beacon to a new implementation. + * + * Emits an {Upgraded} event. + * + * Requirements: + * + * - msg.sender must be the owner of the contract. + * - `newImplementation` must be a contract. + */ + function upgradeTo(address newImplementation) public virtual onlyOwner { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Sets the implementation contract address for this beacon + * + * Requirements: + * + * - `newImplementation` must be a contract. + */ + function _setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract"); + _implementation = newImplementation; + } +} diff --git a/certora/munged/proxy/transparent/ProxyAdmin.sol b/certora/munged/proxy/transparent/ProxyAdmin.sol new file mode 100644 index 000000000..839534298 --- /dev/null +++ b/certora/munged/proxy/transparent/ProxyAdmin.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.sol) + +pragma solidity ^0.8.0; + +import "./TransparentUpgradeableProxy.sol"; +import "../../access/Ownable.sol"; + +/** + * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an + * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}. + */ +contract ProxyAdmin is Ownable { + /** + * @dev Returns the current implementation of `proxy`. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) { + // We need to manually run the static call since the getter cannot be flagged as view + // bytes4(keccak256("implementation()")) == 0x5c60da1b + (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b"); + require(success); + return abi.decode(returndata, (address)); + } + + /** + * @dev Returns the current admin of `proxy`. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) { + // We need to manually run the static call since the getter cannot be flagged as view + // bytes4(keccak256("admin()")) == 0xf851a440 + (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440"); + require(success); + return abi.decode(returndata, (address)); + } + + /** + * @dev Changes the admin of `proxy` to `newAdmin`. + * + * Requirements: + * + * - This contract must be the current admin of `proxy`. + */ + function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner { + proxy.changeAdmin(newAdmin); + } + + /** + * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner { + proxy.upgradeTo(implementation); + } + + /** + * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See + * {TransparentUpgradeableProxy-upgradeToAndCall}. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function upgradeAndCall( + TransparentUpgradeableProxy proxy, + address implementation, + bytes memory data + ) public payable virtual onlyOwner { + proxy.upgradeToAndCall{value: msg.value}(implementation, data); + } +} diff --git a/certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol b/certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol new file mode 100644 index 000000000..10808d58d --- /dev/null +++ b/certora/munged/proxy/transparent/TransparentUpgradeableProxy.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol) + +pragma solidity ^0.8.0; + +import "../ERC1967/ERC1967Proxy.sol"; + +/** + * @dev This contract implements a proxy that is upgradeable by an admin. + * + * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector + * clashing], which can potentially be used in an attack, this contract uses the + * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two + * things that go hand in hand: + * + * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if + * that call matches one of the admin functions exposed by the proxy itself. + * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the + * implementation. If the admin tries to call a function on the implementation it will fail with an error that says + * "admin cannot fallback to proxy target". + * + * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing + * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due + * to sudden errors when trying to call a function from the proxy implementation. + * + * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, + * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy. + */ +contract TransparentUpgradeableProxy is ERC1967Proxy { + /** + * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and + * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}. + */ + constructor( + address _logic, + address admin_, + bytes memory _data + ) payable ERC1967Proxy(_logic, _data) { + assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); + _changeAdmin(admin_); + } + + /** + * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. + */ + modifier ifAdmin() { + if (msg.sender == _getAdmin()) { + _; + } else { + _fallback(); + } + } + + /** + * @dev Returns the current admin. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the + * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` + */ + function admin() external ifAdmin returns (address admin_) { + admin_ = _getAdmin(); + } + + /** + * @dev Returns the current implementation. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the + * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` + */ + function implementation() external ifAdmin returns (address implementation_) { + implementation_ = _implementation(); + } + + /** + * @dev Changes the admin of the proxy. + * + * Emits an {AdminChanged} event. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}. + */ + function changeAdmin(address newAdmin) external virtual ifAdmin { + _changeAdmin(newAdmin); + } + + /** + * @dev Upgrade the implementation of the proxy. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}. + */ + function upgradeTo(address newImplementation) external ifAdmin { + _upgradeToAndCall(newImplementation, bytes(""), false); + } + + /** + * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified + * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the + * proxied contract. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}. + */ + function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin { + _upgradeToAndCall(newImplementation, data, true); + } + + /** + * @dev Returns the current admin. + */ + function _admin() internal view virtual returns (address) { + return _getAdmin(); + } + + /** + * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}. + */ + function _beforeFallback() internal virtual override { + require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target"); + super._beforeFallback(); + } +} diff --git a/certora/munged/proxy/utils/Initializable.sol b/certora/munged/proxy/utils/Initializable.sol new file mode 100644 index 000000000..1fe4583a2 --- /dev/null +++ b/certora/munged/proxy/utils/Initializable.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol) + +pragma solidity ^0.8.0; + +import "../../utils/Address.sol"; + +/** + * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed + * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + * + * [CAUTION] + * ==== + * Avoid leaving a contract uninitialized. + * + * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation + * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the + * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed: + * + * [.hljs-theme-light.nopadding] + * ``` + * /// @custom:oz-upgrades-unsafe-allow constructor + * constructor() initializer {} + * ``` + * ==== + */ +abstract contract Initializable { + /** + * @dev Indicates that the contract has been initialized. + */ + bool private _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private _initializing; + + /** + * @dev Modifier to protect an initializer function from being invoked twice. + */ + modifier initializer() { + // If the contract is initializing we ignore whether _initialized is set in order to support multiple + // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the + // contract may have been reentered. + require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized"); + + bool isTopLevelCall = !_initializing; + if (isTopLevelCall) { + _initializing = true; + _initialized = true; + } + + _; + + if (isTopLevelCall) { + _initializing = false; + } + } + + /** + * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the + * {initializer} modifier, directly or indirectly. + */ + modifier onlyInitializing() { + require(_initializing, "Initializable: contract is not initializing"); + _; + } + + function _isConstructor() private view returns (bool) { + return !Address.isContract(address(this)); + } +} diff --git a/certora/munged/proxy/utils/UUPSUpgradeable.sol b/certora/munged/proxy/utils/UUPSUpgradeable.sol new file mode 100644 index 000000000..7ccc50317 --- /dev/null +++ b/certora/munged/proxy/utils/UUPSUpgradeable.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/UUPSUpgradeable.sol) + +pragma solidity ^0.8.0; + +import "../../interfaces/draft-IERC1822.sol"; +import "../ERC1967/ERC1967Upgrade.sol"; + +/** + * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an + * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy. + * + * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is + * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing + * `UUPSUpgradeable` with a custom implementation of upgrades. + * + * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism. + * + * _Available since v4.1._ + */ +abstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment + address private immutable __self = address(this); + + /** + * @dev Check that the execution is being performed through a delegatecall call and that the execution context is + * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case + * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a + * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to + * fail. + */ + modifier onlyProxy() { + require(address(this) != __self, "Function must be called through delegatecall"); + require(_getImplementation() == __self, "Function must be called through active proxy"); + _; + } + + /** + * @dev Check that the execution is not being performed through a delegate call. This allows a function to be + * callable on the implementing contract but not through proxies. + */ + modifier notDelegated() { + require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall"); + _; + } + + /** + * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the + * implementation. It is used to validate that the this implementation remains valid after an upgrade. + * + * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks + * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this + * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier. + */ + function proxiableUUID() external view virtual override notDelegated returns (bytes32) { + return _IMPLEMENTATION_SLOT; + } + + /** + * @dev Upgrade the implementation of the proxy to `newImplementation`. + * + * Calls {_authorizeUpgrade}. + * + * Emits an {Upgraded} event. + */ + function upgradeTo(address newImplementation) external virtual onlyProxy { + _authorizeUpgrade(newImplementation); + _upgradeToAndCallUUPS(newImplementation, new bytes(0), false); + } + + /** + * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call + * encoded in `data`. + * + * Calls {_authorizeUpgrade}. + * + * Emits an {Upgraded} event. + */ + function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy { + _authorizeUpgrade(newImplementation); + _upgradeToAndCallUUPS(newImplementation, data, true); + } + + /** + * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by + * {upgradeTo} and {upgradeToAndCall}. + * + * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}. + * + * ```solidity + * function _authorizeUpgrade(address) internal override onlyOwner {} + * ``` + */ + function _authorizeUpgrade(address newImplementation) internal virtual; +} diff --git a/certora/munged/security/Pausable.sol b/certora/munged/security/Pausable.sol new file mode 100644 index 000000000..0c09e6c8a --- /dev/null +++ b/certora/munged/security/Pausable.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol) + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +/** + * @dev Contract module which allows children to implement an emergency stop + * mechanism that can be triggered by an authorized account. + * + * This module is used through inheritance. It will make available the + * modifiers `whenNotPaused` and `whenPaused`, which can be applied to + * the functions of your contract. Note that they will not be pausable by + * simply including this module, only once the modifiers are put in place. + */ +abstract contract Pausable is Context { + /** + * @dev Emitted when the pause is triggered by `account`. + */ + event Paused(address account); + + /** + * @dev Emitted when the pause is lifted by `account`. + */ + event Unpaused(address account); + + bool private _paused; + + /** + * @dev Initializes the contract in unpaused state. + */ + constructor() { + _paused = false; + } + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + require(!paused(), "Pausable: paused"); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + require(paused(), "Pausable: not paused"); + _; + } + + /** + * @dev Triggers stopped state. + * + * Requirements: + * + * - The contract must not be paused. + */ + function _pause() internal virtual whenNotPaused { + _paused = true; + emit Paused(_msgSender()); + } + + /** + * @dev Returns to normal state. + * + * Requirements: + * + * - The contract must be paused. + */ + function _unpause() internal virtual whenPaused { + _paused = false; + emit Unpaused(_msgSender()); + } +} diff --git a/certora/munged/security/PullPayment.sol b/certora/munged/security/PullPayment.sol new file mode 100644 index 000000000..6a50f2cb2 --- /dev/null +++ b/certora/munged/security/PullPayment.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (security/PullPayment.sol) + +pragma solidity ^0.8.0; + +import "../utils/escrow/Escrow.sol"; + +/** + * @dev Simple implementation of a + * https://consensys.github.io/smart-contract-best-practices/recommendations/#favor-pull-over-push-for-external-calls[pull-payment] + * strategy, where the paying contract doesn't interact directly with the + * receiver account, which must withdraw its payments itself. + * + * Pull-payments are often considered the best practice when it comes to sending + * Ether, security-wise. It prevents recipients from blocking execution, and + * eliminates reentrancy concerns. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + * + * To use, derive from the `PullPayment` contract, and use {_asyncTransfer} + * instead of Solidity's `transfer` function. Payees can query their due + * payments with {payments}, and retrieve them with {withdrawPayments}. + */ +abstract contract PullPayment { + Escrow private immutable _escrow; + + constructor() { + _escrow = new Escrow(); + } + + /** + * @dev Withdraw accumulated payments, forwarding all gas to the recipient. + * + * Note that _any_ account can call this function, not just the `payee`. + * This means that contracts unaware of the `PullPayment` protocol can still + * receive funds this way, by having a separate account call + * {withdrawPayments}. + * + * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. + * Make sure you trust the recipient, or are either following the + * checks-effects-interactions pattern or using {ReentrancyGuard}. + * + * @param payee Whose payments will be withdrawn. + */ + function withdrawPayments(address payable payee) public virtual { + _escrow.withdraw(payee); + } + + /** + * @dev Returns the payments owed to an address. + * @param dest The creditor's address. + */ + function payments(address dest) public view returns (uint256) { + return _escrow.depositsOf(dest); + } + + /** + * @dev Called by the payer to store the sent amount as credit to be pulled. + * Funds sent in this way are stored in an intermediate {Escrow} contract, so + * there is no danger of them being spent before withdrawal. + * + * @param dest The destination address of the funds. + * @param amount The amount to transfer. + */ + function _asyncTransfer(address dest, uint256 amount) internal virtual { + _escrow.deposit{value: amount}(dest); + } +} diff --git a/certora/munged/security/README.adoc b/certora/munged/security/README.adoc new file mode 100644 index 000000000..66f398fec --- /dev/null +++ b/certora/munged/security/README.adoc @@ -0,0 +1,20 @@ += Security + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/security + +These contracts aim to cover common security practices. + +* {PullPayment}: A pattern that can be used to avoid reentrancy attacks. +* {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions. +* {Pausable}: A common emergency response mechanism that can pause functionality while a remediation is pending. + +TIP: For an overview on reentrancy and the possible mechanisms to prevent it, read our article https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + +== Contracts + +{{PullPayment}} + +{{ReentrancyGuard}} + +{{Pausable}} diff --git a/certora/munged/security/ReentrancyGuard.sol b/certora/munged/security/ReentrancyGuard.sol new file mode 100644 index 000000000..ec8ccc7c7 --- /dev/null +++ b/certora/munged/security/ReentrancyGuard.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Contract module that helps prevent reentrant calls to a function. + * + * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier + * available, which can be applied to functions to make sure there are no nested + * (reentrant) calls to them. + * + * Note that because there is a single `nonReentrant` guard, functions marked as + * `nonReentrant` may not call one another. This can be worked around by making + * those functions `private`, and then adding `external` `nonReentrant` entry + * points to them. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + */ +abstract contract ReentrancyGuard { + // Booleans are more expensive than uint256 or any type that takes up a full + // word because each write operation emits an extra SLOAD to first read the + // slot's contents, replace the bits taken up by the boolean, and then write + // back. This is the compiler's defense against contract upgrades and + // pointer aliasing, and it cannot be disabled. + + // The values being non-zero value makes deployment a bit more expensive, + // but in exchange the refund on every call to nonReentrant will be lower in + // amount. Since refunds are capped to a percentage of the total + // transaction's gas, it is best to keep them low in cases like this one, to + // increase the likelihood of the full refund coming into effect. + uint256 private constant _NOT_ENTERED = 1; + uint256 private constant _ENTERED = 2; + + uint256 private _status; + + constructor() { + _status = _NOT_ENTERED; + } + + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is not supported. It is possible to prevent this from happening + * by making the `nonReentrant` function external, and making it call a + * `private` function that does the actual work. + */ + modifier nonReentrant() { + // On the first call to nonReentrant, _notEntered will be true + require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _status = _ENTERED; + + _; + + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _status = _NOT_ENTERED; + } +} diff --git a/certora/munged/token/ERC1155/ERC1155.sol b/certora/munged/token/ERC1155/ERC1155.sol new file mode 100644 index 000000000..a9a619842 --- /dev/null +++ b/certora/munged/token/ERC1155/ERC1155.sol @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC1155/ERC1155.sol) + +pragma solidity ^0.8.0; + +import "./IERC1155.sol"; +import "./IERC1155Receiver.sol"; +import "./extensions/IERC1155MetadataURI.sol"; +import "../../utils/Address.sol"; +import "../../utils/Context.sol"; +import "../../utils/introspection/ERC165.sol"; + +/** + * @dev Implementation of the basic standard multi-token. + * See https://eips.ethereum.org/EIPS/eip-1155 + * Originally based on code by Enjin: https://github.com/enjin/erc-1155 + * + * _Available since v3.1._ + */ +contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { + using Address for address; + + // Mapping from token ID to account balances + mapping(uint256 => mapping(address => uint256)) private _balances; + + // Mapping from account to operator approvals + mapping(address => mapping(address => bool)) private _operatorApprovals; + + // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json + string private _uri; + + /** + * @dev See {_setURI}. + */ + constructor(string memory uri_) { + _setURI(uri_); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IERC1155).interfaceId || + interfaceId == type(IERC1155MetadataURI).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC1155MetadataURI-uri}. + * + * This implementation returns the same URI for *all* token types. It relies + * on the token type ID substitution mechanism + * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. + * + * Clients calling this function must replace the `\{id\}` substring with the + * actual token type ID. + */ + function uri(uint256) public view virtual override returns (string memory) { + return _uri; + } + + /** + * @dev See {IERC1155-balanceOf}. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { + require(account != address(0), "ERC1155: balance query for the zero address"); + return _balances[id][account]; + } + + /** + * @dev See {IERC1155-balanceOfBatch}. + * + * Requirements: + * + * - `accounts` and `ids` must have the same length. + */ + function balanceOfBatch(address[] memory accounts, uint256[] memory ids) + public + view + virtual + override + returns (uint256[] memory) + { + require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch"); + + uint256[] memory batchBalances = new uint256[](accounts.length); + + for (uint256 i = 0; i < accounts.length; ++i) { + batchBalances[i] = balanceOf(accounts[i], ids[i]); + } + + return batchBalances; + } + + /** + * @dev See {IERC1155-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) public virtual override { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /** + * @dev See {IERC1155-isApprovedForAll}. + */ + function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { + return _operatorApprovals[account][operator]; + } + + /** + * @dev See {IERC1155-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual override { + require( + from == _msgSender() || isApprovedForAll(from, _msgSender()), + "ERC1155: caller is not owner nor approved" + ); + _safeTransferFrom(from, to, id, amount, data); + } + + /** + * @dev See {IERC1155-safeBatchTransferFrom}. + */ + function safeBatchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual override { + require( + from == _msgSender() || isApprovedForAll(from, _msgSender()), + "ERC1155: transfer caller is not owner nor approved" + ); + _safeBatchTransferFrom(from, to, ids, amounts, data); + } + + /** + * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `from` must have a balance of tokens of type `id` of at least `amount`. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function _safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) internal virtual { + require(to != address(0), "ERC1155: transfer to the zero address"); + + address operator = _msgSender(); + uint256[] memory ids = _asSingletonArray(id); + uint256[] memory amounts = _asSingletonArray(amount); + + _beforeTokenTransfer(operator, from, to, ids, amounts, data); + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + _balances[id][to] += amount; + + emit TransferSingle(operator, from, to, id, amount); + + _afterTokenTransfer(operator, from, to, ids, amounts, data); + + _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + */ + function _safeBatchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual { + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + require(to != address(0), "ERC1155: transfer to the zero address"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, from, to, ids, amounts, data); + + for (uint256 i = 0; i < ids.length; ++i) { + uint256 id = ids[i]; + uint256 amount = amounts[i]; + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + _balances[id][to] += amount; + } + + emit TransferBatch(operator, from, to, ids, amounts); + + _afterTokenTransfer(operator, from, to, ids, amounts, data); + + _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); + } + + /** + * @dev Sets a new URI for all token types, by relying on the token type ID + * substitution mechanism + * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. + * + * By this mechanism, any occurrence of the `\{id\}` substring in either the + * URI or any of the amounts in the JSON file at said URI will be replaced by + * clients with the token type ID. + * + * For example, the `https://token-cdn-domain/\{id\}.json` URI would be + * interpreted by clients as + * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` + * for token type ID 0x4cce0. + * + * See {uri}. + * + * Because these URIs cannot be meaningfully represented by the {URI} event, + * this function emits no events. + */ + function _setURI(string memory newuri) internal virtual { + _uri = newuri; + } + + /** + * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function _mint( + address to, + uint256 id, + uint256 amount, + bytes memory data + ) internal virtual { + require(to != address(0), "ERC1155: mint to the zero address"); + + address operator = _msgSender(); + uint256[] memory ids = _asSingletonArray(id); + uint256[] memory amounts = _asSingletonArray(amount); + + _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); + + _balances[id][to] += amount; + emit TransferSingle(operator, address(0), to, id, amount); + + _afterTokenTransfer(operator, address(0), to, ids, amounts, data); + + _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}. + * + * Requirements: + * + * - `ids` and `amounts` must have the same length. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + */ + function _mintBatch( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual { + require(to != address(0), "ERC1155: mint to the zero address"); + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); + + for (uint256 i = 0; i < ids.length; i++) { + _balances[ids[i]][to] += amounts[i]; + } + + emit TransferBatch(operator, address(0), to, ids, amounts); + + _afterTokenTransfer(operator, address(0), to, ids, amounts, data); + + _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data); + } + + /** + * @dev Destroys `amount` tokens of token type `id` from `from` + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `from` must have at least `amount` tokens of token type `id`. + */ + function _burn( + address from, + uint256 id, + uint256 amount + ) internal virtual { + require(from != address(0), "ERC1155: burn from the zero address"); + + address operator = _msgSender(); + uint256[] memory ids = _asSingletonArray(id); + uint256[] memory amounts = _asSingletonArray(amount); + + _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + + emit TransferSingle(operator, from, address(0), id, amount); + + _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}. + * + * Requirements: + * + * - `ids` and `amounts` must have the same length. + */ + function _burnBatch( + address from, + uint256[] memory ids, + uint256[] memory amounts + ) internal virtual { + require(from != address(0), "ERC1155: burn from the zero address"); + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); + + for (uint256 i = 0; i < ids.length; i++) { + uint256 id = ids[i]; + uint256 amount = amounts[i]; + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + } + + emit TransferBatch(operator, from, address(0), ids, amounts); + + _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Emits a {ApprovalForAll} event. + */ + function _setApprovalForAll( + address owner, + address operator, + bool approved + ) internal virtual { + require(owner != operator, "ERC1155: setting approval status for self"); + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev Hook that is called before any token transfer. This includes minting + * and burning, as well as batched variants. + * + * The same hook is called on both single and batched variants. For single + * transfers, the length of the `id` and `amount` arrays will be 1. + * + * Calling conditions (for each `id` and `amount` pair): + * + * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * of token type `id` will be transferred to `to`. + * - When `from` is zero, `amount` tokens of token type `id` will be minted + * for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` + * will be burned. + * - `from` and `to` are never both zero. + * - `ids` and `amounts` have the same, non-zero length. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual {} + + /** + * @dev Hook that is called after any token transfer. This includes minting + * and burning, as well as batched variants. + * + * The same hook is called on both single and batched variants. For single + * transfers, the length of the `id` and `amount` arrays will be 1. + * + * Calling conditions (for each `id` and `amount` pair): + * + * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * of token type `id` will be transferred to `to`. + * - When `from` is zero, `amount` tokens of token type `id` will be minted + * for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` + * will be burned. + * - `from` and `to` are never both zero. + * - `ids` and `amounts` have the same, non-zero length. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual {} + + function _doSafeTransferAcceptanceCheck( + address operator, + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) private { + if (to.isContract()) { + try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { + if (response != IERC1155Receiver.onERC1155Received.selector) { + revert("ERC1155: ERC1155Receiver rejected tokens"); + } + } catch Error(string memory reason) { + revert(reason); + } catch { + revert("ERC1155: transfer to non ERC1155Receiver implementer"); + } + } + } + + function _doSafeBatchTransferAcceptanceCheck( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) private { + if (to.isContract()) { + try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( + bytes4 response + ) { + if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { + revert("ERC1155: ERC1155Receiver rejected tokens"); + } + } catch Error(string memory reason) { + revert(reason); + } catch { + revert("ERC1155: transfer to non ERC1155Receiver implementer"); + } + } + } + + function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { + uint256[] memory array = new uint256[](1); + array[0] = element; + + return array; + } +} diff --git a/certora/munged/token/ERC1155/IERC1155.sol b/certora/munged/token/ERC1155/IERC1155.sol new file mode 100644 index 000000000..f2190a4fa --- /dev/null +++ b/certora/munged/token/ERC1155/IERC1155.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol) + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * @dev Required interface of an ERC1155 compliant contract, as defined in the + * https://eips.ethereum.org/EIPS/eip-1155[EIP]. + * + * _Available since v3.1._ + */ +interface IERC1155 is IERC165 { + /** + * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. + */ + event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); + + /** + * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all + * transfers. + */ + event TransferBatch( + address indexed operator, + address indexed from, + address indexed to, + uint256[] ids, + uint256[] values + ); + + /** + * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to + * `approved`. + */ + event ApprovalForAll(address indexed account, address indexed operator, bool approved); + + /** + * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. + * + * If an {URI} event was emitted for `id`, the standard + * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value + * returned by {IERC1155MetadataURI-uri}. + */ + event URI(string value, uint256 indexed id); + + /** + * @dev Returns the amount of tokens of token type `id` owned by `account`. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function balanceOf(address account, uint256 id) external view returns (uint256); + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. + * + * Requirements: + * + * - `accounts` and `ids` must have the same length. + */ + function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) + external + view + returns (uint256[] memory); + + /** + * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, + * + * Emits an {ApprovalForAll} event. + * + * Requirements: + * + * - `operator` cannot be the caller. + */ + function setApprovalForAll(address operator, bool approved) external; + + /** + * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. + * + * See {setApprovalForAll}. + */ + function isApprovedForAll(address account, address operator) external view returns (bool); + + /** + * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}. + * - `from` must have a balance of tokens of type `id` of at least `amount`. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes calldata data + ) external; + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - `ids` and `amounts` must have the same length. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + */ + function safeBatchTransferFrom( + address from, + address to, + uint256[] calldata ids, + uint256[] calldata amounts, + bytes calldata data + ) external; +} diff --git a/certora/munged/token/ERC1155/IERC1155Receiver.sol b/certora/munged/token/ERC1155/IERC1155Receiver.sol new file mode 100644 index 000000000..0dd271d04 --- /dev/null +++ b/certora/munged/token/ERC1155/IERC1155Receiver.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol) + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * @dev _Available since v3.1._ + */ +interface IERC1155Receiver is IERC165 { + /** + * @dev Handles the receipt of a single ERC1155 token type. This function is + * called at the end of a `safeTransferFrom` after the balance has been updated. + * + * NOTE: To accept the transfer, this must return + * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` + * (i.e. 0xf23a6e61, or its own function selector). + * + * @param operator The address which initiated the transfer (i.e. msg.sender) + * @param from The address which previously owned the token + * @param id The ID of the token being transferred + * @param value The amount of tokens being transferred + * @param data Additional data with no specified format + * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed + */ + function onERC1155Received( + address operator, + address from, + uint256 id, + uint256 value, + bytes calldata data + ) external returns (bytes4); + + /** + * @dev Handles the receipt of a multiple ERC1155 token types. This function + * is called at the end of a `safeBatchTransferFrom` after the balances have + * been updated. + * + * NOTE: To accept the transfer(s), this must return + * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` + * (i.e. 0xbc197c81, or its own function selector). + * + * @param operator The address which initiated the batch transfer (i.e. msg.sender) + * @param from The address which previously owned the token + * @param ids An array containing ids of each token being transferred (order and length must match values array) + * @param values An array containing amounts of each token being transferred (order and length must match ids array) + * @param data Additional data with no specified format + * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed + */ + function onERC1155BatchReceived( + address operator, + address from, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external returns (bytes4); +} diff --git a/certora/munged/token/ERC1155/README.adoc b/certora/munged/token/ERC1155/README.adoc new file mode 100644 index 000000000..2e0b22bae --- /dev/null +++ b/certora/munged/token/ERC1155/README.adoc @@ -0,0 +1,47 @@ += ERC 1155 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc1155 + +This set of interfaces and contracts are all related to the https://eips.ethereum.org/EIPS/eip-1155[ERC1155 Multi Token Standard]. + +The EIP consists of three interfaces which fulfill different roles, found here as {IERC1155}, {IERC1155MetadataURI} and {IERC1155Receiver}. + +{ERC1155} implements the mandatory {IERC1155} interface, as well as the optional extension {IERC1155MetadataURI}, by relying on the substitution mechanism to use the same URI for all token types, dramatically reducing gas costs. + +Additionally there are multiple custom extensions, including: + +* designation of addresses that can pause token transfers for all users ({ERC1155Pausable}). +* destruction of own tokens ({ERC1155Burnable}). + +NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC1155 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc1155.adoc#Presets[ERC1155 Presets] (such as {ERC1155PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. + +== Core + +{{IERC1155}} + +{{IERC1155MetadataURI}} + +{{ERC1155}} + +{{IERC1155Receiver}} + +{{ERC1155Receiver}} + +== Extensions + +{{ERC1155Pausable}} + +{{ERC1155Burnable}} + +{{ERC1155Supply}} + +== Presets + +These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC1155PresetMinterPauser}} + +== Utilities + +{{ERC1155Holder}} diff --git a/certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol b/certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol new file mode 100644 index 000000000..4a7c86e1b --- /dev/null +++ b/certora/munged/token/ERC1155/extensions/ERC1155Burnable.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; + +/** + * @dev Extension of {ERC1155} that allows token holders to destroy both their + * own tokens and those that they have been approved to use. + * + * _Available since v3.1._ + */ +abstract contract ERC1155Burnable is ERC1155 { + function burn( + address account, + uint256 id, + uint256 value + ) public virtual { + require( + account == _msgSender() || isApprovedForAll(account, _msgSender()), + "ERC1155: caller is not owner nor approved" + ); + + _burn(account, id, value); + } + + function burnBatch( + address account, + uint256[] memory ids, + uint256[] memory values + ) public virtual { + require( + account == _msgSender() || isApprovedForAll(account, _msgSender()), + "ERC1155: caller is not owner nor approved" + ); + + _burnBatch(account, ids, values); + } +} diff --git a/certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol b/certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol new file mode 100644 index 000000000..64790e2aa --- /dev/null +++ b/certora/munged/token/ERC1155/extensions/ERC1155Pausable.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Pausable.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; +import "../../../security/Pausable.sol"; + +/** + * @dev ERC1155 token with pausable token transfers, minting and burning. + * + * Useful for scenarios such as preventing trades until the end of an evaluation + * period, or having an emergency switch for freezing all token transfers in the + * event of a large bug. + * + * _Available since v3.1._ + */ +abstract contract ERC1155Pausable is ERC1155, Pausable { + /** + * @dev See {ERC1155-_beforeTokenTransfer}. + * + * Requirements: + * + * - the contract must not be paused. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + + require(!paused(), "ERC1155Pausable: token transfer while paused"); + } +} diff --git a/certora/munged/token/ERC1155/extensions/ERC1155Supply.sol b/certora/munged/token/ERC1155/extensions/ERC1155Supply.sol new file mode 100644 index 000000000..5caa52726 --- /dev/null +++ b/certora/munged/token/ERC1155/extensions/ERC1155Supply.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Supply.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; + +/** + * @dev Extension of ERC1155 that adds tracking of total supply per id. + * + * Useful for scenarios where Fungible and Non-fungible tokens have to be + * clearly identified. Note: While a totalSupply of 1 might mean the + * corresponding is an NFT, there is no guarantees that no other token with the + * same id are not going to be minted. + */ +abstract contract ERC1155Supply is ERC1155 { + mapping(uint256 => uint256) private _totalSupply; + + /** + * @dev Total amount of tokens in with a given id. + */ + function totalSupply(uint256 id) public view virtual returns (uint256) { + return _totalSupply[id]; + } + + /** + * @dev Indicates whether any token exist with a given id, or not. + */ + function exists(uint256 id) public view virtual returns (bool) { + return ERC1155Supply.totalSupply(id) > 0; + } + + /** + * @dev See {ERC1155-_beforeTokenTransfer}. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + + if (from == address(0)) { + for (uint256 i = 0; i < ids.length; ++i) { + _totalSupply[ids[i]] += amounts[i]; + } + } + + if (to == address(0)) { + for (uint256 i = 0; i < ids.length; ++i) { + uint256 id = ids[i]; + uint256 amount = amounts[i]; + uint256 supply = _totalSupply[id]; + require(supply >= amount, "ERC1155: burn amount exceeds totalSupply"); + unchecked { + _totalSupply[id] = supply - amount; + } + } + } + } +} diff --git a/certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol b/certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol new file mode 100644 index 000000000..520a29715 --- /dev/null +++ b/certora/munged/token/ERC1155/extensions/IERC1155MetadataURI.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol) + +pragma solidity ^0.8.0; + +import "../IERC1155.sol"; + +/** + * @dev Interface of the optional ERC1155MetadataExtension interface, as defined + * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP]. + * + * _Available since v3.1._ + */ +interface IERC1155MetadataURI is IERC1155 { + /** + * @dev Returns the URI for token type `id`. + * + * If the `\{id\}` substring is present in the URI, it must be replaced by + * clients with the actual token type ID. + */ + function uri(uint256 id) external view returns (string memory); +} diff --git a/certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol b/certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol new file mode 100644 index 000000000..e57fdcc39 --- /dev/null +++ b/certora/munged/token/ERC1155/presets/ERC1155PresetMinterPauser.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/presets/ERC1155PresetMinterPauser.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; +import "../extensions/ERC1155Burnable.sol"; +import "../extensions/ERC1155Pausable.sol"; +import "../../../access/AccessControlEnumerable.sol"; +import "../../../utils/Context.sol"; + +/** + * @dev {ERC1155} token, including: + * + * - ability for holders to burn (destroy) their tokens + * - a minter role that allows for token minting (creation) + * - a pauser role that allows to stop all token transfers + * + * This contract uses {AccessControl} to lock permissioned functions using the + * different roles - head to its documentation for details. + * + * The account that deploys the contract will be granted the minter and pauser + * roles, as well as the default admin role, which will let it grant both minter + * and pauser roles to other accounts. + * + * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._ + */ +contract ERC1155PresetMinterPauser is Context, AccessControlEnumerable, ERC1155Burnable, ERC1155Pausable { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + /** + * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, and `PAUSER_ROLE` to the account that + * deploys the contract. + */ + constructor(string memory uri) ERC1155(uri) { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + + _setupRole(MINTER_ROLE, _msgSender()); + _setupRole(PAUSER_ROLE, _msgSender()); + } + + /** + * @dev Creates `amount` new tokens for `to`, of token type `id`. + * + * See {ERC1155-_mint}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function mint( + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint"); + + _mint(to, id, amount, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] variant of {mint}. + */ + function mintBatch( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint"); + + _mintBatch(to, ids, amounts, data); + } + + /** + * @dev Pauses all token transfers. + * + * See {ERC1155Pausable} and {Pausable-_pause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function pause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have pauser role to pause"); + _pause(); + } + + /** + * @dev Unpauses all token transfers. + * + * See {ERC1155Pausable} and {Pausable-_unpause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function unpause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have pauser role to unpause"); + _unpause(); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(AccessControlEnumerable, ERC1155) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override(ERC1155, ERC1155Pausable) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + } +} diff --git a/certora/munged/token/ERC1155/presets/README.md b/certora/munged/token/ERC1155/presets/README.md new file mode 100644 index 000000000..468200b72 --- /dev/null +++ b/certora/munged/token/ERC1155/presets/README.md @@ -0,0 +1 @@ +Contract presets are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com/) as a more powerful alternative. diff --git a/certora/munged/token/ERC1155/utils/ERC1155Holder.sol b/certora/munged/token/ERC1155/utils/ERC1155Holder.sol new file mode 100644 index 000000000..7249de841 --- /dev/null +++ b/certora/munged/token/ERC1155/utils/ERC1155Holder.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol) + +pragma solidity ^0.8.0; + +import "./ERC1155Receiver.sol"; + +/** + * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens. + * + * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be + * stuck. + * + * @dev _Available since v3.1._ + */ +contract ERC1155Holder is ERC1155Receiver { + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] memory, + uint256[] memory, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155BatchReceived.selector; + } +} diff --git a/certora/munged/token/ERC1155/utils/ERC1155Receiver.sol b/certora/munged/token/ERC1155/utils/ERC1155Receiver.sol new file mode 100644 index 000000000..2e6804a2d --- /dev/null +++ b/certora/munged/token/ERC1155/utils/ERC1155Receiver.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol) + +pragma solidity ^0.8.0; + +import "../IERC1155Receiver.sol"; +import "../../../utils/introspection/ERC165.sol"; + +/** + * @dev _Available since v3.1._ + */ +abstract contract ERC1155Receiver is ERC165, IERC1155Receiver { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId); + } +} diff --git a/certora/munged/token/ERC20/ERC20.sol b/certora/munged/token/ERC20/ERC20.sol new file mode 100644 index 000000000..dc963af88 --- /dev/null +++ b/certora/munged/token/ERC20/ERC20.sol @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol) + +pragma solidity ^0.8.0; + +import "./IERC20.sol"; +import "./extensions/IERC20Metadata.sol"; +import "../../utils/Context.sol"; + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC20 + * applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20, IERC20Metadata { + mapping(address => uint256) private _balances; + + mapping(address => mapping(address => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + /** + * @dev Sets the values for {name} and {symbol}. + * + * The default value of {decimals} is 18. To select a different value for + * {decimals} you should overload it. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC20} uses, unless this function is + * overridden; + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual override returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address to, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + _transfer(owner, to, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Requirements: + * + * - `from` and `to` cannot be the zero address. + * - `from` must have a balance of at least `amount`. + * - the caller must have allowance for ``from``'s tokens of at least + * `amount`. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) public virtual override returns (bool) { + address spender = _msgSender(); + _spendAllowance(from, spender, amount); + _transfer(from, to, amount); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, allowance(owner, spender) + addedValue); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + address owner = _msgSender(); + uint256 currentAllowance = allowance(owner, spender); + require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); + unchecked { + _approve(owner, spender, currentAllowance - subtractedValue); + } + + return true; + } + + /** + * @dev Moves `amount` of tokens from `sender` to `recipient`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `from` must have a balance of at least `amount`. + */ + function _transfer( + address from, + address to, + uint256 amount + ) internal virtual { + require(from != address(0), "ERC20: transfer from the zero address"); + require(to != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(from, to, amount); + + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + } + _balances[to] += amount; + + emit Transfer(from, to, amount); + + _afterTokenTransfer(from, to, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply += amount; + _balances[account] += amount; + emit Transfer(address(0), account, amount); + + _afterTokenTransfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + uint256 accountBalance = _balances[account]; + require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); + unchecked { + _balances[account] = accountBalance - amount; + } + _totalSupply -= amount; + + emit Transfer(account, address(0), amount); + + _afterTokenTransfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve( + address owner, + address spender, + uint256 amount + ) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `amount`. + * + * Does not update the allowance amount in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Might emit an {Approval} event. + */ + function _spendAllowance( + address owner, + address spender, + uint256 amount + ) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + require(currentAllowance >= amount, "ERC20: insufficient allowance"); + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual {} + + /** + * @dev Hook that is called after any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * has been transferred to `to`. + * - when `from` is zero, `amount` tokens have been minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens have been burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual {} +} diff --git a/certora/munged/token/ERC20/IERC20.sol b/certora/munged/token/ERC20/IERC20.sol new file mode 100644 index 000000000..810ff275f --- /dev/null +++ b/certora/munged/token/ERC20/IERC20.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/certora/munged/token/ERC20/README.adoc b/certora/munged/token/ERC20/README.adoc new file mode 100644 index 000000000..df20c17a2 --- /dev/null +++ b/certora/munged/token/ERC20/README.adoc @@ -0,0 +1,83 @@ += ERC 20 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc20 + +This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-20[ERC20 Token Standard]. + +TIP: For an overview of ERC20 tokens and a walk through on how to create a token contract read our xref:ROOT:erc20.adoc[ERC20 guide]. + +There are a few core contracts that implement the behavior specified in the EIP: + +* {IERC20}: the interface all ERC20 implementations should conform to. +* {IERC20Metadata}: the extended ERC20 interface including the <>, <> and <> functions. +* {ERC20}: the implementation of the ERC20 interface, including the <>, <> and <> optional standard extension to the base interface. + +Additionally there are multiple custom extensions, including: + +* {ERC20Burnable}: destruction of own tokens. +* {ERC20Capped}: enforcement of a cap to the total supply when minting tokens. +* {ERC20Pausable}: ability to pause token transfers. +* {ERC20Snapshot}: efficient storage of past token balances to be later queried at any point in time. +* {ERC20Permit}: gasless approval of tokens (standardized as ERC2612). +* {ERC20FlashMint}: token level support for flash loans through the minting and burning of ephemeral tokens (standardized as ERC3156). +* {ERC20Votes}: support for voting and vote delegation. +* {ERC20VotesComp}: support for voting and vote delegation (compatible with Compound's token, with uint96 restrictions). +* {ERC20Wrapper}: wrapper to create an ERC20 backed by another ERC20, with deposit and withdraw methods. Useful in conjunction with {ERC20Votes}. + +Finally, there are some utilities to interact with ERC20 contracts in various ways. + +* {SafeERC20}: a wrapper around the interface that eliminates the need to handle boolean return values. +* {TokenTimelock}: hold tokens for a beneficiary until a specified time. + +The following related EIPs are in draft status. + +- {ERC20Permit} + +NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC20 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc20.adoc#Presets[ERC20 Presets] (such as {ERC20PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. + +== Core + +{{IERC20}} + +{{IERC20Metadata}} + +{{ERC20}} + +== Extensions + +{{ERC20Burnable}} + +{{ERC20Capped}} + +{{ERC20Pausable}} + +{{ERC20Snapshot}} + +{{ERC20Votes}} + +{{ERC20VotesComp}} + +{{ERC20Wrapper}} + +{{ERC20FlashMint}} + +== Draft EIPs + +The following EIPs are still in Draft status. Due to their nature as drafts, the details of these contracts may change and we cannot guarantee their xref:ROOT:releases-stability.adoc[stability]. Minor releases of OpenZeppelin Contracts may contain breaking changes for the contracts in this directory, which will be duly announced in the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md[changelog]. The EIPs included here are used by projects in production and this may make them less likely to change significantly. + +{{ERC20Permit}} + +== Presets + +These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC20PresetMinterPauser}} + +{{ERC20PresetFixedSupply}} + +== Utilities + +{{SafeERC20}} + +{{TokenTimelock}} diff --git a/certora/munged/token/ERC20/extensions/ERC20Burnable.sol b/certora/munged/token/ERC20/extensions/ERC20Burnable.sol new file mode 100644 index 000000000..1cd08ee81 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Burnable.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../../../utils/Context.sol"; + +/** + * @dev Extension of {ERC20} that allows token holders to destroy both their own + * tokens and those that they have an allowance for, in a way that can be + * recognized off-chain (via event analysis). + */ +abstract contract ERC20Burnable is Context, ERC20 { + /** + * @dev Destroys `amount` tokens from the caller. + * + * See {ERC20-_burn}. + */ + function burn(uint256 amount) public virtual { + _burn(_msgSender(), amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, deducting from the caller's + * allowance. + * + * See {ERC20-_burn} and {ERC20-allowance}. + * + * Requirements: + * + * - the caller must have allowance for ``accounts``'s tokens of at least + * `amount`. + */ + function burnFrom(address account, uint256 amount) public virtual { + _spendAllowance(account, _msgSender(), amount); + _burn(account, amount); + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Capped.sol b/certora/munged/token/ERC20/extensions/ERC20Capped.sol new file mode 100644 index 000000000..16f830d18 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Capped.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Capped.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; + +/** + * @dev Extension of {ERC20} that adds a cap to the supply of tokens. + */ +abstract contract ERC20Capped is ERC20 { + uint256 private immutable _cap; + + /** + * @dev Sets the value of the `cap`. This value is immutable, it can only be + * set once during construction. + */ + constructor(uint256 cap_) { + require(cap_ > 0, "ERC20Capped: cap is 0"); + _cap = cap_; + } + + /** + * @dev Returns the cap on the token's total supply. + */ + function cap() public view virtual returns (uint256) { + return _cap; + } + + /** + * @dev See {ERC20-_mint}. + */ + function _mint(address account, uint256 amount) internal virtual override { + require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded"); + super._mint(account, amount); + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol b/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol new file mode 100644 index 000000000..cbcf3b60f --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20FlashMint.sol) + +pragma solidity ^0.8.0; + +import "../../../interfaces/IERC3156FlashBorrower.sol"; +import "../../../interfaces/IERC3156FlashLender.sol"; +import "../ERC20.sol"; + +/** + * @dev Implementation of the ERC3156 Flash loans extension, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * Adds the {flashLoan} method, which provides flash loan support at the token + * level. By default there is no fee, but this can be changed by overriding {flashFee}. + * + * _Available since v4.1._ + */ +abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { + bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); + + /** + * @dev Returns the maximum amount of tokens available for loan. + * @param token The address of the token that is requested. + * @return The amount of token that can be loaned. + */ + function maxFlashLoan(address token) public view virtual override returns (uint256) { + return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0; + } + + /** + * @dev Returns the fee applied when doing flash loans. By default this + * implementation has 0 fees. This function can be overloaded to make + * the flash loan mechanism deflationary. + * @param token The token to be flash loaned. + * @param amount The amount of tokens to be loaned. + * @return The fees applied to the corresponding flash loan. + */ + function flashFee(address token, uint256 amount) public view virtual override returns (uint256) { + require(token == address(this), "ERC20FlashMint: wrong token"); + // silence warning about unused variable without the addition of bytecode. + amount; + return 0; + } + + /** + * @dev Performs a flash loan. New tokens are minted and sent to the + * `receiver`, who is required to implement the {IERC3156FlashBorrower} + * interface. By the end of the flash loan, the receiver is expected to own + * amount + fee tokens and have them approved back to the token contract itself so + * they can be burned. + * @param receiver The receiver of the flash loan. Should implement the + * {IERC3156FlashBorrower.onFlashLoan} interface. + * @param token The token to be flash loaned. Only `address(this)` is + * supported. + * @param amount The amount of tokens to be loaned. + * @param data An arbitrary datafield that is passed to the receiver. + * @return `true` if the flash loan was successful. + */ + // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount + // minted at the beginning is always recovered and burned at the end, or else the entire function will revert. + // slither-disable-next-line reentrancy-no-eth + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) public virtual override returns (bool) { + require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan"); + uint256 fee = flashFee(token, amount); + _mint(address(receiver), amount); + require( + receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, + "ERC20FlashMint: invalid return value" + ); + uint256 currentAllowance = allowance(address(receiver), address(this)); + require(currentAllowance >= amount + fee, "ERC20FlashMint: allowance does not allow refund"); + _approve(address(receiver), address(this), currentAllowance - amount - fee); + _burn(address(receiver), amount + fee); + return true; + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Pausable.sol b/certora/munged/token/ERC20/extensions/ERC20Pausable.sol new file mode 100644 index 000000000..e448e96a6 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Pausable.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Pausable.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../../../security/Pausable.sol"; + +/** + * @dev ERC20 token with pausable token transfers, minting and burning. + * + * Useful for scenarios such as preventing trades until the end of an evaluation + * period, or having an emergency switch for freezing all token transfers in the + * event of a large bug. + */ +abstract contract ERC20Pausable is ERC20, Pausable { + /** + * @dev See {ERC20-_beforeTokenTransfer}. + * + * Requirements: + * + * - the contract must not be paused. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override { + super._beforeTokenTransfer(from, to, amount); + + require(!paused(), "ERC20Pausable: token transfer while paused"); + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Snapshot.sol b/certora/munged/token/ERC20/extensions/ERC20Snapshot.sol new file mode 100644 index 000000000..6f59666ac --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Snapshot.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Snapshot.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../../../utils/Arrays.sol"; +import "../../../utils/Counters.sol"; + +/** + * @dev This contract extends an ERC20 token with a snapshot mechanism. When a snapshot is created, the balances and + * total supply at the time are recorded for later access. + * + * This can be used to safely create mechanisms based on token balances such as trustless dividends or weighted voting. + * In naive implementations it's possible to perform a "double spend" attack by reusing the same balance from different + * accounts. By using snapshots to calculate dividends or voting power, those attacks no longer apply. It can also be + * used to create an efficient ERC20 forking mechanism. + * + * Snapshots are created by the internal {_snapshot} function, which will emit the {Snapshot} event and return a + * snapshot id. To get the total supply at the time of a snapshot, call the function {totalSupplyAt} with the snapshot + * id. To get the balance of an account at the time of a snapshot, call the {balanceOfAt} function with the snapshot id + * and the account address. + * + * NOTE: Snapshot policy can be customized by overriding the {_getCurrentSnapshotId} method. For example, having it + * return `block.number` will trigger the creation of snapshot at the beginning of each new block. When overriding this + * function, be careful about the monotonicity of its result. Non-monotonic snapshot ids will break the contract. + * + * Implementing snapshots for every block using this method will incur significant gas costs. For a gas-efficient + * alternative consider {ERC20Votes}. + * + * ==== Gas Costs + * + * Snapshots are efficient. Snapshot creation is _O(1)_. Retrieval of balances or total supply from a snapshot is _O(log + * n)_ in the number of snapshots that have been created, although _n_ for a specific account will generally be much + * smaller since identical balances in subsequent snapshots are stored as a single entry. + * + * There is a constant overhead for normal ERC20 transfers due to the additional snapshot bookkeeping. This overhead is + * only significant for the first transfer that immediately follows a snapshot for a particular account. Subsequent + * transfers will have normal cost until the next snapshot, and so on. + */ + +abstract contract ERC20Snapshot is ERC20 { + // Inspired by Jordi Baylina's MiniMeToken to record historical balances: + // https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol + + using Arrays for uint256[]; + using Counters for Counters.Counter; + + // Snapshotted values have arrays of ids and the value corresponding to that id. These could be an array of a + // Snapshot struct, but that would impede usage of functions that work on an array. + struct Snapshots { + uint256[] ids; + uint256[] values; + } + + mapping(address => Snapshots) private _accountBalanceSnapshots; + Snapshots private _totalSupplySnapshots; + + // Snapshot ids increase monotonically, with the first value being 1. An id of 0 is invalid. + Counters.Counter private _currentSnapshotId; + + /** + * @dev Emitted by {_snapshot} when a snapshot identified by `id` is created. + */ + event Snapshot(uint256 id); + + /** + * @dev Creates a new snapshot and returns its snapshot id. + * + * Emits a {Snapshot} event that contains the same id. + * + * {_snapshot} is `internal` and you have to decide how to expose it externally. Its usage may be restricted to a + * set of accounts, for example using {AccessControl}, or it may be open to the public. + * + * [WARNING] + * ==== + * While an open way of calling {_snapshot} is required for certain trust minimization mechanisms such as forking, + * you must consider that it can potentially be used by attackers in two ways. + * + * First, it can be used to increase the cost of retrieval of values from snapshots, although it will grow + * logarithmically thus rendering this attack ineffective in the long term. Second, it can be used to target + * specific accounts and increase the cost of ERC20 transfers for them, in the ways specified in the Gas Costs + * section above. + * + * We haven't measured the actual numbers; if this is something you're interested in please reach out to us. + * ==== + */ + function _snapshot() internal virtual returns (uint256) { + _currentSnapshotId.increment(); + + uint256 currentId = _getCurrentSnapshotId(); + emit Snapshot(currentId); + return currentId; + } + + /** + * @dev Get the current snapshotId + */ + function _getCurrentSnapshotId() internal view virtual returns (uint256) { + return _currentSnapshotId.current(); + } + + /** + * @dev Retrieves the balance of `account` at the time `snapshotId` was created. + */ + function balanceOfAt(address account, uint256 snapshotId) public view virtual returns (uint256) { + (bool snapshotted, uint256 value) = _valueAt(snapshotId, _accountBalanceSnapshots[account]); + + return snapshotted ? value : balanceOf(account); + } + + /** + * @dev Retrieves the total supply at the time `snapshotId` was created. + */ + function totalSupplyAt(uint256 snapshotId) public view virtual returns (uint256) { + (bool snapshotted, uint256 value) = _valueAt(snapshotId, _totalSupplySnapshots); + + return snapshotted ? value : totalSupply(); + } + + // Update balance and/or total supply snapshots before the values are modified. This is implemented + // in the _beforeTokenTransfer hook, which is executed for _mint, _burn, and _transfer operations. + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override { + super._beforeTokenTransfer(from, to, amount); + + if (from == address(0)) { + // mint + _updateAccountSnapshot(to); + _updateTotalSupplySnapshot(); + } else if (to == address(0)) { + // burn + _updateAccountSnapshot(from); + _updateTotalSupplySnapshot(); + } else { + // transfer + _updateAccountSnapshot(from); + _updateAccountSnapshot(to); + } + } + + function _valueAt(uint256 snapshotId, Snapshots storage snapshots) private view returns (bool, uint256) { + require(snapshotId > 0, "ERC20Snapshot: id is 0"); + require(snapshotId <= _getCurrentSnapshotId(), "ERC20Snapshot: nonexistent id"); + + // When a valid snapshot is queried, there are three possibilities: + // a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never + // created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds + // to this id is the current one. + // b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the + // requested id, and its value is the one to return. + // c) More snapshots were created after the requested one, and the queried value was later modified. There will be + // no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is + // larger than the requested one. + // + // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if + // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does + // exactly this. + + uint256 index = snapshots.ids.findUpperBound(snapshotId); + + if (index == snapshots.ids.length) { + return (false, 0); + } else { + return (true, snapshots.values[index]); + } + } + + function _updateAccountSnapshot(address account) private { + _updateSnapshot(_accountBalanceSnapshots[account], balanceOf(account)); + } + + function _updateTotalSupplySnapshot() private { + _updateSnapshot(_totalSupplySnapshots, totalSupply()); + } + + function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) private { + uint256 currentId = _getCurrentSnapshotId(); + if (_lastSnapshotId(snapshots.ids) < currentId) { + snapshots.ids.push(currentId); + snapshots.values.push(currentValue); + } + } + + function _lastSnapshotId(uint256[] storage ids) private view returns (uint256) { + if (ids.length == 0) { + return 0; + } else { + return ids[ids.length - 1]; + } + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Votes.sol b/certora/munged/token/ERC20/extensions/ERC20Votes.sol new file mode 100644 index 000000000..c0e88bc19 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Votes.sol @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Votes.sol) + +pragma solidity ^0.8.0; + +import "./draft-ERC20Permit.sol"; +import "../../../utils/math/Math.sol"; +import "../../../governance/utils/IVotes.sol"; +import "../../../utils/math/SafeCast.sol"; +import "../../../utils/cryptography/ECDSA.sol"; + +/** + * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's, + * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1. + * + * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module. + * + * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either + * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting + * power can be queried through the public accessors {getVotes} and {getPastVotes}. + * + * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it + * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + * + * _Available since v4.2._ + */ +abstract contract ERC20Votes is IVotes, ERC20Permit { + struct Checkpoint { + uint32 fromBlock; + uint224 votes; + } + + bytes32 private constant _DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + + mapping(address => address) private _delegates; + mapping(address => Checkpoint[]) private _checkpoints; + Checkpoint[] private _totalSupplyCheckpoints; + + /** + * @dev Get the `pos`-th checkpoint for `account`. + */ + function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) { + return _checkpoints[account][pos]; + } + + /** + * @dev Get number of checkpoints for `account`. + */ + function numCheckpoints(address account) public view virtual returns (uint32) { + return SafeCast.toUint32(_checkpoints[account].length); + } + + /** + * @dev Get the address `account` is currently delegating to. + */ + function delegates(address account) public view virtual override returns (address) { + return _delegates[account]; + } + + /** + * @dev Gets the current votes balance for `account` + */ + function getVotes(address account) public view virtual override returns (uint256) { + uint256 pos = _checkpoints[account].length; + return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; + } + + /** + * @dev Retrieve the number of votes for `account` at the end of `blockNumber`. + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_checkpoints[account], blockNumber); + } + + /** + * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. + * It is but NOT the sum of all the delegated votes! + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); + } + + /** + * @dev Lookup a value in a list of (sorted) checkpoints. + */ + function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) { + // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. + // + // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). + // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant. + // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) + // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) + // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not + // out of bounds (in which case we're looking too far in the past and the result is 0). + // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is + // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out + // the same. + uint256 high = ckpts.length; + uint256 low = 0; + while (low < high) { + uint256 mid = Math.average(low, high); + if (ckpts[mid].fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + return high == 0 ? 0 : ckpts[high - 1].votes; + } + + /** + * @dev Delegate votes from the sender to `delegatee`. + */ + function delegate(address delegatee) public virtual override { + _delegate(_msgSender(), delegatee); + } + + /** + * @dev Delegates votes from signer to `delegatee` + */ + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + require(block.timestamp <= expiry, "ERC20Votes: signature expired"); + address signer = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), + v, + r, + s + ); + require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce"); + _delegate(signer, delegatee); + } + + /** + * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). + */ + function _maxSupply() internal view virtual returns (uint224) { + return type(uint224).max; + } + + /** + * @dev Snapshots the totalSupply after it has been increased. + */ + function _mint(address account, uint256 amount) internal virtual override { + super._mint(account, amount); + require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); + + _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); + } + + /** + * @dev Snapshots the totalSupply after it has been decreased. + */ + function _burn(address account, uint256 amount) internal virtual override { + super._burn(account, amount); + + _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); + } + + /** + * @dev Move voting power when tokens are transferred. + * + * Emits a {DelegateVotesChanged} event. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override { + super._afterTokenTransfer(from, to, amount); + + _moveVotingPower(delegates(from), delegates(to), amount); + } + + /** + * @dev Change delegation for `delegator` to `delegatee`. + * + * Emits events {DelegateChanged} and {DelegateVotesChanged}. + */ + function _delegate(address delegator, address delegatee) internal virtual { + address currentDelegate = delegates(delegator); + uint256 delegatorBalance = balanceOf(delegator); + _delegates[delegator] = delegatee; + + emit DelegateChanged(delegator, currentDelegate, delegatee); + + _moveVotingPower(currentDelegate, delegatee, delegatorBalance); + } + + function _moveVotingPower( + address src, + address dst, + uint256 amount + ) private { + if (src != dst && amount > 0) { + if (src != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); + emit DelegateVotesChanged(src, oldWeight, newWeight); + } + + if (dst != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); + emit DelegateVotesChanged(dst, oldWeight, newWeight); + } + } + } + + function _writeCheckpoint( + Checkpoint[] storage ckpts, + function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; + newWeight = op(oldWeight, delta); + + if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { + ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); + } else { + ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + } + } + + function _add(uint256 a, uint256 b) private pure returns (uint256) { + return a + b; + } + + function _subtract(uint256 a, uint256 b) private pure returns (uint256) { + return a - b; + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20VotesComp.sol b/certora/munged/token/ERC20/extensions/ERC20VotesComp.sol new file mode 100644 index 000000000..0461310a4 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20VotesComp.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20VotesComp.sol) + +pragma solidity ^0.8.0; + +import "./ERC20Votes.sol"; + +/** + * @dev Extension of ERC20 to support Compound's voting and delegation. This version exactly matches Compound's + * interface, with the drawback of only supporting supply up to (2^96^ - 1). + * + * NOTE: You should use this contract if you need exact compatibility with COMP (for example in order to use your token + * with Governor Alpha or Bravo) and if you are sure the supply cap of 2^96^ is enough for you. Otherwise, use the + * {ERC20Votes} variant of this module. + * + * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either + * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting + * power can be queried through the public accessors {getCurrentVotes} and {getPriorVotes}. + * + * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it + * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + * + * _Available since v4.2._ + */ +abstract contract ERC20VotesComp is ERC20Votes { + /** + * @dev Comp version of the {getVotes} accessor, with `uint96` return type. + */ + function getCurrentVotes(address account) external view virtual returns (uint96) { + return SafeCast.toUint96(getVotes(account)); + } + + /** + * @dev Comp version of the {getPastVotes} accessor, with `uint96` return type. + */ + function getPriorVotes(address account, uint256 blockNumber) external view virtual returns (uint96) { + return SafeCast.toUint96(getPastVotes(account, blockNumber)); + } + + /** + * @dev Maximum token supply. Reduced to `type(uint96).max` (2^96^ - 1) to fit COMP interface. + */ + function _maxSupply() internal view virtual override returns (uint224) { + return type(uint96).max; + } +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol b/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol new file mode 100644 index 000000000..32a3b830a --- /dev/null +++ b/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Wrapper.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../utils/SafeERC20.sol"; + +/** + * @dev Extension of the ERC20 token contract to support token wrapping. + * + * Users can deposit and withdraw "underlying tokens" and receive a matching number of "wrapped tokens". This is useful + * in conjunction with other modules. For example, combining this wrapping mechanism with {ERC20Votes} will allow the + * wrapping of an existing "basic" ERC20 into a governance token. + * + * _Available since v4.2._ + */ +abstract contract ERC20Wrapper is ERC20 { + IERC20 public immutable underlying; + + constructor(IERC20 underlyingToken) { + underlying = underlyingToken; + } + + /** + * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens. + */ + function depositFor(address account, uint256 amount) public virtual returns (bool) { + SafeERC20.safeTransferFrom(underlying, _msgSender(), address(this), amount); + _mint(account, amount); + return true; + } + + /** + * @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens. + */ + function withdrawTo(address account, uint256 amount) public virtual returns (bool) { + _burn(_msgSender(), amount); + SafeERC20.safeTransfer(underlying, account, amount); + return true; + } + + /** + * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal + * function that can be exposed with access control if desired. + */ + function _recover(address account) internal virtual returns (uint256) { + uint256 value = underlying.balanceOf(address(this)) - totalSupply(); + _mint(account, value); + return value; + } +} diff --git a/certora/munged/token/ERC20/extensions/IERC20Metadata.sol b/certora/munged/token/ERC20/extensions/IERC20Metadata.sol new file mode 100644 index 000000000..83ba6ac5e --- /dev/null +++ b/certora/munged/token/ERC20/extensions/IERC20Metadata.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) + +pragma solidity ^0.8.0; + +import "../IERC20.sol"; + +/** + * @dev Interface for the optional metadata functions from the ERC20 standard. + * + * _Available since v4.1._ + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} diff --git a/certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol b/certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol new file mode 100644 index 000000000..cf72fc086 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/draft-ERC20Permit.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-ERC20Permit.sol) + +pragma solidity ^0.8.0; + +import "./draft-IERC20Permit.sol"; +import "../ERC20.sol"; +import "../../../utils/cryptography/draft-EIP712.sol"; +import "../../../utils/cryptography/ECDSA.sol"; +import "../../../utils/Counters.sol"; + +/** + * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + * + * _Available since v3.4._ + */ +abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { + using Counters for Counters.Counter; + + mapping(address => Counters.Counter) private _nonces; + + // solhint-disable-next-line var-name-mixedcase + bytes32 private immutable _PERMIT_TYPEHASH = + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + + /** + * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. + * + * It's a good idea to use the same `name` that is defined as the ERC20 token name. + */ + constructor(string memory name) EIP712(name, "1") {} + + /** + * @dev See {IERC20Permit-permit}. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); + + bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); + + bytes32 hash = _hashTypedDataV4(structHash); + + address signer = ECDSA.recover(hash, v, r, s); + require(signer == owner, "ERC20Permit: invalid signature"); + + _approve(owner, spender, value); + } + + /** + * @dev See {IERC20Permit-nonces}. + */ + function nonces(address owner) public view virtual override returns (uint256) { + return _nonces[owner].current(); + } + + /** + * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view override returns (bytes32) { + return _domainSeparatorV4(); + } + + /** + * @dev "Consume a nonce": return the current value and increment. + * + * _Available since v4.1._ + */ + function _useNonce(address owner) internal virtual returns (uint256 current) { + Counters.Counter storage nonce = _nonces[owner]; + current = nonce.current(); + nonce.increment(); + } +} diff --git a/certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol b/certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol new file mode 100644 index 000000000..6363b1408 --- /dev/null +++ b/certora/munged/token/ERC20/extensions/draft-IERC20Permit.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + */ +interface IERC20Permit { + /** + * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, + * given ``owner``'s signed approval. + * + * IMPORTANT: The same issues {IERC20-approve} has related to transaction + * ordering also apply here. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `deadline` must be a timestamp in the future. + * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` + * over the EIP712-formatted function arguments. + * - the signature must use ``owner``'s current nonce (see {nonces}). + * + * For more information on the signature format, see the + * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP + * section]. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + /** + * @dev Returns the current nonce for `owner`. This value must be + * included whenever a signature is generated for {permit}. + * + * Every successful call to {permit} increases ``owner``'s nonce by one. This + * prevents a signature from being used multiple times. + */ + function nonces(address owner) external view returns (uint256); + + /** + * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32); +} diff --git a/certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol b/certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol new file mode 100644 index 000000000..52afef3ab --- /dev/null +++ b/certora/munged/token/ERC20/presets/ERC20PresetFixedSupply.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/presets/ERC20PresetFixedSupply.sol) +pragma solidity ^0.8.0; + +import "../extensions/ERC20Burnable.sol"; + +/** + * @dev {ERC20} token, including: + * + * - Preminted initial supply + * - Ability for holders to burn (destroy) their tokens + * - No access control mechanism (for minting/pausing) and hence no governance + * + * This contract uses {ERC20Burnable} to include burn capabilities - head to + * its documentation for details. + * + * _Available since v3.4._ + * + * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._ + */ +contract ERC20PresetFixedSupply is ERC20Burnable { + /** + * @dev Mints `initialSupply` amount of token and transfers them to `owner`. + * + * See {ERC20-constructor}. + */ + constructor( + string memory name, + string memory symbol, + uint256 initialSupply, + address owner + ) ERC20(name, symbol) { + _mint(owner, initialSupply); + } +} diff --git a/certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol b/certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol new file mode 100644 index 000000000..e711a894f --- /dev/null +++ b/certora/munged/token/ERC20/presets/ERC20PresetMinterPauser.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/presets/ERC20PresetMinterPauser.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../extensions/ERC20Burnable.sol"; +import "../extensions/ERC20Pausable.sol"; +import "../../../access/AccessControlEnumerable.sol"; +import "../../../utils/Context.sol"; + +/** + * @dev {ERC20} token, including: + * + * - ability for holders to burn (destroy) their tokens + * - a minter role that allows for token minting (creation) + * - a pauser role that allows to stop all token transfers + * + * This contract uses {AccessControl} to lock permissioned functions using the + * different roles - head to its documentation for details. + * + * The account that deploys the contract will be granted the minter and pauser + * roles, as well as the default admin role, which will let it grant both minter + * and pauser roles to other accounts. + * + * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._ + */ +contract ERC20PresetMinterPauser is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + /** + * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the + * account that deploys the contract. + * + * See {ERC20-constructor}. + */ + constructor(string memory name, string memory symbol) ERC20(name, symbol) { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + + _setupRole(MINTER_ROLE, _msgSender()); + _setupRole(PAUSER_ROLE, _msgSender()); + } + + /** + * @dev Creates `amount` new tokens for `to`. + * + * See {ERC20-_mint}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function mint(address to, uint256 amount) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint"); + _mint(to, amount); + } + + /** + * @dev Pauses all token transfers. + * + * See {ERC20Pausable} and {Pausable-_pause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function pause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause"); + _pause(); + } + + /** + * @dev Unpauses all token transfers. + * + * See {ERC20Pausable} and {Pausable-_unpause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function unpause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause"); + _unpause(); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override(ERC20, ERC20Pausable) { + super._beforeTokenTransfer(from, to, amount); + } +} diff --git a/certora/munged/token/ERC20/presets/README.md b/certora/munged/token/ERC20/presets/README.md new file mode 100644 index 000000000..468200b72 --- /dev/null +++ b/certora/munged/token/ERC20/presets/README.md @@ -0,0 +1 @@ +Contract presets are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com/) as a more powerful alternative. diff --git a/certora/munged/token/ERC20/utils/SafeERC20.sol b/certora/munged/token/ERC20/utils/SafeERC20.sol new file mode 100644 index 000000000..5752d9313 --- /dev/null +++ b/certora/munged/token/ERC20/utils/SafeERC20.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) + +pragma solidity ^0.8.0; + +import "../IERC20.sol"; +import "../../../utils/Address.sol"; + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using Address for address; + + function safeTransfer( + IERC20 token, + address to, + uint256 value + ) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom( + IERC20 token, + address from, + address to, + uint256 value + ) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + /** + * @dev Deprecated. This function has issues similar to the ones found in + * {IERC20-approve}, and its usage is discouraged. + * + * Whenever possible, use {safeIncreaseAllowance} and + * {safeDecreaseAllowance} instead. + */ + function safeApprove( + IERC20 token, + address spender, + uint256 value + ) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function safeIncreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + uint256 newAllowance = token.allowance(address(this), spender) + value; + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + function safeDecreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + unchecked { + uint256 oldAllowance = token.allowance(address(this), spender); + require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); + uint256 newAllowance = oldAllowance - value; + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); + if (returndata.length > 0) { + // Return data is optional + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} diff --git a/certora/munged/token/ERC20/utils/TokenTimelock.sol b/certora/munged/token/ERC20/utils/TokenTimelock.sol new file mode 100644 index 000000000..d879a7e7d --- /dev/null +++ b/certora/munged/token/ERC20/utils/TokenTimelock.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/utils/TokenTimelock.sol) + +pragma solidity ^0.8.0; + +import "./SafeERC20.sol"; + +/** + * @dev A token holder contract that will allow a beneficiary to extract the + * tokens after a given release time. + * + * Useful for simple vesting schedules like "advisors get all of their tokens + * after 1 year". + */ +contract TokenTimelock { + using SafeERC20 for IERC20; + + // ERC20 basic token contract being held + IERC20 private immutable _token; + + // beneficiary of tokens after they are released + address private immutable _beneficiary; + + // timestamp when token release is enabled + uint256 private immutable _releaseTime; + + /** + * @dev Deploys a timelock instance that is able to hold the token specified, and will only release it to + * `beneficiary_` when {release} is invoked after `releaseTime_`. The release time is specified as a Unix timestamp + * (in seconds). + */ + constructor( + IERC20 token_, + address beneficiary_, + uint256 releaseTime_ + ) { + require(releaseTime_ > block.timestamp, "TokenTimelock: release time is before current time"); + _token = token_; + _beneficiary = beneficiary_; + _releaseTime = releaseTime_; + } + + /** + * @dev Returns the token being held. + */ + function token() public view virtual returns (IERC20) { + return _token; + } + + /** + * @dev Returns the beneficiary that will receive the tokens. + */ + function beneficiary() public view virtual returns (address) { + return _beneficiary; + } + + /** + * @dev Returns the time when the tokens are released in seconds since Unix epoch (i.e. Unix timestamp). + */ + function releaseTime() public view virtual returns (uint256) { + return _releaseTime; + } + + /** + * @dev Transfers tokens held by the timelock to the beneficiary. Will only succeed if invoked after the release + * time. + */ + function release() public virtual { + require(block.timestamp >= releaseTime(), "TokenTimelock: current time is before release time"); + + uint256 amount = token().balanceOf(address(this)); + require(amount > 0, "TokenTimelock: no tokens to release"); + + token().safeTransfer(beneficiary(), amount); + } +} diff --git a/certora/munged/token/ERC721/ERC721.sol b/certora/munged/token/ERC721/ERC721.sol new file mode 100644 index 000000000..5e61eaf65 --- /dev/null +++ b/certora/munged/token/ERC721/ERC721.sol @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/ERC721.sol) + +pragma solidity ^0.8.0; + +import "./IERC721.sol"; +import "./IERC721Receiver.sol"; +import "./extensions/IERC721Metadata.sol"; +import "../../utils/Address.sol"; +import "../../utils/Context.sol"; +import "../../utils/Strings.sol"; +import "../../utils/introspection/ERC165.sol"; + +/** + * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including + * the Metadata extension, but not including the Enumerable extension, which is available separately as + * {ERC721Enumerable}. + */ +contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { + using Address for address; + using Strings for uint256; + + // Token name + string private _name; + + // Token symbol + string private _symbol; + + // Mapping from token ID to owner address + mapping(uint256 => address) private _owners; + + // Mapping owner address to token count + mapping(address => uint256) private _balances; + + // Mapping from token ID to approved address + mapping(uint256 => address) private _tokenApprovals; + + // Mapping from owner to operator approvals + mapping(address => mapping(address => bool)) private _operatorApprovals; + + /** + * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IERC721).interfaceId || + interfaceId == type(IERC721Metadata).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC721-balanceOf}. + */ + function balanceOf(address owner) public view virtual override returns (uint256) { + require(owner != address(0), "ERC721: balance query for the zero address"); + return _balances[owner]; + } + + /** + * @dev See {IERC721-ownerOf}. + */ + function ownerOf(uint256 tokenId) public view virtual override returns (address) { + address owner = _owners[tokenId]; + require(owner != address(0), "ERC721: owner query for nonexistent token"); + return owner; + } + + /** + * @dev See {IERC721Metadata-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IERC721Metadata-symbol}. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev See {IERC721Metadata-tokenURI}. + */ + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); + + string memory baseURI = _baseURI(); + return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; + } + + /** + * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each + * token will be the concatenation of the `baseURI` and the `tokenId`. Empty + * by default, can be overridden in child contracts. + */ + function _baseURI() internal view virtual returns (string memory) { + return ""; + } + + /** + * @dev See {IERC721-approve}. + */ + function approve(address to, uint256 tokenId) public virtual override { + address owner = ERC721.ownerOf(tokenId); + require(to != owner, "ERC721: approval to current owner"); + + require( + _msgSender() == owner || isApprovedForAll(owner, _msgSender()), + "ERC721: approve caller is not owner nor approved for all" + ); + + _approve(to, tokenId); + } + + /** + * @dev See {IERC721-getApproved}. + */ + function getApproved(uint256 tokenId) public view virtual override returns (address) { + require(_exists(tokenId), "ERC721: approved query for nonexistent token"); + + return _tokenApprovals[tokenId]; + } + + /** + * @dev See {IERC721-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) public virtual override { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /** + * @dev See {IERC721-isApprovedForAll}. + */ + function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { + return _operatorApprovals[owner][operator]; + } + + /** + * @dev See {IERC721-transferFrom}. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + //solhint-disable-next-line max-line-length + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); + + _transfer(from, to, tokenId); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + safeTransferFrom(from, to, tokenId, ""); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) public virtual override { + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); + _safeTransfer(from, to, tokenId, _data); + } + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * `_data` is additional data, it has no specified format and it is sent in call to `to`. + * + * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. + * implement alternative mechanisms to perform token transfer, such as signature-based. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeTransfer( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) internal virtual { + _transfer(from, to, tokenId); + require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); + } + + /** + * @dev Returns whether `tokenId` exists. + * + * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. + * + * Tokens start existing when they are minted (`_mint`), + * and stop existing when they are burned (`_burn`). + */ + function _exists(uint256 tokenId) internal view virtual returns (bool) { + return _owners[tokenId] != address(0); + } + + /** + * @dev Returns whether `spender` is allowed to manage `tokenId`. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { + require(_exists(tokenId), "ERC721: operator query for nonexistent token"); + address owner = ERC721.ownerOf(tokenId); + return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); + } + + /** + * @dev Safely mints `tokenId` and transfers it to `to`. + * + * Requirements: + * + * - `tokenId` must not exist. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeMint(address to, uint256 tokenId) internal virtual { + _safeMint(to, tokenId, ""); + } + + /** + * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is + * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. + */ + function _safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) internal virtual { + _mint(to, tokenId); + require( + _checkOnERC721Received(address(0), to, tokenId, _data), + "ERC721: transfer to non ERC721Receiver implementer" + ); + } + + /** + * @dev Mints `tokenId` and transfers it to `to`. + * + * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible + * + * Requirements: + * + * - `tokenId` must not exist. + * - `to` cannot be the zero address. + * + * Emits a {Transfer} event. + */ + function _mint(address to, uint256 tokenId) internal virtual { + require(to != address(0), "ERC721: mint to the zero address"); + require(!_exists(tokenId), "ERC721: token already minted"); + + _beforeTokenTransfer(address(0), to, tokenId); + + _balances[to] += 1; + _owners[tokenId] = to; + + emit Transfer(address(0), to, tokenId); + + _afterTokenTransfer(address(0), to, tokenId); + } + + /** + * @dev Destroys `tokenId`. + * The approval is cleared when the token is burned. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Transfer} event. + */ + function _burn(uint256 tokenId) internal virtual { + address owner = ERC721.ownerOf(tokenId); + + _beforeTokenTransfer(owner, address(0), tokenId); + + // Clear approvals + _approve(address(0), tokenId); + + _balances[owner] -= 1; + delete _owners[tokenId]; + + emit Transfer(owner, address(0), tokenId); + + _afterTokenTransfer(owner, address(0), tokenId); + } + + /** + * @dev Transfers `tokenId` from `from` to `to`. + * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * + * Emits a {Transfer} event. + */ + function _transfer( + address from, + address to, + uint256 tokenId + ) internal virtual { + require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); + require(to != address(0), "ERC721: transfer to the zero address"); + + _beforeTokenTransfer(from, to, tokenId); + + // Clear approvals from the previous owner + _approve(address(0), tokenId); + + _balances[from] -= 1; + _balances[to] += 1; + _owners[tokenId] = to; + + emit Transfer(from, to, tokenId); + + _afterTokenTransfer(from, to, tokenId); + } + + /** + * @dev Approve `to` to operate on `tokenId` + * + * Emits a {Approval} event. + */ + function _approve(address to, uint256 tokenId) internal virtual { + _tokenApprovals[tokenId] = to; + emit Approval(ERC721.ownerOf(tokenId), to, tokenId); + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Emits a {ApprovalForAll} event. + */ + function _setApprovalForAll( + address owner, + address operator, + bool approved + ) internal virtual { + require(owner != operator, "ERC721: approve to caller"); + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. + * The call is not executed if the target address is not a contract. + * + * @param from address representing the previous owner of the given token ID + * @param to target address that will receive the tokens + * @param tokenId uint256 ID of the token to be transferred + * @param _data bytes optional data to send along with the call + * @return bool whether the call correctly returned the expected magic value + */ + function _checkOnERC721Received( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) private returns (bool) { + if (to.isContract()) { + try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { + return retval == IERC721Receiver.onERC721Received.selector; + } catch (bytes memory reason) { + if (reason.length == 0) { + revert("ERC721: transfer to non ERC721Receiver implementer"); + } else { + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } else { + return true; + } + } + + /** + * @dev Hook that is called before any token transfer. This includes minting + * and burning. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be + * transferred to `to`. + * - When `from` is zero, `tokenId` will be minted for `to`. + * - When `to` is zero, ``from``'s `tokenId` will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual {} + + /** + * @dev Hook that is called after any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual {} +} diff --git a/certora/munged/token/ERC721/IERC721.sol b/certora/munged/token/ERC721/IERC721.sol new file mode 100644 index 000000000..fc58b032b --- /dev/null +++ b/certora/munged/token/ERC721/IERC721.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol) + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * @dev Required interface of an ERC721 compliant contract. + */ +interface IERC721 is IERC165 { + /** + * @dev Emitted when `tokenId` token is transferred from `from` to `to`. + */ + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. + */ + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. + */ + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /** + * @dev Returns the number of tokens in ``owner``'s account. + */ + function balanceOf(address owner) external view returns (uint256 balance); + + /** + * @dev Returns the owner of the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function ownerOf(uint256 tokenId) external view returns (address owner); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /** + * @dev Transfers `tokenId` token from `from` to `to`. + * + * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. + * The approval is cleared when the token is transferred. + * + * Only a single account can be approved at a time, so approving the zero address clears previous approvals. + * + * Requirements: + * + * - The caller must own the token or be an approved operator. + * - `tokenId` must exist. + * + * Emits an {Approval} event. + */ + function approve(address to, uint256 tokenId) external; + + /** + * @dev Returns the account approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getApproved(uint256 tokenId) external view returns (address operator); + + /** + * @dev Approve or remove `operator` as an operator for the caller. + * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the caller. + * + * Emits an {ApprovalForAll} event. + */ + function setApprovalForAll(address operator, bool _approved) external; + + /** + * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. + * + * See {setApprovalForAll} + */ + function isApprovedForAll(address owner, address operator) external view returns (bool); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes calldata data + ) external; +} diff --git a/certora/munged/token/ERC721/IERC721Receiver.sol b/certora/munged/token/ERC721/IERC721Receiver.sol new file mode 100644 index 000000000..298e3565f --- /dev/null +++ b/certora/munged/token/ERC721/IERC721Receiver.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol) + +pragma solidity ^0.8.0; + +/** + * @title ERC721 token receiver interface + * @dev Interface for any contract that wants to support safeTransfers + * from ERC721 asset contracts. + */ +interface IERC721Receiver { + /** + * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} + * by `operator` from `from`, this function is called. + * + * It must return its Solidity selector to confirm the token transfer. + * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. + * + * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. + */ + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external returns (bytes4); +} diff --git a/certora/munged/token/ERC721/README.adoc b/certora/munged/token/ERC721/README.adoc new file mode 100644 index 000000000..92959576b --- /dev/null +++ b/certora/munged/token/ERC721/README.adoc @@ -0,0 +1,67 @@ += ERC 721 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc721 + +This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-721[ERC721 Non-Fungible Token Standard]. + +TIP: For a walk through on how to create an ERC721 token read our xref:ROOT:erc721.adoc[ERC721 guide]. + +The EIP specifies four interfaces: + +* {IERC721}: Core functionality required in all compliant implementation. +* {IERC721Metadata}: Optional extension that adds name, symbol, and token URI, almost always included. +* {IERC721Enumerable}: Optional extension that allows enumerating the tokens on chain, often not included since it requires large gas overhead. +* {IERC721Receiver}: An interface that must be implemented by contracts if they want to accept tokens through `safeTransferFrom`. + +OpenZeppelin Contracts provides implementations of all four interfaces: + +* {ERC721}: The core and metadata extensions, with a base URI mechanism. +* {ERC721Enumerable}: The enumerable extension. +* {ERC721Holder}: A bare bones implementation of the receiver interface. + +Additionally there are a few of other extensions: + +* {ERC721URIStorage}: A more flexible but more expensive way of storing metadata. +* {ERC721Votes}: Support for voting and vote delegation. +* {ERC721Royalty}: A way to signal royalty information following ERC2981. +* {ERC721Pausable}: A primitive to pause contract operation. +* {ERC721Burnable}: A way for token holders to burn their own tokens. + +NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC721 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc721.adoc#Presets[ERC721 Presets] (such as {ERC721PresetMinterPauserAutoId}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. + +== Core + +{{IERC721}} + +{{IERC721Metadata}} + +{{IERC721Enumerable}} + +{{ERC721}} + +{{ERC721Enumerable}} + +{{IERC721Receiver}} + +== Extensions + +{{ERC721Pausable}} + +{{ERC721Burnable}} + +{{ERC721URIStorage}} + +{{ERC721Votes}} + +{{ERC721Royalty}} + +== Presets + +These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC721PresetMinterPauserAutoId}} + +== Utilities + +{{ERC721Holder}} diff --git a/certora/munged/token/ERC721/extensions/ERC721Burnable.sol b/certora/munged/token/ERC721/extensions/ERC721Burnable.sol new file mode 100644 index 000000000..063997ddf --- /dev/null +++ b/certora/munged/token/ERC721/extensions/ERC721Burnable.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../../utils/Context.sol"; + +/** + * @title ERC721 Burnable Token + * @dev ERC721 Token that can be irreversibly burned (destroyed). + */ +abstract contract ERC721Burnable is Context, ERC721 { + /** + * @dev Burns `tokenId`. See {ERC721-_burn}. + * + * Requirements: + * + * - The caller must own `tokenId` or be an approved operator. + */ + function burn(uint256 tokenId) public virtual { + //solhint-disable-next-line max-line-length + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Burnable: caller is not owner nor approved"); + _burn(tokenId); + } +} diff --git a/certora/munged/token/ERC721/extensions/ERC721Enumerable.sol b/certora/munged/token/ERC721/extensions/ERC721Enumerable.sol new file mode 100644 index 000000000..46afd5d0b --- /dev/null +++ b/certora/munged/token/ERC721/extensions/ERC721Enumerable.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "./IERC721Enumerable.sol"; + +/** + * @dev This implements an optional extension of {ERC721} defined in the EIP that adds + * enumerability of all the token ids in the contract as well as all token ids owned by each + * account. + */ +abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { + // Mapping from owner to list of owned token IDs + mapping(address => mapping(uint256 => uint256)) private _ownedTokens; + + // Mapping from token ID to index of the owner tokens list + mapping(uint256 => uint256) private _ownedTokensIndex; + + // Array with all token ids, used for enumeration + uint256[] private _allTokens; + + // Mapping from token id to position in the allTokens array + mapping(uint256 => uint256) private _allTokensIndex; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { + return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { + require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds"); + return _ownedTokens[owner][index]; + } + + /** + * @dev See {IERC721Enumerable-totalSupply}. + */ + function totalSupply() public view virtual override returns (uint256) { + return _allTokens.length; + } + + /** + * @dev See {IERC721Enumerable-tokenByIndex}. + */ + function tokenByIndex(uint256 index) public view virtual override returns (uint256) { + require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds"); + return _allTokens[index]; + } + + /** + * @dev Hook that is called before any token transfer. This includes minting + * and burning. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be + * transferred to `to`. + * - When `from` is zero, `tokenId` will be minted for `to`. + * - When `to` is zero, ``from``'s `tokenId` will be burned. + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual override { + super._beforeTokenTransfer(from, to, tokenId); + + if (from == address(0)) { + _addTokenToAllTokensEnumeration(tokenId); + } else if (from != to) { + _removeTokenFromOwnerEnumeration(from, tokenId); + } + if (to == address(0)) { + _removeTokenFromAllTokensEnumeration(tokenId); + } else if (to != from) { + _addTokenToOwnerEnumeration(to, tokenId); + } + } + + /** + * @dev Private function to add a token to this extension's ownership-tracking data structures. + * @param to address representing the new owner of the given token ID + * @param tokenId uint256 ID of the token to be added to the tokens list of the given address + */ + function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { + uint256 length = ERC721.balanceOf(to); + _ownedTokens[to][length] = tokenId; + _ownedTokensIndex[tokenId] = length; + } + + /** + * @dev Private function to add a token to this extension's token tracking data structures. + * @param tokenId uint256 ID of the token to be added to the tokens list + */ + function _addTokenToAllTokensEnumeration(uint256 tokenId) private { + _allTokensIndex[tokenId] = _allTokens.length; + _allTokens.push(tokenId); + } + + /** + * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that + * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for + * gas optimizations e.g. when performing a transfer operation (avoiding double writes). + * This has O(1) time complexity, but alters the order of the _ownedTokens array. + * @param from address representing the previous owner of the given token ID + * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address + */ + function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { + // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = ERC721.balanceOf(from) - 1; + uint256 tokenIndex = _ownedTokensIndex[tokenId]; + + // When the token to delete is the last token, the swap operation is unnecessary + if (tokenIndex != lastTokenIndex) { + uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; + + _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + } + + // This also deletes the contents at the last position of the array + delete _ownedTokensIndex[tokenId]; + delete _ownedTokens[from][lastTokenIndex]; + } + + /** + * @dev Private function to remove a token from this extension's token tracking data structures. + * This has O(1) time complexity, but alters the order of the _allTokens array. + * @param tokenId uint256 ID of the token to be removed from the tokens list + */ + function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { + // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = _allTokens.length - 1; + uint256 tokenIndex = _allTokensIndex[tokenId]; + + // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so + // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding + // an 'if' statement (like in _removeTokenFromOwnerEnumeration) + uint256 lastTokenId = _allTokens[lastTokenIndex]; + + _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + + // This also deletes the contents at the last position of the array + delete _allTokensIndex[tokenId]; + _allTokens.pop(); + } +} diff --git a/certora/munged/token/ERC721/extensions/ERC721Pausable.sol b/certora/munged/token/ERC721/extensions/ERC721Pausable.sol new file mode 100644 index 000000000..fbf8b6382 --- /dev/null +++ b/certora/munged/token/ERC721/extensions/ERC721Pausable.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Pausable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../../security/Pausable.sol"; + +/** + * @dev ERC721 token with pausable token transfers, minting and burning. + * + * Useful for scenarios such as preventing trades until the end of an evaluation + * period, or having an emergency switch for freezing all token transfers in the + * event of a large bug. + */ +abstract contract ERC721Pausable is ERC721, Pausable { + /** + * @dev See {ERC721-_beforeTokenTransfer}. + * + * Requirements: + * + * - the contract must not be paused. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual override { + super._beforeTokenTransfer(from, to, tokenId); + + require(!paused(), "ERC721Pausable: token transfer while paused"); + } +} diff --git a/certora/munged/token/ERC721/extensions/ERC721Royalty.sol b/certora/munged/token/ERC721/extensions/ERC721Royalty.sol new file mode 100644 index 000000000..f9414da05 --- /dev/null +++ b/certora/munged/token/ERC721/extensions/ERC721Royalty.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/ERC721Royalty.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../common/ERC2981.sol"; +import "../../../utils/introspection/ERC165.sol"; + +/** + * @dev Extension of ERC721 with the ERC2981 NFT Royalty Standard, a standardized way to retrieve royalty payment + * information. + * + * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for + * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first. + * + * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See + * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to + * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported. + * + * _Available since v4.5._ + */ +abstract contract ERC721Royalty is ERC2981, ERC721 { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC2981) returns (bool) { + return super.supportsInterface(interfaceId); + } + + /** + * @dev See {ERC721-_burn}. This override additionally clears the royalty information for the token. + */ + function _burn(uint256 tokenId) internal virtual override { + super._burn(tokenId); + _resetTokenRoyalty(tokenId); + } +} diff --git a/certora/munged/token/ERC721/extensions/ERC721URIStorage.sol b/certora/munged/token/ERC721/extensions/ERC721URIStorage.sol new file mode 100644 index 000000000..bc0e07e7f --- /dev/null +++ b/certora/munged/token/ERC721/extensions/ERC721URIStorage.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721URIStorage.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; + +/** + * @dev ERC721 token with storage based token URI management. + */ +abstract contract ERC721URIStorage is ERC721 { + using Strings for uint256; + + // Optional mapping for token URIs + mapping(uint256 => string) private _tokenURIs; + + /** + * @dev See {IERC721Metadata-tokenURI}. + */ + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + require(_exists(tokenId), "ERC721URIStorage: URI query for nonexistent token"); + + string memory _tokenURI = _tokenURIs[tokenId]; + string memory base = _baseURI(); + + // If there is no base URI, return the token URI. + if (bytes(base).length == 0) { + return _tokenURI; + } + // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). + if (bytes(_tokenURI).length > 0) { + return string(abi.encodePacked(base, _tokenURI)); + } + + return super.tokenURI(tokenId); + } + + /** + * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { + require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); + _tokenURIs[tokenId] = _tokenURI; + } + + /** + * @dev Destroys `tokenId`. + * The approval is cleared when the token is burned. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Transfer} event. + */ + function _burn(uint256 tokenId) internal virtual override { + super._burn(tokenId); + + if (bytes(_tokenURIs[tokenId]).length != 0) { + delete _tokenURIs[tokenId]; + } + } +} diff --git a/certora/munged/token/ERC721/extensions/IERC721Enumerable.sol b/certora/munged/token/ERC721/extensions/IERC721Enumerable.sol new file mode 100644 index 000000000..dfea427ba --- /dev/null +++ b/certora/munged/token/ERC721/extensions/IERC721Enumerable.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../IERC721.sol"; + +/** + * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Enumerable is IERC721 { + /** + * @dev Returns the total amount of tokens stored by the contract. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns a token ID owned by `owner` at a given `index` of its token list. + * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + /** + * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. + * Use along with {totalSupply} to enumerate all tokens. + */ + function tokenByIndex(uint256 index) external view returns (uint256); +} diff --git a/certora/munged/token/ERC721/extensions/IERC721Metadata.sol b/certora/munged/token/ERC721/extensions/IERC721Metadata.sol new file mode 100644 index 000000000..dca77ba5b --- /dev/null +++ b/certora/munged/token/ERC721/extensions/IERC721Metadata.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) + +pragma solidity ^0.8.0; + +import "../IERC721.sol"; + +/** + * @title ERC-721 Non-Fungible Token Standard, optional metadata extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Metadata is IERC721 { + /** + * @dev Returns the token collection name. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the token collection symbol. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. + */ + function tokenURI(uint256 tokenId) external view returns (string memory); +} diff --git a/certora/munged/token/ERC721/extensions/draft-ERC721Votes.sol b/certora/munged/token/ERC721/extensions/draft-ERC721Votes.sol new file mode 100644 index 000000000..7d23c4921 --- /dev/null +++ b/certora/munged/token/ERC721/extensions/draft-ERC721Votes.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/draft-ERC721Votes.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../../governance/utils/Votes.sol"; + +/** + * @dev Extension of ERC721 to support voting and delegation as implemented by {Votes}, where each individual NFT counts + * as 1 vote unit. + * + * Tokens do not count as votes until they are delegated, because votes must be tracked which incurs an additional cost + * on every transfer. Token holders can either delegate to a trusted representative who will decide how to make use of + * the votes in governance decisions, or they can delegate to themselves to be their own representative. + * + * _Available since v4.5._ + */ +abstract contract ERC721Votes is ERC721, Votes { + /** + * @dev Adjusts votes when tokens are transferred. + * + * Emits a {Votes-DelegateVotesChanged} event. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual override { + _transferVotingUnits(from, to, 1); + super._afterTokenTransfer(from, to, tokenId); + } + + /** + * @dev Returns the balance of `account`. + */ + function _getVotingUnits(address account) internal virtual override returns (uint256) { + return balanceOf(account); + } +} diff --git a/certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol b/certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol new file mode 100644 index 000000000..11b978780 --- /dev/null +++ b/certora/munged/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../extensions/ERC721Enumerable.sol"; +import "../extensions/ERC721Burnable.sol"; +import "../extensions/ERC721Pausable.sol"; +import "../../../access/AccessControlEnumerable.sol"; +import "../../../utils/Context.sol"; +import "../../../utils/Counters.sol"; + +/** + * @dev {ERC721} token, including: + * + * - ability for holders to burn (destroy) their tokens + * - a minter role that allows for token minting (creation) + * - a pauser role that allows to stop all token transfers + * - token ID and URI autogeneration + * + * This contract uses {AccessControl} to lock permissioned functions using the + * different roles - head to its documentation for details. + * + * The account that deploys the contract will be granted the minter and pauser + * roles, as well as the default admin role, which will let it grant both minter + * and pauser roles to other accounts. + * + * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._ + */ +contract ERC721PresetMinterPauserAutoId is + Context, + AccessControlEnumerable, + ERC721Enumerable, + ERC721Burnable, + ERC721Pausable +{ + using Counters for Counters.Counter; + + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + Counters.Counter private _tokenIdTracker; + + string private _baseTokenURI; + + /** + * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the + * account that deploys the contract. + * + * Token URIs will be autogenerated based on `baseURI` and their token IDs. + * See {ERC721-tokenURI}. + */ + constructor( + string memory name, + string memory symbol, + string memory baseTokenURI + ) ERC721(name, symbol) { + _baseTokenURI = baseTokenURI; + + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + + _setupRole(MINTER_ROLE, _msgSender()); + _setupRole(PAUSER_ROLE, _msgSender()); + } + + function _baseURI() internal view virtual override returns (string memory) { + return _baseTokenURI; + } + + /** + * @dev Creates a new token for `to`. Its token ID will be automatically + * assigned (and available on the emitted {IERC721-Transfer} event), and the token + * URI autogenerated based on the base URI passed at construction. + * + * See {ERC721-_mint}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function mint(address to) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have minter role to mint"); + + // We cannot just use balanceOf to create the new tokenId because tokens + // can be burned (destroyed), so we need a separate counter. + _mint(to, _tokenIdTracker.current()); + _tokenIdTracker.increment(); + } + + /** + * @dev Pauses all token transfers. + * + * See {ERC721Pausable} and {Pausable-_pause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function pause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to pause"); + _pause(); + } + + /** + * @dev Unpauses all token transfers. + * + * See {ERC721Pausable} and {Pausable-_unpause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function unpause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to unpause"); + _unpause(); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual override(ERC721, ERC721Enumerable, ERC721Pausable) { + super._beforeTokenTransfer(from, to, tokenId); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(AccessControlEnumerable, ERC721, ERC721Enumerable) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/certora/munged/token/ERC721/presets/README.md b/certora/munged/token/ERC721/presets/README.md new file mode 100644 index 000000000..468200b72 --- /dev/null +++ b/certora/munged/token/ERC721/presets/README.md @@ -0,0 +1 @@ +Contract presets are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com/) as a more powerful alternative. diff --git a/certora/munged/token/ERC721/utils/ERC721Holder.sol b/certora/munged/token/ERC721/utils/ERC721Holder.sol new file mode 100644 index 000000000..394926d51 --- /dev/null +++ b/certora/munged/token/ERC721/utils/ERC721Holder.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol) + +pragma solidity ^0.8.0; + +import "../IERC721Receiver.sol"; + +/** + * @dev Implementation of the {IERC721Receiver} interface. + * + * Accepts all token transfers. + * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. + */ +contract ERC721Holder is IERC721Receiver { + /** + * @dev See {IERC721Receiver-onERC721Received}. + * + * Always returns `IERC721Receiver.onERC721Received.selector`. + */ + function onERC721Received( + address, + address, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC721Received.selector; + } +} diff --git a/certora/munged/token/ERC777/ERC777.sol b/certora/munged/token/ERC777/ERC777.sol new file mode 100644 index 000000000..e459d041d --- /dev/null +++ b/certora/munged/token/ERC777/ERC777.sol @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC777/ERC777.sol) + +pragma solidity ^0.8.0; + +import "./IERC777.sol"; +import "./IERC777Recipient.sol"; +import "./IERC777Sender.sol"; +import "../ERC20/IERC20.sol"; +import "../../utils/Address.sol"; +import "../../utils/Context.sol"; +import "../../utils/introspection/IERC1820Registry.sol"; + +/** + * @dev Implementation of the {IERC777} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * + * Support for ERC20 is included in this contract, as specified by the EIP: both + * the ERC777 and ERC20 interfaces can be safely used when interacting with it. + * Both {IERC777-Sent} and {IERC20-Transfer} events are emitted on token + * movements. + * + * Additionally, the {IERC777-granularity} value is hard-coded to `1`, meaning that there + * are no special restrictions in the amount of tokens that created, moved, or + * destroyed. This makes integration with ERC20 applications seamless. + */ +contract ERC777 is Context, IERC777, IERC20 { + using Address for address; + + IERC1820Registry internal constant _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + + mapping(address => uint256) private _balances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); + bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + + // This isn't ever read from - it's only used to respond to the defaultOperators query. + address[] private _defaultOperatorsArray; + + // Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators). + mapping(address => bool) private _defaultOperators; + + // For each account, a mapping of its operators and revoked default operators. + mapping(address => mapping(address => bool)) private _operators; + mapping(address => mapping(address => bool)) private _revokedDefaultOperators; + + // ERC20-allowances + mapping(address => mapping(address => uint256)) private _allowances; + + /** + * @dev `defaultOperators` may be an empty array. + */ + constructor( + string memory name_, + string memory symbol_, + address[] memory defaultOperators_ + ) { + _name = name_; + _symbol = symbol_; + + _defaultOperatorsArray = defaultOperators_; + for (uint256 i = 0; i < defaultOperators_.length; i++) { + _defaultOperators[defaultOperators_[i]] = true; + } + + // register interfaces + _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this)); + _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this)); + } + + /** + * @dev See {IERC777-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IERC777-symbol}. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev See {ERC20-decimals}. + * + * Always returns 18, as per the + * [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility). + */ + function decimals() public pure virtual returns (uint8) { + return 18; + } + + /** + * @dev See {IERC777-granularity}. + * + * This implementation always returns `1`. + */ + function granularity() public view virtual override returns (uint256) { + return 1; + } + + /** + * @dev See {IERC777-totalSupply}. + */ + function totalSupply() public view virtual override(IERC20, IERC777) returns (uint256) { + return _totalSupply; + } + + /** + * @dev Returns the amount of tokens owned by an account (`tokenHolder`). + */ + function balanceOf(address tokenHolder) public view virtual override(IERC20, IERC777) returns (uint256) { + return _balances[tokenHolder]; + } + + /** + * @dev See {IERC777-send}. + * + * Also emits a {IERC20-Transfer} event for ERC20 compatibility. + */ + function send( + address recipient, + uint256 amount, + bytes memory data + ) public virtual override { + _send(_msgSender(), recipient, amount, data, "", true); + } + + /** + * @dev See {IERC20-transfer}. + * + * Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient} + * interface if it is a contract. + * + * Also emits a {Sent} event. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _send(_msgSender(), recipient, amount, "", "", false); + return true; + } + + /** + * @dev See {IERC777-burn}. + * + * Also emits a {IERC20-Transfer} event for ERC20 compatibility. + */ + function burn(uint256 amount, bytes memory data) public virtual override { + _burn(_msgSender(), amount, data, ""); + } + + /** + * @dev See {IERC777-isOperatorFor}. + */ + function isOperatorFor(address operator, address tokenHolder) public view virtual override returns (bool) { + return + operator == tokenHolder || + (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) || + _operators[tokenHolder][operator]; + } + + /** + * @dev See {IERC777-authorizeOperator}. + */ + function authorizeOperator(address operator) public virtual override { + require(_msgSender() != operator, "ERC777: authorizing self as operator"); + + if (_defaultOperators[operator]) { + delete _revokedDefaultOperators[_msgSender()][operator]; + } else { + _operators[_msgSender()][operator] = true; + } + + emit AuthorizedOperator(operator, _msgSender()); + } + + /** + * @dev See {IERC777-revokeOperator}. + */ + function revokeOperator(address operator) public virtual override { + require(operator != _msgSender(), "ERC777: revoking self as operator"); + + if (_defaultOperators[operator]) { + _revokedDefaultOperators[_msgSender()][operator] = true; + } else { + delete _operators[_msgSender()][operator]; + } + + emit RevokedOperator(operator, _msgSender()); + } + + /** + * @dev See {IERC777-defaultOperators}. + */ + function defaultOperators() public view virtual override returns (address[] memory) { + return _defaultOperatorsArray; + } + + /** + * @dev See {IERC777-operatorSend}. + * + * Emits {Sent} and {IERC20-Transfer} events. + */ + function operatorSend( + address sender, + address recipient, + uint256 amount, + bytes memory data, + bytes memory operatorData + ) public virtual override { + require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder"); + _send(sender, recipient, amount, data, operatorData, true); + } + + /** + * @dev See {IERC777-operatorBurn}. + * + * Emits {Burned} and {IERC20-Transfer} events. + */ + function operatorBurn( + address account, + uint256 amount, + bytes memory data, + bytes memory operatorData + ) public virtual override { + require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder"); + _burn(account, amount, data, operatorData); + } + + /** + * @dev See {IERC20-allowance}. + * + * Note that operator and allowance concepts are orthogonal: operators may + * not have allowance, and accounts with allowance may not be operators + * themselves. + */ + function allowance(address holder, address spender) public view virtual override returns (uint256) { + return _allowances[holder][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Note that accounts cannot have allowance issued by their operators. + */ + function approve(address spender, uint256 value) public virtual override returns (bool) { + address holder = _msgSender(); + _approve(holder, spender, value); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Note that operator and allowance concepts are orthogonal: operators cannot + * call `transferFrom` (unless they have allowance), and accounts with + * allowance cannot call `operatorSend` (unless they are operators). + * + * Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events. + */ + function transferFrom( + address holder, + address recipient, + uint256 amount + ) public virtual override returns (bool) { + address spender = _msgSender(); + _spendAllowance(holder, spender, amount); + _send(holder, recipient, amount, "", "", false); + return true; + } + + /** + * @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * If a send hook is registered for `account`, the corresponding function + * will be called with `operator`, `data` and `operatorData`. + * + * See {IERC777Sender} and {IERC777Recipient}. + * + * Emits {Minted} and {IERC20-Transfer} events. + * + * Requirements + * + * - `account` cannot be the zero address. + * - if `account` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function _mint( + address account, + uint256 amount, + bytes memory userData, + bytes memory operatorData + ) internal virtual { + _mint(account, amount, userData, operatorData, true); + } + + /** + * @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * If `requireReceptionAck` is set to true, and if a send hook is + * registered for `account`, the corresponding function will be called with + * `operator`, `data` and `operatorData`. + * + * See {IERC777Sender} and {IERC777Recipient}. + * + * Emits {Minted} and {IERC20-Transfer} events. + * + * Requirements + * + * - `account` cannot be the zero address. + * - if `account` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function _mint( + address account, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) internal virtual { + require(account != address(0), "ERC777: mint to the zero address"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, address(0), account, amount); + + // Update state variables + _totalSupply += amount; + _balances[account] += amount; + + _callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck); + + emit Minted(operator, account, amount, userData, operatorData); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Send tokens + * @param from address token holder address + * @param to address recipient address + * @param amount uint256 amount of tokens to transfer + * @param userData bytes extra information provided by the token holder (if any) + * @param operatorData bytes extra information provided by the operator (if any) + * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient + */ + function _send( + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) internal virtual { + require(from != address(0), "ERC777: transfer from the zero address"); + require(to != address(0), "ERC777: transfer to the zero address"); + + address operator = _msgSender(); + + _callTokensToSend(operator, from, to, amount, userData, operatorData); + + _move(operator, from, to, amount, userData, operatorData); + + _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck); + } + + /** + * @dev Burn tokens + * @param from address token holder address + * @param amount uint256 amount of tokens to burn + * @param data bytes extra information provided by the token holder + * @param operatorData bytes extra information provided by the operator (if any) + */ + function _burn( + address from, + uint256 amount, + bytes memory data, + bytes memory operatorData + ) internal virtual { + require(from != address(0), "ERC777: burn from the zero address"); + + address operator = _msgSender(); + + _callTokensToSend(operator, from, address(0), amount, data, operatorData); + + _beforeTokenTransfer(operator, from, address(0), amount); + + // Update state variables + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC777: burn amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + } + _totalSupply -= amount; + + emit Burned(operator, from, amount, data, operatorData); + emit Transfer(from, address(0), amount); + } + + function _move( + address operator, + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData + ) private { + _beforeTokenTransfer(operator, from, to, amount); + + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC777: transfer amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + } + _balances[to] += amount; + + emit Sent(operator, from, to, amount, userData, operatorData); + emit Transfer(from, to, amount); + } + + /** + * @dev See {ERC20-_approve}. + * + * Note that accounts cannot have allowance issued by their operators. + */ + function _approve( + address holder, + address spender, + uint256 value + ) internal virtual { + require(holder != address(0), "ERC777: approve from the zero address"); + require(spender != address(0), "ERC777: approve to the zero address"); + + _allowances[holder][spender] = value; + emit Approval(holder, spender, value); + } + + /** + * @dev Call from.tokensToSend() if the interface is registered + * @param operator address operator requesting the transfer + * @param from address token holder address + * @param to address recipient address + * @param amount uint256 amount of tokens to transfer + * @param userData bytes extra information provided by the token holder (if any) + * @param operatorData bytes extra information provided by the operator (if any) + */ + function _callTokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData + ) private { + address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH); + if (implementer != address(0)) { + IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData); + } + } + + /** + * @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but + * tokensReceived() was not registered for the recipient + * @param operator address operator requesting the transfer + * @param from address token holder address + * @param to address recipient address + * @param amount uint256 amount of tokens to transfer + * @param userData bytes extra information provided by the token holder (if any) + * @param operatorData bytes extra information provided by the operator (if any) + * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient + */ + function _callTokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) private { + address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH); + if (implementer != address(0)) { + IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData); + } else if (requireReceptionAck) { + require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient"); + } + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `amount`. + * + * Does not update the allowance amount in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Might emit an {Approval} event. + */ + function _spendAllowance( + address owner, + address spender, + uint256 amount + ) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + require(currentAllowance >= amount, "ERC777: insufficient allowance"); + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } + + /** + * @dev Hook that is called before any token transfer. This includes + * calls to {send}, {transfer}, {operatorSend}, minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be to transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256 amount + ) internal virtual {} +} diff --git a/certora/munged/token/ERC777/IERC777.sol b/certora/munged/token/ERC777/IERC777.sol new file mode 100644 index 000000000..5a729176e --- /dev/null +++ b/certora/munged/token/ERC777/IERC777.sol @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC777Token standard as defined in the EIP. + * + * This contract uses the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let + * token holders and recipients react to token movements by using setting implementers + * for the associated interfaces in said registry. See {IERC1820Registry} and + * {ERC1820Implementer}. + */ +interface IERC777 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the smallest part of the token that is not divisible. This + * means all token operations (creation, movement and destruction) must have + * amounts that are a multiple of this number. + * + * For most token contracts, this value will equal 1. + */ + function granularity() external view returns (uint256); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by an account (`owner`). + */ + function balanceOf(address owner) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * If send or receive hooks are registered for the caller and `recipient`, + * the corresponding functions will be called with `data` and empty + * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. + * + * Emits a {Sent} event. + * + * Requirements + * + * - the caller must have at least `amount` tokens. + * - `recipient` cannot be the zero address. + * - if `recipient` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function send( + address recipient, + uint256 amount, + bytes calldata data + ) external; + + /** + * @dev Destroys `amount` tokens from the caller's account, reducing the + * total supply. + * + * If a send hook is registered for the caller, the corresponding function + * will be called with `data` and empty `operatorData`. See {IERC777Sender}. + * + * Emits a {Burned} event. + * + * Requirements + * + * - the caller must have at least `amount` tokens. + */ + function burn(uint256 amount, bytes calldata data) external; + + /** + * @dev Returns true if an account is an operator of `tokenHolder`. + * Operators can send and burn tokens on behalf of their owners. All + * accounts are their own operator. + * + * See {operatorSend} and {operatorBurn}. + */ + function isOperatorFor(address operator, address tokenHolder) external view returns (bool); + + /** + * @dev Make an account an operator of the caller. + * + * See {isOperatorFor}. + * + * Emits an {AuthorizedOperator} event. + * + * Requirements + * + * - `operator` cannot be calling address. + */ + function authorizeOperator(address operator) external; + + /** + * @dev Revoke an account's operator status for the caller. + * + * See {isOperatorFor} and {defaultOperators}. + * + * Emits a {RevokedOperator} event. + * + * Requirements + * + * - `operator` cannot be calling address. + */ + function revokeOperator(address operator) external; + + /** + * @dev Returns the list of default operators. These accounts are operators + * for all token holders, even if {authorizeOperator} was never called on + * them. + * + * This list is immutable, but individual holders may revoke these via + * {revokeOperator}, in which case {isOperatorFor} will return false. + */ + function defaultOperators() external view returns (address[] memory); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must + * be an operator of `sender`. + * + * If send or receive hooks are registered for `sender` and `recipient`, + * the corresponding functions will be called with `data` and + * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. + * + * Emits a {Sent} event. + * + * Requirements + * + * - `sender` cannot be the zero address. + * - `sender` must have at least `amount` tokens. + * - the caller must be an operator for `sender`. + * - `recipient` cannot be the zero address. + * - if `recipient` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function operatorSend( + address sender, + address recipient, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external; + + /** + * @dev Destroys `amount` tokens from `account`, reducing the total supply. + * The caller must be an operator of `account`. + * + * If a send hook is registered for `account`, the corresponding function + * will be called with `data` and `operatorData`. See {IERC777Sender}. + * + * Emits a {Burned} event. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + * - the caller must be an operator for `account`. + */ + function operatorBurn( + address account, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external; + + event Sent( + address indexed operator, + address indexed from, + address indexed to, + uint256 amount, + bytes data, + bytes operatorData + ); + + event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); + + event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); + + event AuthorizedOperator(address indexed operator, address indexed tokenHolder); + + event RevokedOperator(address indexed operator, address indexed tokenHolder); +} diff --git a/certora/munged/token/ERC777/IERC777Recipient.sol b/certora/munged/token/ERC777/IERC777Recipient.sol new file mode 100644 index 000000000..717dd8f8c --- /dev/null +++ b/certora/munged/token/ERC777/IERC777Recipient.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Recipient.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. + * + * Accounts can be notified of {IERC777} tokens being sent to them by having a + * contract implement this interface (contract holders can be their own + * implementer) and registering it on the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. + * + * See {IERC1820Registry} and {ERC1820Implementer}. + */ +interface IERC777Recipient { + /** + * @dev Called by an {IERC777} token contract whenever tokens are being + * moved or created into a registered account (`to`). The type of operation + * is conveyed by `from` being the zero address or not. + * + * This call occurs _after_ the token contract's state is updated, so + * {IERC777-balanceOf}, etc., can be used to query the post-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} diff --git a/certora/munged/token/ERC777/IERC777Sender.sol b/certora/munged/token/ERC777/IERC777Sender.sol new file mode 100644 index 000000000..969e3e367 --- /dev/null +++ b/certora/munged/token/ERC777/IERC777Sender.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Sender.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC777TokensSender standard as defined in the EIP. + * + * {IERC777} Token holders can be notified of operations performed on their + * tokens by having a contract implement this interface (contract holders can be + * their own implementer) and registering it on the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. + * + * See {IERC1820Registry} and {ERC1820Implementer}. + */ +interface IERC777Sender { + /** + * @dev Called by an {IERC777} token contract whenever a registered holder's + * (`from`) tokens are about to be moved or destroyed. The type of operation + * is conveyed by `to` being the zero address or not. + * + * This call occurs _before_ the token contract's state is updated, so + * {IERC777-balanceOf}, etc., can be used to query the pre-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} diff --git a/certora/munged/token/ERC777/README.adoc b/certora/munged/token/ERC777/README.adoc new file mode 100644 index 000000000..d8f25f060 --- /dev/null +++ b/certora/munged/token/ERC777/README.adoc @@ -0,0 +1,30 @@ += ERC 777 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc777 + +This set of interfaces and contracts are all related to the [ERC777 token standard](https://eips.ethereum.org/EIPS/eip-777). + +TIP: For an overview of ERC777 tokens and a walk through on how to create a token contract read our xref:ROOT:erc777.adoc[ERC777 guide]. + +The token behavior itself is implemented in the core contracts: {IERC777}, {ERC777}. + +Additionally there are interfaces used to develop contracts that react to token movements: {IERC777Sender}, {IERC777Recipient}. + +== Core + +{{IERC777}} + +{{ERC777}} + +== Hooks + +{{IERC777Sender}} + +{{IERC777Recipient}} + +== Presets + +These contracts are preconfigured combinations of features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC777PresetFixedSupply}} diff --git a/certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol b/certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol new file mode 100644 index 000000000..8bd4b79aa --- /dev/null +++ b/certora/munged/token/ERC777/presets/ERC777PresetFixedSupply.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC777/presets/ERC777PresetFixedSupply.sol) +pragma solidity ^0.8.0; + +import "../ERC777.sol"; + +/** + * @dev {ERC777} token, including: + * + * - Preminted initial supply + * - No access control mechanism (for minting/pausing) and hence no governance + * + * _Available since v3.4._ + */ +contract ERC777PresetFixedSupply is ERC777 { + /** + * @dev Mints `initialSupply` amount of token and transfers them to `owner`. + * + * See {ERC777-constructor}. + */ + constructor( + string memory name, + string memory symbol, + address[] memory defaultOperators, + uint256 initialSupply, + address owner + ) ERC777(name, symbol, defaultOperators) { + _mint(owner, initialSupply, "", ""); + } +} diff --git a/certora/munged/token/common/ERC2981.sol b/certora/munged/token/common/ERC2981.sol new file mode 100644 index 000000000..90d7ed34a --- /dev/null +++ b/certora/munged/token/common/ERC2981.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/common/ERC2981.sol) + +pragma solidity ^0.8.0; + +import "../../interfaces/IERC2981.sol"; +import "../../utils/introspection/ERC165.sol"; + +/** + * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information. + * + * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for + * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first. + * + * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the + * fee is specified in basis points by default. + * + * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See + * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to + * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported. + * + * _Available since v4.5._ + */ +abstract contract ERC2981 is IERC2981, ERC165 { + struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; + } + + RoyaltyInfo private _defaultRoyaltyInfo; + mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { + return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @inheritdoc IERC2981 + */ + function royaltyInfo(uint256 _tokenId, uint256 _salePrice) + external + view + virtual + override + returns (address, uint256) + { + RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId]; + + if (royalty.receiver == address(0)) { + royalty = _defaultRoyaltyInfo; + } + + uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator(); + + return (royalty.receiver, royaltyAmount); + } + + /** + * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a + * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an + * override. + */ + function _feeDenominator() internal pure virtual returns (uint96) { + return 10000; + } + + /** + * @dev Sets the royalty information that all ids in this contract will default to. + * + * Requirements: + * + * - `receiver` cannot be the zero address. + * - `feeNumerator` cannot be greater than the fee denominator. + */ + function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual { + require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice"); + require(receiver != address(0), "ERC2981: invalid receiver"); + + _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator); + } + + /** + * @dev Removes default royalty information. + */ + function _deleteDefaultRoyalty() internal virtual { + delete _defaultRoyaltyInfo; + } + + /** + * @dev Sets the royalty information for a specific token id, overriding the global default. + * + * Requirements: + * + * - `tokenId` must be already minted. + * - `receiver` cannot be the zero address. + * - `feeNumerator` cannot be greater than the fee denominator. + */ + function _setTokenRoyalty( + uint256 tokenId, + address receiver, + uint96 feeNumerator + ) internal virtual { + require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice"); + require(receiver != address(0), "ERC2981: Invalid parameters"); + + _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator); + } + + /** + * @dev Resets royalty information for the token id back to the global default. + */ + function _resetTokenRoyalty(uint256 tokenId) internal virtual { + delete _tokenRoyaltyInfo[tokenId]; + } +} diff --git a/certora/munged/token/common/README.adoc b/certora/munged/token/common/README.adoc new file mode 100644 index 000000000..af6167464 --- /dev/null +++ b/certora/munged/token/common/README.adoc @@ -0,0 +1,10 @@ += Common (Tokens) + +Functionality that is common to multiple token standards. + +* {ERC2981}: NFT Royalties compatible with both ERC721 and ERC1155. +** For ERC721 consider {ERC721Royalty} which clears the royalty information from storage on burn. + +== Contracts + +{{ERC2981}} diff --git a/certora/munged/utils/Address.sol b/certora/munged/utils/Address.sol new file mode 100644 index 000000000..daea7f31e --- /dev/null +++ b/certora/munged/utils/Address.sol @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) + +pragma solidity ^0.8.1; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + * + * [IMPORTANT] + * ==== + * You shouldn't rely on `isContract` to protect against flash loan attacks! + * + * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets + * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract + * constructor. + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize/address.code.length, which returns 0 + // for contracts in construction, since the code is only stored at the end + // of the constructor execution. + + return account.code.length > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + (bool success, ) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + (bool success, bytes memory returndata) = target.call{value: value}(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + (bool success, bytes memory returndata) = target.delegatecall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the + * revert reason using the provided one. + * + * _Available since v4.3._ + */ + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} diff --git a/certora/munged/utils/Arrays.sol b/certora/munged/utils/Arrays.sol new file mode 100644 index 000000000..0783614cd --- /dev/null +++ b/certora/munged/utils/Arrays.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Arrays.sol) + +pragma solidity ^0.8.0; + +import "./math/Math.sol"; + +/** + * @dev Collection of functions related to array types. + */ +library Arrays { + /** + * @dev Searches a sorted `array` and returns the first index that contains + * a value greater or equal to `element`. If no such index exists (i.e. all + * values in the array are strictly less than `element`), the array length is + * returned. Time complexity O(log n). + * + * `array` is expected to be sorted in ascending order, and to contain no + * repeated elements. + */ + function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) { + if (array.length == 0) { + return 0; + } + + uint256 low = 0; + uint256 high = array.length; + + while (low < high) { + uint256 mid = Math.average(low, high); + + // Note that mid will always be strictly less than high (i.e. it will be a valid array index) + // because Math.average rounds down (it does integer division with truncation). + if (array[mid] > element) { + high = mid; + } else { + low = mid + 1; + } + } + + // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound. + if (low > 0 && array[low - 1] == element) { + return low - 1; + } else { + return low; + } + } +} diff --git a/certora/munged/utils/Base64.sol b/certora/munged/utils/Base64.sol new file mode 100644 index 000000000..2de471d1d --- /dev/null +++ b/certora/munged/utils/Base64.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (utils/Base64.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Provides a set of functions to operate with Base64 strings. + * + * _Available since v4.5._ + */ +library Base64 { + /** + * @dev Base64 Encoding/Decoding Table + */ + string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + /** + * @dev Converts a `bytes` to its Bytes64 `string` representation. + */ + function encode(bytes memory data) internal pure returns (string memory) { + /** + * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence + * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol + */ + if (data.length == 0) return ""; + + // Loads the table into memory + string memory table = _TABLE; + + // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter + // and split into 4 numbers of 6 bits. + // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up + // - `data.length + 2` -> Round up + // - `/ 3` -> Number of 3-bytes chunks + // - `4 *` -> 4 characters for each chunk + string memory result = new string(4 * ((data.length + 2) / 3)); + + assembly { + // Prepare the lookup table (skip the first "length" byte) + let tablePtr := add(table, 1) + + // Prepare result pointer, jump over length + let resultPtr := add(result, 32) + + // Run over the input, 3 bytes at a time + for { + let dataPtr := data + let endPtr := add(data, mload(data)) + } lt(dataPtr, endPtr) { + + } { + // Advance 3 bytes + dataPtr := add(dataPtr, 3) + let input := mload(dataPtr) + + // To write each character, shift the 3 bytes (18 bits) chunk + // 4 times in blocks of 6 bits for each character (18, 12, 6, 0) + // and apply logical AND with 0x3F which is the number of + // the previous character in the ASCII table prior to the Base64 Table + // The result is then added to the table to get the character to write, + // and finally write it in the result pointer but with a left shift + // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits + + mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) + resultPtr := add(resultPtr, 1) // Advance + + mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) + resultPtr := add(resultPtr, 1) // Advance + + mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F)))) + resultPtr := add(resultPtr, 1) // Advance + + mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F)))) + resultPtr := add(resultPtr, 1) // Advance + } + + // When data `bytes` is not exactly 3 bytes long + // it is padded with `=` characters at the end + switch mod(mload(data), 3) + case 1 { + mstore8(sub(resultPtr, 1), 0x3d) + mstore8(sub(resultPtr, 2), 0x3d) + } + case 2 { + mstore8(sub(resultPtr, 1), 0x3d) + } + } + + return result; + } +} diff --git a/certora/munged/utils/Checkpoints.sol b/certora/munged/utils/Checkpoints.sol new file mode 100644 index 000000000..606098bcc --- /dev/null +++ b/certora/munged/utils/Checkpoints.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (utils/Checkpoints.sol) +pragma solidity ^0.8.0; + +import "./math/Math.sol"; +import "./math/SafeCast.sol"; + +/** + * @dev This library defines the `History` struct, for checkpointing values as they change at different points in + * time, and later looking up past values by block number. See {Votes} as an example. + * + * To create a history of checkpoints define a variable type `Checkpoints.History` in your contract, and store a new + * checkpoint for the current transaction block using the {push} function. + * + * _Available since v4.5._ + */ +library Checkpoints { + struct Checkpoint { + uint32 _blockNumber; + uint224 _value; + } + + struct History { + Checkpoint[] _checkpoints; + } + + /** + * @dev Returns the value in the latest checkpoint, or zero if there are no checkpoints. + */ + function latest(History storage self) internal view returns (uint256) { + uint256 pos = self._checkpoints.length; + return pos == 0 ? 0 : self._checkpoints[pos - 1]._value; + } + + /** + * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one + * before it is returned, or zero otherwise. + */ + function getAtBlock(History storage self, uint256 blockNumber) internal view returns (uint256) { + require(blockNumber < block.number, "Checkpoints: block not yet mined"); + + uint256 high = self._checkpoints.length; + uint256 low = 0; + while (low < high) { + uint256 mid = Math.average(low, high); + if (self._checkpoints[mid]._blockNumber > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + return high == 0 ? 0 : self._checkpoints[high - 1]._value; + } + + /** + * @dev Pushes a value onto a History so that it is stored as the checkpoint for the current block. + * + * Returns previous value and new value. + */ + function push(History storage self, uint256 value) internal returns (uint256, uint256) { + uint256 pos = self._checkpoints.length; + uint256 old = latest(self); + if (pos > 0 && self._checkpoints[pos - 1]._blockNumber == block.number) { + self._checkpoints[pos - 1]._value = SafeCast.toUint224(value); + } else { + self._checkpoints.push( + Checkpoint({_blockNumber: SafeCast.toUint32(block.number), _value: SafeCast.toUint224(value)}) + ); + } + return (old, value); + } + + /** + * @dev Pushes a value onto a History, by updating the latest value using binary operation `op`. The new value will + * be set to `op(latest, delta)`. + * + * Returns previous value and new value. + */ + function push( + History storage self, + function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) internal returns (uint256, uint256) { + return push(self, op(latest(self), delta)); + } +} diff --git a/certora/munged/utils/Context.sol b/certora/munged/utils/Context.sol new file mode 100644 index 000000000..f304065b4 --- /dev/null +++ b/certora/munged/utils/Context.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } +} diff --git a/certora/munged/utils/Counters.sol b/certora/munged/utils/Counters.sol new file mode 100644 index 000000000..8a4f2a2e7 --- /dev/null +++ b/certora/munged/utils/Counters.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) + +pragma solidity ^0.8.0; + +/** + * @title Counters + * @author Matt Condon (@shrugs) + * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number + * of elements in a mapping, issuing ERC721 ids, or counting request ids. + * + * Include with `using Counters for Counters.Counter;` + */ +library Counters { + struct Counter { + // This variable should never be directly accessed by users of the library: interactions must be restricted to + // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add + // this feature: see https://github.com/ethereum/solidity/issues/4637 + uint256 _value; // default: 0 + } + + function current(Counter storage counter) internal view returns (uint256) { + return counter._value; + } + + function increment(Counter storage counter) internal { + unchecked { + counter._value += 1; + } + } + + function decrement(Counter storage counter) internal { + uint256 value = counter._value; + require(value > 0, "Counter: decrement overflow"); + unchecked { + counter._value = value - 1; + } + } + + function reset(Counter storage counter) internal { + counter._value = 0; + } +} diff --git a/certora/munged/utils/Create2.sol b/certora/munged/utils/Create2.sol new file mode 100644 index 000000000..40164c1e2 --- /dev/null +++ b/certora/munged/utils/Create2.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Create2.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. + * `CREATE2` can be used to compute in advance the address where a smart + * contract will be deployed, which allows for interesting new mechanisms known + * as 'counterfactual interactions'. + * + * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more + * information. + */ +library Create2 { + /** + * @dev Deploys a contract using `CREATE2`. The address where the contract + * will be deployed can be known in advance via {computeAddress}. + * + * The bytecode for a contract can be obtained from Solidity with + * `type(contractName).creationCode`. + * + * Requirements: + * + * - `bytecode` must not be empty. + * - `salt` must have not been used for `bytecode` already. + * - the factory must have a balance of at least `amount`. + * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. + */ + function deploy( + uint256 amount, + bytes32 salt, + bytes memory bytecode + ) internal returns (address) { + address addr; + require(address(this).balance >= amount, "Create2: insufficient balance"); + require(bytecode.length != 0, "Create2: bytecode length is zero"); + assembly { + addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) + } + require(addr != address(0), "Create2: Failed on deploy"); + return addr; + } + + /** + * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the + * `bytecodeHash` or `salt` will result in a new destination address. + */ + function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { + return computeAddress(salt, bytecodeHash, address(this)); + } + + /** + * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at + * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. + */ + function computeAddress( + bytes32 salt, + bytes32 bytecodeHash, + address deployer + ) internal pure returns (address) { + bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)); + return address(uint160(uint256(_data))); + } +} diff --git a/certora/munged/utils/Multicall.sol b/certora/munged/utils/Multicall.sol new file mode 100644 index 000000000..bdb820139 --- /dev/null +++ b/certora/munged/utils/Multicall.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol) + +pragma solidity ^0.8.0; + +import "./Address.sol"; + +/** + * @dev Provides a function to batch together multiple calls in a single external call. + * + * _Available since v4.1._ + */ +abstract contract Multicall { + /** + * @dev Receives and executes a batch of function calls on this contract. + */ + function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) { + results = new bytes[](data.length); + for (uint256 i = 0; i < data.length; i++) { + results[i] = Address.functionDelegateCall(address(this), data[i]); + } + return results; + } +} diff --git a/certora/munged/utils/README.adoc b/certora/munged/utils/README.adoc new file mode 100644 index 000000000..7fef825a6 --- /dev/null +++ b/certora/munged/utils/README.adoc @@ -0,0 +1,111 @@ += Utilities + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/utils + +Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives. + +The {Address}, {Arrays}, {Base64} and {Strings} libraries provide more operations related to these native data types, while {SafeCast} adds ways to safely convert between the different signed and unsigned numeric types. +{Multicall} provides a function to batch together multiple calls in a single external call. + +For new data types: + + * {Counters}: a simple way to get a counter that can only be incremented, decremented or reset. Very useful for ID generation, counting contract activity, among others. + * {EnumerableMap}: like Solidity's https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] type, but with key-value _enumeration_: this will let you know how many entries a mapping has, and iterate over them (which is not possible with `mapping`). + * {EnumerableSet}: like {EnumerableMap}, but for https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets]. Can be used to store privileged accounts, issued IDs, etc. + +[NOTE] +==== +Because Solidity does not support generic types, {EnumerableMap} and {EnumerableSet} are specialized to a limited number of key-value types. + +As of v3.0, {EnumerableMap} supports `uint256 -> address` (`UintToAddressMap`), and {EnumerableSet} supports `address` and `uint256` (`AddressSet` and `UintSet`). +==== + +Finally, {Create2} contains all necessary utilities to safely use the https://blog.openzeppelin.com/getting-the-most-out-of-create2/[`CREATE2` EVM opcode], without having to deal with low-level assembly. + +== Math + +{{Math}} + +{{SignedMath}} + +{{SafeCast}} + +{{SafeMath}} + +{{SignedSafeMath}} + +== Cryptography + +{{ECDSA}} + +{{SignatureChecker}} + +{{MerkleProof}} + +{{EIP712}} + +== Escrow + +{{ConditionalEscrow}} + +{{Escrow}} + +{{RefundEscrow}} + +== Introspection + +This set of interfaces and contracts deal with https://en.wikipedia.org/wiki/Type_introspection[type introspection] of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract's _interface_. + +Ethereum contracts have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. `ERC20` tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract _declaring_ its interface can be very helpful in preventing errors. + +There are two main ways to approach this. + +* Locally, where a contract implements `IERC165` and declares an interface, and a second one queries it directly via `ERC165Checker`. +* Globally, where a global and unique registry (`IERC1820Registry`) is used to register implementers of a certain interface (`IERC1820Implementer`). It is then the registry that is queried, which allows for more complex setups, like contracts implementing interfaces for externally-owned accounts. + +Note that, in all cases, accounts simply _declare_ their interfaces, but they are not required to actually implement them. This mechanism can therefore be used to both prevent errors and allow for complex interactions (see `ERC777`), but it must not be relied on for security. + +{{IERC165}} + +{{ERC165}} + +{{ERC165Storage}} + +{{ERC165Checker}} + +{{IERC1820Registry}} + +{{IERC1820Implementer}} + +{{ERC1820Implementer}} + +== Data Structures + +{{BitMaps}} + +{{EnumerableMap}} + +{{EnumerableSet}} + +{{DoubleEndedQueue}} + +{{Checkpoints}} + +== Libraries + +{{Create2}} + +{{Address}} + +{{Arrays}} + +{{Base64}} + +{{Counters}} + +{{Strings}} + +{{StorageSlot}} + +{{Multicall}} diff --git a/certora/munged/utils/StorageSlot.sol b/certora/munged/utils/StorageSlot.sol new file mode 100644 index 000000000..28239dbc3 --- /dev/null +++ b/certora/munged/utils/StorageSlot.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Library for reading and writing primitive types to specific storage slots. + * + * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. + * This library helps with reading and writing to such slots without the need for inline assembly. + * + * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. + * + * Example usage to set ERC1967 implementation slot: + * ``` + * contract ERC1967 { + * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + * + * function _getImplementation() internal view returns (address) { + * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + * } + * + * function _setImplementation(address newImplementation) internal { + * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + * } + * } + * ``` + * + * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ + */ +library StorageSlot { + struct AddressSlot { + address value; + } + + struct BooleanSlot { + bool value; + } + + struct Bytes32Slot { + bytes32 value; + } + + struct Uint256Slot { + uint256 value; + } + + /** + * @dev Returns an `AddressSlot` with member `value` located at `slot`. + */ + function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `BooleanSlot` with member `value` located at `slot`. + */ + function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. + */ + function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Uint256Slot` with member `value` located at `slot`. + */ + function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { + assembly { + r.slot := slot + } + } +} diff --git a/certora/munged/utils/Strings.sol b/certora/munged/utils/Strings.sol new file mode 100644 index 000000000..d38bbe826 --- /dev/null +++ b/certora/munged/utils/Strings.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) + +pragma solidity ^0.8.0; + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + // Inspired by OraclizeAPI's implementation - MIT licence + // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol + + if (value == 0) { + return "0"; + } + uint256 temp = value; + uint256 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + bytes memory buffer = new bytes(digits); + while (value != 0) { + digits -= 1; + buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); + value /= 10; + } + return string(buffer); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + if (value == 0) { + return "0x00"; + } + uint256 temp = value; + uint256 length = 0; + while (temp != 0) { + length++; + temp >>= 8; + } + return toHexString(value, length); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = _HEX_SYMBOLS[value & 0xf]; + value >>= 4; + } + require(value == 0, "Strings: hex length insufficient"); + return string(buffer); + } +} diff --git a/certora/munged/utils/Timers.sol b/certora/munged/utils/Timers.sol new file mode 100644 index 000000000..4bc86f202 --- /dev/null +++ b/certora/munged/utils/Timers.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Timers.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Tooling for timepoints, timers and delays + */ +library Timers { + struct Timestamp { + uint64 _deadline; + } + + function getDeadline(Timestamp memory timer) internal pure returns (uint64) { + return timer._deadline; + } + + function setDeadline(Timestamp storage timer, uint64 timestamp) internal { + timer._deadline = timestamp; + } + + function reset(Timestamp storage timer) internal { + timer._deadline = 0; + } + + function isUnset(Timestamp memory timer) internal pure returns (bool) { + return timer._deadline == 0; + } + + function isStarted(Timestamp memory timer) internal pure returns (bool) { + return timer._deadline > 0; + } + + function isPending(Timestamp memory timer) internal view returns (bool) { + return timer._deadline > block.timestamp; + } + + function isExpired(Timestamp memory timer) internal view returns (bool) { + return isStarted(timer) && timer._deadline <= block.timestamp; + } + + struct BlockNumber { + uint64 _deadline; + } + + function getDeadline(BlockNumber memory timer) internal pure returns (uint64) { + return timer._deadline; + } + + function setDeadline(BlockNumber storage timer, uint64 timestamp) internal { + timer._deadline = timestamp; + } + + function reset(BlockNumber storage timer) internal { + timer._deadline = 0; + } + + function isUnset(BlockNumber memory timer) internal pure returns (bool) { + return timer._deadline == 0; + } + + function isStarted(BlockNumber memory timer) internal pure returns (bool) { + return timer._deadline > 0; + } + + function isPending(BlockNumber memory timer) internal view returns (bool) { + return timer._deadline > block.number; + } + + function isExpired(BlockNumber memory timer) internal view returns (bool) { + return isStarted(timer) && timer._deadline <= block.number; + } +} diff --git a/certora/munged/utils/cryptography/ECDSA.sol b/certora/munged/utils/cryptography/ECDSA.sol new file mode 100644 index 000000000..b2db6bd77 --- /dev/null +++ b/certora/munged/utils/cryptography/ECDSA.sol @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol) + +pragma solidity ^0.8.0; + +import "../Strings.sol"; + +/** + * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. + * + * These functions can be used to verify that a message was signed by the holder + * of the private keys of a given address. + */ +library ECDSA { + enum RecoverError { + NoError, + InvalidSignature, + InvalidSignatureLength, + InvalidSignatureS, + InvalidSignatureV + } + + function _throwError(RecoverError error) private pure { + if (error == RecoverError.NoError) { + return; // no error: do nothing + } else if (error == RecoverError.InvalidSignature) { + revert("ECDSA: invalid signature"); + } else if (error == RecoverError.InvalidSignatureLength) { + revert("ECDSA: invalid signature length"); + } else if (error == RecoverError.InvalidSignatureS) { + revert("ECDSA: invalid signature 's' value"); + } else if (error == RecoverError.InvalidSignatureV) { + revert("ECDSA: invalid signature 'v' value"); + } + } + + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature` or error string. This address can then be used for verification purposes. + * + * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {toEthSignedMessageHash} on it. + * + * Documentation for signature generation: + * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] + * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] + * + * _Available since v4.3._ + */ + function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { + // Check the signature length + // - case 65: r,s,v signature (standard) + // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ + if (signature.length == 65) { + bytes32 r; + bytes32 s; + uint8 v; + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + return tryRecover(hash, v, r, s); + } else if (signature.length == 64) { + bytes32 r; + bytes32 vs; + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + assembly { + r := mload(add(signature, 0x20)) + vs := mload(add(signature, 0x40)) + } + return tryRecover(hash, r, vs); + } else { + return (address(0), RecoverError.InvalidSignatureLength); + } + } + + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature`. This address can then be used for verification purposes. + * + * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {toEthSignedMessageHash} on it. + */ + function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, signature); + _throwError(error); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. + * + * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] + * + * _Available since v4.3._ + */ + function tryRecover( + bytes32 hash, + bytes32 r, + bytes32 vs + ) internal pure returns (address, RecoverError) { + bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); + uint8 v = uint8((uint256(vs) >> 255) + 27); + return tryRecover(hash, v, r, s); + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. + * + * _Available since v4.2._ + */ + function recover( + bytes32 hash, + bytes32 r, + bytes32 vs + ) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, r, vs); + _throwError(error); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `v`, + * `r` and `s` signature fields separately. + * + * _Available since v4.3._ + */ + function tryRecover( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address, RecoverError) { + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { + return (address(0), RecoverError.InvalidSignatureS); + } + if (v != 27 && v != 28) { + return (address(0), RecoverError.InvalidSignatureV); + } + + // If the signature is valid (and not malleable), return the signer address + address signer = ecrecover(hash, v, r, s); + if (signer == address(0)) { + return (address(0), RecoverError.InvalidSignature); + } + + return (signer, RecoverError.NoError); + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `v`, + * `r` and `s` signature fields separately. + */ + function recover( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, v, r, s); + _throwError(error); + return recovered; + } + + /** + * @dev Returns an Ethereum Signed Message, created from a `hash`. This + * produces hash corresponding to the one signed with the + * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] + * JSON-RPC method as part of EIP-191. + * + * See {recover}. + */ + function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { + // 32 is the length in bytes of hash, + // enforced by the type signature above + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); + } + + /** + * @dev Returns an Ethereum Signed Message, created from `s`. This + * produces hash corresponding to the one signed with the + * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] + * JSON-RPC method as part of EIP-191. + * + * See {recover}. + */ + function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); + } + + /** + * @dev Returns an Ethereum Signed Typed Data, created from a + * `domainSeparator` and a `structHash`. This produces hash corresponding + * to the one signed with the + * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] + * JSON-RPC method as part of EIP-712. + * + * See {recover}. + */ + function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + } +} diff --git a/certora/munged/utils/cryptography/MerkleProof.sol b/certora/munged/utils/cryptography/MerkleProof.sol new file mode 100644 index 000000000..03244f488 --- /dev/null +++ b/certora/munged/utils/cryptography/MerkleProof.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/MerkleProof.sol) + +pragma solidity ^0.8.0; + +/** + * @dev These functions deal with verification of Merkle Trees proofs. + * + * The proofs can be generated using the JavaScript library + * https://github.com/miguelmota/merkletreejs[merkletreejs]. + * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. + * + * See `test/utils/cryptography/MerkleProof.test.js` for some examples. + * + * WARNING: You should avoid using leaf values that are 64 bytes long prior to + * hashing, or use a hash function other than keccak256 for hashing leaves. + * This is because the concatenation of a sorted pair of internal nodes in + * the merkle tree could be reinterpreted as a leaf value. + */ +library MerkleProof { + /** + * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree + * defined by `root`. For this, a `proof` must be provided, containing + * sibling hashes on the branch from the leaf to the root of the tree. Each + * pair of leaves and each pair of pre-images are assumed to be sorted. + */ + function verify( + bytes32[] memory proof, + bytes32 root, + bytes32 leaf + ) internal pure returns (bool) { + return processProof(proof, leaf) == root; + } + + /** + * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up + * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt + * hash matches the root of the tree. When processing the proof, the pairs + * of leafs & pre-images are assumed to be sorted. + * + * _Available since v4.4._ + */ + function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { + bytes32 computedHash = leaf; + for (uint256 i = 0; i < proof.length; i++) { + bytes32 proofElement = proof[i]; + if (computedHash <= proofElement) { + // Hash(current computed hash + current element of the proof) + computedHash = _efficientHash(computedHash, proofElement); + } else { + // Hash(current element of the proof + current computed hash) + computedHash = _efficientHash(proofElement, computedHash); + } + } + return computedHash; + } + + function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { + assembly { + mstore(0x00, a) + mstore(0x20, b) + value := keccak256(0x00, 0x40) + } + } +} diff --git a/certora/munged/utils/cryptography/SignatureChecker.sol b/certora/munged/utils/cryptography/SignatureChecker.sol new file mode 100644 index 000000000..3ed6e719a --- /dev/null +++ b/certora/munged/utils/cryptography/SignatureChecker.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/SignatureChecker.sol) + +pragma solidity ^0.8.0; + +import "./ECDSA.sol"; +import "../Address.sol"; +import "../../interfaces/IERC1271.sol"; + +/** + * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA + * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like + * Argent and Gnosis Safe. + * + * _Available since v4.1._ + */ +library SignatureChecker { + /** + * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the + * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`. + * + * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus + * change through time. It could return true at block N and false at block N+1 (or the opposite). + */ + function isValidSignatureNow( + address signer, + bytes32 hash, + bytes memory signature + ) internal view returns (bool) { + (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature); + if (error == ECDSA.RecoverError.NoError && recovered == signer) { + return true; + } + + (bool success, bytes memory result) = signer.staticcall( + abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature) + ); + return (success && result.length == 32 && abi.decode(result, (bytes4)) == IERC1271.isValidSignature.selector); + } +} diff --git a/certora/munged/utils/cryptography/draft-EIP712.sol b/certora/munged/utils/cryptography/draft-EIP712.sol new file mode 100644 index 000000000..a32c25b7f --- /dev/null +++ b/certora/munged/utils/cryptography/draft-EIP712.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol) + +pragma solidity ^0.8.0; + +import "./ECDSA.sol"; + +/** + * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. + * + * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, + * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding + * they need in their contracts using a combination of `abi.encode` and `keccak256`. + * + * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding + * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA + * ({_hashTypedDataV4}). + * + * The implementation of the domain separator was designed to be as efficient as possible while still properly updating + * the chain id to protect against replay attacks on an eventual fork of the chain. + * + * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method + * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. + * + * _Available since v3.4._ + */ +abstract contract EIP712 { + /* solhint-disable var-name-mixedcase */ + // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to + // invalidate the cached domain separator if the chain id changes. + bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; + uint256 private immutable _CACHED_CHAIN_ID; + address private immutable _CACHED_THIS; + + bytes32 private immutable _HASHED_NAME; + bytes32 private immutable _HASHED_VERSION; + bytes32 private immutable _TYPE_HASH; + + /* solhint-enable var-name-mixedcase */ + + /** + * @dev Initializes the domain separator and parameter caches. + * + * The meaning of `name` and `version` is specified in + * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: + * + * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. + * - `version`: the current major version of the signing domain. + * + * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart + * contract upgrade]. + */ + constructor(string memory name, string memory version) { + bytes32 hashedName = keccak256(bytes(name)); + bytes32 hashedVersion = keccak256(bytes(version)); + bytes32 typeHash = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + _HASHED_NAME = hashedName; + _HASHED_VERSION = hashedVersion; + _CACHED_CHAIN_ID = block.chainid; + _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); + _CACHED_THIS = address(this); + _TYPE_HASH = typeHash; + } + + /** + * @dev Returns the domain separator for the current chain. + */ + function _domainSeparatorV4() internal view returns (bytes32) { + if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { + return _CACHED_DOMAIN_SEPARATOR; + } else { + return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); + } + } + + function _buildDomainSeparator( + bytes32 typeHash, + bytes32 nameHash, + bytes32 versionHash + ) private view returns (bytes32) { + return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); + } + + /** + * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this + * function returns the hash of the fully encoded EIP712 message for this domain. + * + * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: + * + * ```solidity + * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( + * keccak256("Mail(address to,string contents)"), + * mailTo, + * keccak256(bytes(mailContents)) + * ))); + * address signer = ECDSA.recover(digest, signature); + * ``` + */ + function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { + return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); + } +} diff --git a/certora/munged/utils/escrow/ConditionalEscrow.sol b/certora/munged/utils/escrow/ConditionalEscrow.sol new file mode 100644 index 000000000..87f53815b --- /dev/null +++ b/certora/munged/utils/escrow/ConditionalEscrow.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/escrow/ConditionalEscrow.sol) + +pragma solidity ^0.8.0; + +import "./Escrow.sol"; + +/** + * @title ConditionalEscrow + * @dev Base abstract escrow to only allow withdrawal if a condition is met. + * @dev Intended usage: See {Escrow}. Same usage guidelines apply here. + */ +abstract contract ConditionalEscrow is Escrow { + /** + * @dev Returns whether an address is allowed to withdraw their funds. To be + * implemented by derived contracts. + * @param payee The destination address of the funds. + */ + function withdrawalAllowed(address payee) public view virtual returns (bool); + + function withdraw(address payable payee) public virtual override { + require(withdrawalAllowed(payee), "ConditionalEscrow: payee is not allowed to withdraw"); + super.withdraw(payee); + } +} diff --git a/certora/munged/utils/escrow/Escrow.sol b/certora/munged/utils/escrow/Escrow.sol new file mode 100644 index 000000000..c90a74618 --- /dev/null +++ b/certora/munged/utils/escrow/Escrow.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/escrow/Escrow.sol) + +pragma solidity ^0.8.0; + +import "../../access/Ownable.sol"; +import "../Address.sol"; + +/** + * @title Escrow + * @dev Base escrow contract, holds funds designated for a payee until they + * withdraw them. + * + * Intended usage: This contract (and derived escrow contracts) should be a + * standalone contract, that only interacts with the contract that instantiated + * it. That way, it is guaranteed that all Ether will be handled according to + * the `Escrow` rules, and there is no need to check for payable functions or + * transfers in the inheritance tree. The contract that uses the escrow as its + * payment method should be its owner, and provide public methods redirecting + * to the escrow's deposit and withdraw. + */ +contract Escrow is Ownable { + using Address for address payable; + + event Deposited(address indexed payee, uint256 weiAmount); + event Withdrawn(address indexed payee, uint256 weiAmount); + + mapping(address => uint256) private _deposits; + + function depositsOf(address payee) public view returns (uint256) { + return _deposits[payee]; + } + + /** + * @dev Stores the sent amount as credit to be withdrawn. + * @param payee The destination address of the funds. + */ + function deposit(address payee) public payable virtual onlyOwner { + uint256 amount = msg.value; + _deposits[payee] += amount; + emit Deposited(payee, amount); + } + + /** + * @dev Withdraw accumulated balance for a payee, forwarding all gas to the + * recipient. + * + * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. + * Make sure you trust the recipient, or are either following the + * checks-effects-interactions pattern or using {ReentrancyGuard}. + * + * @param payee The address whose funds will be withdrawn and transferred to. + */ + function withdraw(address payable payee) public virtual onlyOwner { + uint256 payment = _deposits[payee]; + + _deposits[payee] = 0; + + payee.sendValue(payment); + + emit Withdrawn(payee, payment); + } +} diff --git a/certora/munged/utils/escrow/RefundEscrow.sol b/certora/munged/utils/escrow/RefundEscrow.sol new file mode 100644 index 000000000..0e9621fee --- /dev/null +++ b/certora/munged/utils/escrow/RefundEscrow.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/escrow/RefundEscrow.sol) + +pragma solidity ^0.8.0; + +import "./ConditionalEscrow.sol"; + +/** + * @title RefundEscrow + * @dev Escrow that holds funds for a beneficiary, deposited from multiple + * parties. + * @dev Intended usage: See {Escrow}. Same usage guidelines apply here. + * @dev The owner account (that is, the contract that instantiates this + * contract) may deposit, close the deposit period, and allow for either + * withdrawal by the beneficiary, or refunds to the depositors. All interactions + * with `RefundEscrow` will be made through the owner contract. + */ +contract RefundEscrow is ConditionalEscrow { + using Address for address payable; + + enum State { + Active, + Refunding, + Closed + } + + event RefundsClosed(); + event RefundsEnabled(); + + State private _state; + address payable private immutable _beneficiary; + + /** + * @dev Constructor. + * @param beneficiary_ The beneficiary of the deposits. + */ + constructor(address payable beneficiary_) { + require(beneficiary_ != address(0), "RefundEscrow: beneficiary is the zero address"); + _beneficiary = beneficiary_; + _state = State.Active; + } + + /** + * @return The current state of the escrow. + */ + function state() public view virtual returns (State) { + return _state; + } + + /** + * @return The beneficiary of the escrow. + */ + function beneficiary() public view virtual returns (address payable) { + return _beneficiary; + } + + /** + * @dev Stores funds that may later be refunded. + * @param refundee The address funds will be sent to if a refund occurs. + */ + function deposit(address refundee) public payable virtual override { + require(state() == State.Active, "RefundEscrow: can only deposit while active"); + super.deposit(refundee); + } + + /** + * @dev Allows for the beneficiary to withdraw their funds, rejecting + * further deposits. + */ + function close() public virtual onlyOwner { + require(state() == State.Active, "RefundEscrow: can only close while active"); + _state = State.Closed; + emit RefundsClosed(); + } + + /** + * @dev Allows for refunds to take place, rejecting further deposits. + */ + function enableRefunds() public virtual onlyOwner { + require(state() == State.Active, "RefundEscrow: can only enable refunds while active"); + _state = State.Refunding; + emit RefundsEnabled(); + } + + /** + * @dev Withdraws the beneficiary's funds. + */ + function beneficiaryWithdraw() public virtual { + require(state() == State.Closed, "RefundEscrow: beneficiary can only withdraw while closed"); + beneficiary().sendValue(address(this).balance); + } + + /** + * @dev Returns whether refundees can withdraw their deposits (be refunded). The overridden function receives a + * 'payee' argument, but we ignore it here since the condition is global, not per-payee. + */ + function withdrawalAllowed(address) public view override returns (bool) { + return state() == State.Refunding; + } +} diff --git a/certora/munged/utils/introspection/ERC165.sol b/certora/munged/utils/introspection/ERC165.sol new file mode 100644 index 000000000..3bf5613a6 --- /dev/null +++ b/certora/munged/utils/introspection/ERC165.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +/** + * @dev Implementation of the {IERC165} interface. + * + * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check + * for the additional interface id that will be supported. For example: + * + * ```solidity + * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); + * } + * ``` + * + * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. + */ +abstract contract ERC165 is IERC165 { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IERC165).interfaceId; + } +} diff --git a/certora/munged/utils/introspection/ERC165Checker.sol b/certora/munged/utils/introspection/ERC165Checker.sol new file mode 100644 index 000000000..6a240e155 --- /dev/null +++ b/certora/munged/utils/introspection/ERC165Checker.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165Checker.sol) + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +/** + * @dev Library used to query support of an interface declared via {IERC165}. + * + * Note that these functions return the actual result of the query: they do not + * `revert` if an interface is not supported. It is up to the caller to decide + * what to do in these cases. + */ +library ERC165Checker { + // As per the EIP-165 spec, no interface should ever match 0xffffffff + bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; + + /** + * @dev Returns true if `account` supports the {IERC165} interface, + */ + function supportsERC165(address account) internal view returns (bool) { + // Any contract that implements ERC165 must explicitly indicate support of + // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid + return + _supportsERC165Interface(account, type(IERC165).interfaceId) && + !_supportsERC165Interface(account, _INTERFACE_ID_INVALID); + } + + /** + * @dev Returns true if `account` supports the interface defined by + * `interfaceId`. Support for {IERC165} itself is queried automatically. + * + * See {IERC165-supportsInterface}. + */ + function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { + // query support of both ERC165 as per the spec and support of _interfaceId + return supportsERC165(account) && _supportsERC165Interface(account, interfaceId); + } + + /** + * @dev Returns a boolean array where each value corresponds to the + * interfaces passed in and whether they're supported or not. This allows + * you to batch check interfaces for a contract where your expectation + * is that some interfaces may not be supported. + * + * See {IERC165-supportsInterface}. + * + * _Available since v3.4._ + */ + function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) + internal + view + returns (bool[] memory) + { + // an array of booleans corresponding to interfaceIds and whether they're supported or not + bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); + + // query support of ERC165 itself + if (supportsERC165(account)) { + // query support of each interface in interfaceIds + for (uint256 i = 0; i < interfaceIds.length; i++) { + interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]); + } + } + + return interfaceIdsSupported; + } + + /** + * @dev Returns true if `account` supports all the interfaces defined in + * `interfaceIds`. Support for {IERC165} itself is queried automatically. + * + * Batch-querying can lead to gas savings by skipping repeated checks for + * {IERC165} support. + * + * See {IERC165-supportsInterface}. + */ + function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { + // query support of ERC165 itself + if (!supportsERC165(account)) { + return false; + } + + // query support of each interface in _interfaceIds + for (uint256 i = 0; i < interfaceIds.length; i++) { + if (!_supportsERC165Interface(account, interfaceIds[i])) { + return false; + } + } + + // all interfaces supported + return true; + } + + /** + * @notice Query if a contract implements an interface, does not check ERC165 support + * @param account The address of the contract to query for support of an interface + * @param interfaceId The interface identifier, as specified in ERC-165 + * @return true if the contract at account indicates support of the interface with + * identifier interfaceId, false otherwise + * @dev Assumes that account contains a contract that supports ERC165, otherwise + * the behavior of this method is undefined. This precondition can be checked + * with {supportsERC165}. + * Interface identification is specified in ERC-165. + */ + function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) { + bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId); + (bool success, bytes memory result) = account.staticcall{gas: 30000}(encodedParams); + if (result.length < 32) return false; + return success && abi.decode(result, (bool)); + } +} diff --git a/certora/munged/utils/introspection/ERC165Storage.sol b/certora/munged/utils/introspection/ERC165Storage.sol new file mode 100644 index 000000000..c99d9f3fb --- /dev/null +++ b/certora/munged/utils/introspection/ERC165Storage.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165Storage.sol) + +pragma solidity ^0.8.0; + +import "./ERC165.sol"; + +/** + * @dev Storage based implementation of the {IERC165} interface. + * + * Contracts may inherit from this and call {_registerInterface} to declare + * their support of an interface. + */ +abstract contract ERC165Storage is ERC165 { + /** + * @dev Mapping of interface ids to whether or not it's supported. + */ + mapping(bytes4 => bool) private _supportedInterfaces; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return super.supportsInterface(interfaceId) || _supportedInterfaces[interfaceId]; + } + + /** + * @dev Registers the contract as an implementer of the interface defined by + * `interfaceId`. Support of the actual ERC165 interface is automatic and + * registering its interface id is not required. + * + * See {IERC165-supportsInterface}. + * + * Requirements: + * + * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). + */ + function _registerInterface(bytes4 interfaceId) internal virtual { + require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); + _supportedInterfaces[interfaceId] = true; + } +} diff --git a/certora/munged/utils/introspection/ERC1820Implementer.sol b/certora/munged/utils/introspection/ERC1820Implementer.sol new file mode 100644 index 000000000..1b5139658 --- /dev/null +++ b/certora/munged/utils/introspection/ERC1820Implementer.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC1820Implementer.sol) + +pragma solidity ^0.8.0; + +import "./IERC1820Implementer.sol"; + +/** + * @dev Implementation of the {IERC1820Implementer} interface. + * + * Contracts may inherit from this and call {_registerInterfaceForAddress} to + * declare their willingness to be implementers. + * {IERC1820Registry-setInterfaceImplementer} should then be called for the + * registration to be complete. + */ +contract ERC1820Implementer is IERC1820Implementer { + bytes32 private constant _ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC"); + + mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces; + + /** + * @dev See {IERC1820Implementer-canImplementInterfaceForAddress}. + */ + function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) + public + view + virtual + override + returns (bytes32) + { + return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00); + } + + /** + * @dev Declares the contract as willing to be an implementer of + * `interfaceHash` for `account`. + * + * See {IERC1820Registry-setInterfaceImplementer} and + * {IERC1820Registry-interfaceHash}. + */ + function _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal virtual { + _supportedInterfaces[interfaceHash][account] = true; + } +} diff --git a/certora/munged/utils/introspection/IERC165.sol b/certora/munged/utils/introspection/IERC165.sol new file mode 100644 index 000000000..e8cdbdbf6 --- /dev/null +++ b/certora/munged/utils/introspection/IERC165.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC165 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-165[EIP]. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others ({ERC165Checker}). + * + * For an implementation, see {ERC165}. + */ +interface IERC165 { + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} diff --git a/certora/munged/utils/introspection/IERC1820Implementer.sol b/certora/munged/utils/introspection/IERC1820Implementer.sol new file mode 100644 index 000000000..c4d0b3028 --- /dev/null +++ b/certora/munged/utils/introspection/IERC1820Implementer.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC1820Implementer.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface for an ERC1820 implementer, as defined in the + * https://eips.ethereum.org/EIPS/eip-1820#interface-implementation-erc1820implementerinterface[EIP]. + * Used by contracts that will be registered as implementers in the + * {IERC1820Registry}. + */ +interface IERC1820Implementer { + /** + * @dev Returns a special value (`ERC1820_ACCEPT_MAGIC`) if this contract + * implements `interfaceHash` for `account`. + * + * See {IERC1820Registry-setInterfaceImplementer}. + */ + function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) external view returns (bytes32); +} diff --git a/certora/munged/utils/introspection/IERC1820Registry.sol b/certora/munged/utils/introspection/IERC1820Registry.sol new file mode 100644 index 000000000..26dc8e62b --- /dev/null +++ b/certora/munged/utils/introspection/IERC1820Registry.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC1820Registry.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the global ERC1820 Registry, as defined in the + * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register + * implementers for interfaces in this registry, as well as query support. + * + * Implementers may be shared by multiple accounts, and can also implement more + * than a single interface for each account. Contracts can implement interfaces + * for themselves, but externally-owned accounts (EOA) must delegate this to a + * contract. + * + * {IERC165} interfaces can also be queried via the registry. + * + * For an in-depth explanation and source code analysis, see the EIP text. + */ +interface IERC1820Registry { + /** + * @dev Sets `newManager` as the manager for `account`. A manager of an + * account is able to set interface implementers for it. + * + * By default, each account is its own manager. Passing a value of `0x0` in + * `newManager` will reset the manager to this initial state. + * + * Emits a {ManagerChanged} event. + * + * Requirements: + * + * - the caller must be the current manager for `account`. + */ + function setManager(address account, address newManager) external; + + /** + * @dev Returns the manager for `account`. + * + * See {setManager}. + */ + function getManager(address account) external view returns (address); + + /** + * @dev Sets the `implementer` contract as ``account``'s implementer for + * `interfaceHash`. + * + * `account` being the zero address is an alias for the caller's address. + * The zero address can also be used in `implementer` to remove an old one. + * + * See {interfaceHash} to learn how these are created. + * + * Emits an {InterfaceImplementerSet} event. + * + * Requirements: + * + * - the caller must be the current manager for `account`. + * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not + * end in 28 zeroes). + * - `implementer` must implement {IERC1820Implementer} and return true when + * queried for support, unless `implementer` is the caller. See + * {IERC1820Implementer-canImplementInterfaceForAddress}. + */ + function setInterfaceImplementer( + address account, + bytes32 _interfaceHash, + address implementer + ) external; + + /** + * @dev Returns the implementer of `interfaceHash` for `account`. If no such + * implementer is registered, returns the zero address. + * + * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28 + * zeroes), `account` will be queried for support of it. + * + * `account` being the zero address is an alias for the caller's address. + */ + function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address); + + /** + * @dev Returns the interface hash for an `interfaceName`, as defined in the + * corresponding + * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP]. + */ + function interfaceHash(string calldata interfaceName) external pure returns (bytes32); + + /** + * @notice Updates the cache with whether the contract implements an ERC165 interface or not. + * @param account Address of the contract for which to update the cache. + * @param interfaceId ERC165 interface for which to update the cache. + */ + function updateERC165Cache(address account, bytes4 interfaceId) external; + + /** + * @notice Checks whether a contract implements an ERC165 interface or not. + * If the result is not cached a direct lookup on the contract address is performed. + * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling + * {updateERC165Cache} with the contract address. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account` implements `interfaceId`, false otherwise. + */ + function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); + + /** + * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account` implements `interfaceId`, false otherwise. + */ + function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); + + event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); + + event ManagerChanged(address indexed account, address indexed newManager); +} diff --git a/certora/munged/utils/math/Math.sol b/certora/munged/utils/math/Math.sol new file mode 100644 index 000000000..291d257b0 --- /dev/null +++ b/certora/munged/utils/math/Math.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Standard math utilities missing in the Solidity language. + */ +library Math { + /** + * @dev Returns the largest of two numbers. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return a >= b ? a : b; + } + + /** + * @dev Returns the smallest of two numbers. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two numbers. The result is rounded towards + * zero. + */ + function average(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b) / 2 can overflow. + return (a & b) + (a ^ b) / 2; + } + + /** + * @dev Returns the ceiling of the division of two numbers. + * + * This differs from standard division with `/` in that it rounds up instead + * of rounding down. + */ + function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b - 1) / b can overflow on addition, so we distribute. + return a / b + (a % b == 0 ? 0 : 1); + } +} diff --git a/certora/munged/utils/math/SafeCast.sol b/certora/munged/utils/math/SafeCast.sol new file mode 100644 index 000000000..3cd647357 --- /dev/null +++ b/certora/munged/utils/math/SafeCast.sol @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. `SafeCast` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing + * all math on `uint256` and `int256` and then downcasting. + */ +library SafeCast { + /** + * @dev Returns the downcasted uint224 from uint256, reverting on + * overflow (when the input is greater than largest uint224). + * + * Counterpart to Solidity's `uint224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + */ + function toUint224(uint256 value) internal pure returns (uint224) { + require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); + return uint224(value); + } + + /** + * @dev Returns the downcasted uint128 from uint256, reverting on + * overflow (when the input is greater than largest uint128). + * + * Counterpart to Solidity's `uint128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + */ + function toUint128(uint256 value) internal pure returns (uint128) { + require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); + return uint128(value); + } + + /** + * @dev Returns the downcasted uint96 from uint256, reverting on + * overflow (when the input is greater than largest uint96). + * + * Counterpart to Solidity's `uint96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + */ + function toUint96(uint256 value) internal pure returns (uint96) { + require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); + return uint96(value); + } + + /** + * @dev Returns the downcasted uint64 from uint256, reverting on + * overflow (when the input is greater than largest uint64). + * + * Counterpart to Solidity's `uint64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + */ + function toUint64(uint256 value) internal pure returns (uint64) { + require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); + return uint64(value); + } + + /** + * @dev Returns the downcasted uint32 from uint256, reverting on + * overflow (when the input is greater than largest uint32). + * + * Counterpart to Solidity's `uint32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + */ + function toUint32(uint256 value) internal pure returns (uint32) { + require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); + return uint32(value); + } + + /** + * @dev Returns the downcasted uint16 from uint256, reverting on + * overflow (when the input is greater than largest uint16). + * + * Counterpart to Solidity's `uint16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + */ + function toUint16(uint256 value) internal pure returns (uint16) { + require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); + return uint16(value); + } + + /** + * @dev Returns the downcasted uint8 from uint256, reverting on + * overflow (when the input is greater than largest uint8). + * + * Counterpart to Solidity's `uint8` operator. + * + * Requirements: + * + * - input must fit into 8 bits. + */ + function toUint8(uint256 value) internal pure returns (uint8) { + require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); + return uint8(value); + } + + /** + * @dev Converts a signed int256 into an unsigned uint256. + * + * Requirements: + * + * - input must be greater than or equal to 0. + */ + function toUint256(int256 value) internal pure returns (uint256) { + require(value >= 0, "SafeCast: value must be positive"); + return uint256(value); + } + + /** + * @dev Returns the downcasted int128 from int256, reverting on + * overflow (when the input is less than smallest int128 or + * greater than largest int128). + * + * Counterpart to Solidity's `int128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + * + * _Available since v3.1._ + */ + function toInt128(int256 value) internal pure returns (int128) { + require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits"); + return int128(value); + } + + /** + * @dev Returns the downcasted int64 from int256, reverting on + * overflow (when the input is less than smallest int64 or + * greater than largest int64). + * + * Counterpart to Solidity's `int64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + * + * _Available since v3.1._ + */ + function toInt64(int256 value) internal pure returns (int64) { + require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits"); + return int64(value); + } + + /** + * @dev Returns the downcasted int32 from int256, reverting on + * overflow (when the input is less than smallest int32 or + * greater than largest int32). + * + * Counterpart to Solidity's `int32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + * + * _Available since v3.1._ + */ + function toInt32(int256 value) internal pure returns (int32) { + require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits"); + return int32(value); + } + + /** + * @dev Returns the downcasted int16 from int256, reverting on + * overflow (when the input is less than smallest int16 or + * greater than largest int16). + * + * Counterpart to Solidity's `int16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + * + * _Available since v3.1._ + */ + function toInt16(int256 value) internal pure returns (int16) { + require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits"); + return int16(value); + } + + /** + * @dev Returns the downcasted int8 from int256, reverting on + * overflow (when the input is less than smallest int8 or + * greater than largest int8). + * + * Counterpart to Solidity's `int8` operator. + * + * Requirements: + * + * - input must fit into 8 bits. + * + * _Available since v3.1._ + */ + function toInt8(int256 value) internal pure returns (int8) { + require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits"); + return int8(value); + } + + /** + * @dev Converts an unsigned uint256 into a signed int256. + * + * Requirements: + * + * - input must be less than or equal to maxInt256. + */ + function toInt256(uint256 value) internal pure returns (int256) { + // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive + require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); + return int256(value); + } +} diff --git a/certora/munged/utils/math/SafeMath.sol b/certora/munged/utils/math/SafeMath.sol new file mode 100644 index 000000000..6eb0aa6bd --- /dev/null +++ b/certora/munged/utils/math/SafeMath.sol @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol) + +pragma solidity ^0.8.0; + +// CAUTION +// This version of SafeMath should only be used with Solidity 0.8 or later, +// because it relies on the compiler's built in overflow checks. + +/** + * @dev Wrappers over Solidity's arithmetic operations. + * + * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler + * now has built in overflow checking. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + uint256 c = a + b; + if (c < a) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the substraction of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b > a) return (false, 0); + return (true, a - b); + } + } + + /** + * @dev Returns the multiplication of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) return (true, 0); + uint256 c = a * b; + if (c / a != b) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the division of two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a / b); + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a % b); + } + } + + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + return a + b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return a - b; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + return a * b; + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return a / b; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + return a % b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {trySub}. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b <= a, errorMessage); + return a - b; + } + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting with custom message on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b > 0, errorMessage); + return a / b; + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting with custom message when dividing by zero. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryMod}. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b > 0, errorMessage); + return a % b; + } + } +} diff --git a/certora/munged/utils/math/SignedMath.sol b/certora/munged/utils/math/SignedMath.sol new file mode 100644 index 000000000..5a9d6068d --- /dev/null +++ b/certora/munged/utils/math/SignedMath.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/SignedMath.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Standard signed math utilities missing in the Solidity language. + */ +library SignedMath { + /** + * @dev Returns the largest of two signed numbers. + */ + function max(int256 a, int256 b) internal pure returns (int256) { + return a >= b ? a : b; + } + + /** + * @dev Returns the smallest of two signed numbers. + */ + function min(int256 a, int256 b) internal pure returns (int256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two signed numbers without overflow. + * The result is rounded towards zero. + */ + function average(int256 a, int256 b) internal pure returns (int256) { + // Formula from the book "Hacker's Delight" + int256 x = (a & b) + ((a ^ b) >> 1); + return x + (int256(uint256(x) >> 255) & (a ^ b)); + } + + /** + * @dev Returns the absolute unsigned value of a signed value. + */ + function abs(int256 n) internal pure returns (uint256) { + unchecked { + // must be unchecked in order to support `n = type(int256).min` + return uint256(n >= 0 ? n : -n); + } + } +} diff --git a/certora/munged/utils/math/SignedSafeMath.sol b/certora/munged/utils/math/SignedSafeMath.sol new file mode 100644 index 000000000..6704d4ce2 --- /dev/null +++ b/certora/munged/utils/math/SignedSafeMath.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/math/SignedSafeMath.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations. + * + * NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler + * now has built in overflow checking. + */ +library SignedSafeMath { + /** + * @dev Returns the multiplication of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(int256 a, int256 b) internal pure returns (int256) { + return a * b; + } + + /** + * @dev Returns the integer division of two signed integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(int256 a, int256 b) internal pure returns (int256) { + return a / b; + } + + /** + * @dev Returns the subtraction of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(int256 a, int256 b) internal pure returns (int256) { + return a - b; + } + + /** + * @dev Returns the addition of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(int256 a, int256 b) internal pure returns (int256) { + return a + b; + } +} diff --git a/certora/munged/utils/structs/BitMaps.sol b/certora/munged/utils/structs/BitMaps.sol new file mode 100644 index 000000000..9721b8312 --- /dev/null +++ b/certora/munged/utils/structs/BitMaps.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/structs/BitMaps.sol) +pragma solidity ^0.8.0; + +/** + * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential. + * Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor]. + */ +library BitMaps { + struct BitMap { + mapping(uint256 => uint256) _data; + } + + /** + * @dev Returns whether the bit at `index` is set. + */ + function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { + uint256 bucket = index >> 8; + uint256 mask = 1 << (index & 0xff); + return bitmap._data[bucket] & mask != 0; + } + + /** + * @dev Sets the bit at `index` to the boolean `value`. + */ + function setTo( + BitMap storage bitmap, + uint256 index, + bool value + ) internal { + if (value) { + set(bitmap, index); + } else { + unset(bitmap, index); + } + } + + /** + * @dev Sets the bit at `index`. + */ + function set(BitMap storage bitmap, uint256 index) internal { + uint256 bucket = index >> 8; + uint256 mask = 1 << (index & 0xff); + bitmap._data[bucket] |= mask; + } + + /** + * @dev Unsets the bit at `index`. + */ + function unset(BitMap storage bitmap, uint256 index) internal { + uint256 bucket = index >> 8; + uint256 mask = 1 << (index & 0xff); + bitmap._data[bucket] &= ~mask; + } +} diff --git a/certora/munged/utils/structs/DoubleEndedQueue.sol b/certora/munged/utils/structs/DoubleEndedQueue.sol new file mode 100644 index 000000000..5ac2766eb --- /dev/null +++ b/certora/munged/utils/structs/DoubleEndedQueue.sol @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import "../math/SafeCast.sol"; + +/** + * @dev A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends of + * the sequence (called front and back). Among other access patterns, it can be used to implement efficient LIFO and + * FIFO queues. Storage use is optimized, and all operations are O(1) constant time. This includes {clear}, given that + * the existing queue contents are left in storage. + * + * The struct is called `Bytes32Deque`. Other types can be cast to and from `bytes32`. This data structure can only be + * used in storage, and not in memory. + * ``` + * DoubleEndedQueue.Bytes32Deque queue; + * ``` + * + * _Available since v4.6._ + */ +library DoubleEndedQueue { + /** + * @dev An operation (e.g. {front}) couldn't be completed due to the queue being empty. + */ + error Empty(); + + /** + * @dev An operation (e.g. {at}) could't be completed due to an index being out of bounds. + */ + error OutOfBounds(); + + /** + * @dev Indices are signed integers because the queue can grow in any direction. They are 128 bits so begin and end + * are packed in a single storage slot for efficient access. Since the items are added one at a time we can safely + * assume that these 128-bit indices will not overflow, and use unchecked arithmetic. + * + * Struct members have an underscore prefix indicating that they are "private" and should not be read or written to + * directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and + * lead to unexpected behavior. + * + * Indices are in the range [begin, end) which means the first item is at data[begin] and the last item is at + * data[end - 1]. + */ + struct Bytes32Deque { + int128 _begin; + int128 _end; + mapping(int128 => bytes32) _data; + } + + /** + * @dev Inserts an item at the end of the queue. + */ + function pushBack(Bytes32Deque storage deque, bytes32 value) internal { + int128 backIndex = deque._end; + deque._data[backIndex] = value; + unchecked { + deque._end = backIndex + 1; + } + } + + /** + * @dev Removes the item at the end of the queue and returns it. + * + * Reverts with `Empty` if the queue is empty. + */ + function popBack(Bytes32Deque storage deque) internal returns (bytes32 value) { + if (empty(deque)) revert Empty(); + int128 backIndex; + unchecked { + backIndex = deque._end - 1; + } + value = deque._data[backIndex]; + delete deque._data[backIndex]; + deque._end = backIndex; + } + + /** + * @dev Inserts an item at the beginning of the queue. + */ + function pushFront(Bytes32Deque storage deque, bytes32 value) internal { + int128 frontIndex; + unchecked { + frontIndex = deque._begin - 1; + } + deque._data[frontIndex] = value; + deque._begin = frontIndex; + } + + /** + * @dev Removes the item at the beginning of the queue and returns it. + * + * Reverts with `Empty` if the queue is empty. + */ + function popFront(Bytes32Deque storage deque) internal returns (bytes32 value) { + if (empty(deque)) revert Empty(); + int128 frontIndex = deque._begin; + value = deque._data[frontIndex]; + delete deque._data[frontIndex]; + unchecked { + deque._begin = frontIndex + 1; + } + } + + /** + * @dev Returns the item at the beginning of the queue. + * + * Reverts with `Empty` if the queue is empty. + */ + function front(Bytes32Deque storage deque) internal view returns (bytes32 value) { + if (empty(deque)) revert Empty(); + int128 frontIndex = deque._begin; + return deque._data[frontIndex]; + } + + /** + * @dev Returns the item at the end of the queue. + * + * Reverts with `Empty` if the queue is empty. + */ + function back(Bytes32Deque storage deque) internal view returns (bytes32 value) { + if (empty(deque)) revert Empty(); + int128 backIndex; + unchecked { + backIndex = deque._end - 1; + } + return deque._data[backIndex]; + } + + /** + * @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at + * `length(deque) - 1`. + * + * Reverts with `OutOfBounds` if the index is out of bounds. + */ + function at(Bytes32Deque storage deque, uint256 index) internal view returns (bytes32 value) { + // int256(deque._begin) is a safe upcast + int128 idx = SafeCast.toInt128(int256(deque._begin) + SafeCast.toInt256(index)); + if (idx >= deque._end) revert OutOfBounds(); + return deque._data[idx]; + } + + /** + * @dev Resets the queue back to being empty. + * + * NOTE: The current items are left behind in storage. This does not affect the functioning of the queue, but misses + * out on potential gas refunds. + */ + function clear(Bytes32Deque storage deque) internal { + deque._begin = 0; + deque._end = 0; + } + + /** + * @dev Returns the number of items in the queue. + */ + function length(Bytes32Deque storage deque) internal view returns (uint256) { + // The interface preserves the invariant that begin <= end so we assume this will not overflow. + // We also assume there are at most int256.max items in the queue. + unchecked { + return uint256(int256(deque._end) - int256(deque._begin)); + } + } + + /** + * @dev Returns true if the queue is empty. + */ + function empty(Bytes32Deque storage deque) internal view returns (bool) { + return deque._end <= deque._begin; + } +} diff --git a/certora/munged/utils/structs/EnumerableMap.sol b/certora/munged/utils/structs/EnumerableMap.sol new file mode 100644 index 000000000..1c3f4357e --- /dev/null +++ b/certora/munged/utils/structs/EnumerableMap.sol @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableMap.sol) + +pragma solidity ^0.8.0; + +import "./EnumerableSet.sol"; + +/** + * @dev Library for managing an enumerable variant of Solidity's + * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] + * type. + * + * Maps have the following properties: + * + * - Entries are added, removed, and checked for existence in constant time + * (O(1)). + * - Entries are enumerated in O(n). No guarantees are made on the ordering. + * + * ``` + * contract Example { + * // Add the library methods + * using EnumerableMap for EnumerableMap.UintToAddressMap; + * + * // Declare a set state variable + * EnumerableMap.UintToAddressMap private myMap; + * } + * ``` + * + * The following map types are supported: + * + * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0 + * - `address -> uint256` (`AddressToUintMap`) since v4.6.0 + */ +library EnumerableMap { + using EnumerableSet for EnumerableSet.Bytes32Set; + + // To implement this library for multiple types with as little code + // repetition as possible, we write it in terms of a generic Map type with + // bytes32 keys and values. + // The Map implementation uses private functions, and user-facing + // implementations (such as Uint256ToAddressMap) are just wrappers around + // the underlying Map. + // This means that we can only create new EnumerableMaps for types that fit + // in bytes32. + + struct Map { + // Storage of keys + EnumerableSet.Bytes32Set _keys; + mapping(bytes32 => bytes32) _values; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function _set( + Map storage map, + bytes32 key, + bytes32 value + ) private returns (bool) { + map._values[key] = value; + return map._keys.add(key); + } + + /** + * @dev Removes a key-value pair from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function _remove(Map storage map, bytes32 key) private returns (bool) { + delete map._values[key]; + return map._keys.remove(key); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function _contains(Map storage map, bytes32 key) private view returns (bool) { + return map._keys.contains(key); + } + + /** + * @dev Returns the number of key-value pairs in the map. O(1). + */ + function _length(Map storage map) private view returns (uint256) { + return map._keys.length(); + } + + /** + * @dev Returns the key-value pair stored at position `index` in the map. O(1). + * + * Note that there are no guarantees on the ordering of entries inside the + * array, and it may change when more entries are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) { + bytes32 key = map._keys.at(index); + return (key, map._values[key]); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) { + bytes32 value = map._values[key]; + if (value == bytes32(0)) { + return (_contains(map, key), bytes32(0)); + } else { + return (true, value); + } + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function _get(Map storage map, bytes32 key) private view returns (bytes32) { + bytes32 value = map._values[key]; + require(value != 0 || _contains(map, key), "EnumerableMap: nonexistent key"); + return value; + } + + /** + * @dev Same as {_get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {_tryGet}. + */ + function _get( + Map storage map, + bytes32 key, + string memory errorMessage + ) private view returns (bytes32) { + bytes32 value = map._values[key]; + require(value != 0 || _contains(map, key), errorMessage); + return value; + } + + // UintToAddressMap + + struct UintToAddressMap { + Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set( + UintToAddressMap storage map, + uint256 key, + address value + ) internal returns (bool) { + return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value)))); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { + return _remove(map._inner, bytes32(key)); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { + return _contains(map._inner, bytes32(key)); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(UintToAddressMap storage map) internal view returns (uint256) { + return _length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) { + (bytes32 key, bytes32 value) = _at(map._inner, index); + return (uint256(key), address(uint160(uint256(value)))); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + * + * _Available since v3.4._ + */ + function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) { + (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key)); + return (success, address(uint160(uint256(value)))); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { + return address(uint160(uint256(_get(map._inner, bytes32(key))))); + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + UintToAddressMap storage map, + uint256 key, + string memory errorMessage + ) internal view returns (address) { + return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage)))); + } + + // AddressToUintMap + + struct AddressToUintMap { + Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set( + AddressToUintMap storage map, + address key, + uint256 value + ) internal returns (bool) { + return _set(map._inner, bytes32(uint256(uint160(key))), bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(AddressToUintMap storage map, address key) internal returns (bool) { + return _remove(map._inner, bytes32(uint256(uint160(key)))); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(AddressToUintMap storage map, address key) internal view returns (bool) { + return _contains(map._inner, bytes32(uint256(uint160(key)))); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(AddressToUintMap storage map) internal view returns (uint256) { + return _length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) { + (bytes32 key, bytes32 value) = _at(map._inner, index); + return (address(uint160(uint256(key))), uint256(value)); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + * + * _Available since v3.4._ + */ + function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) { + (bool success, bytes32 value) = _tryGet(map._inner, bytes32(uint256(uint160(key)))); + return (success, uint256(value)); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(AddressToUintMap storage map, address key) internal view returns (uint256) { + return uint256(_get(map._inner, bytes32(uint256(uint160(key))))); + } +} diff --git a/certora/munged/utils/structs/EnumerableSet.sol b/certora/munged/utils/structs/EnumerableSet.sol new file mode 100644 index 000000000..68148e944 --- /dev/null +++ b/certora/munged/utils/structs/EnumerableSet.sol @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Library for managing + * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive + * types. + * + * Sets have the following properties: + * + * - Elements are added, removed, and checked for existence in constant time + * (O(1)). + * - Elements are enumerated in O(n). No guarantees are made on the ordering. + * + * ``` + * contract Example { + * // Add the library methods + * using EnumerableSet for EnumerableSet.AddressSet; + * + * // Declare a set state variable + * EnumerableSet.AddressSet private mySet; + * } + * ``` + * + * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) + * and `uint256` (`UintSet`) are supported. + */ +library EnumerableSet { + // To implement this library for multiple types with as little code + // repetition as possible, we write it in terms of a generic Set type with + // bytes32 values. + // The Set implementation uses private functions, and user-facing + // implementations (such as AddressSet) are just wrappers around the + // underlying Set. + // This means that we can only create new EnumerableSets for types that fit + // in bytes32. + + struct Set { + // Storage of set values + bytes32[] _values; + // Position of the value in the `values` array, plus 1 because index 0 + // means a value is not in the set. + mapping(bytes32 => uint256) _indexes; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function _add(Set storage set, bytes32 value) private returns (bool) { + if (!_contains(set, value)) { + set._values.push(value); + // The value is stored at length-1, but we add 1 to all indexes + // and use 0 as a sentinel value + set._indexes[value] = set._values.length; + return true; + } else { + return false; + } + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function _remove(Set storage set, bytes32 value) private returns (bool) { + // We read and store the value's index to prevent multiple reads from the same storage slot + uint256 valueIndex = set._indexes[value]; + + if (valueIndex != 0) { + // Equivalent to contains(set, value) + // To delete an element from the _values array in O(1), we swap the element to delete with the last one in + // the array, and then remove the last element (sometimes called as 'swap and pop'). + // This modifies the order of the array, as noted in {at}. + + uint256 toDeleteIndex = valueIndex - 1; + uint256 lastIndex = set._values.length - 1; + + if (lastIndex != toDeleteIndex) { + bytes32 lastvalue = set._values[lastIndex]; + + // Move the last value to the index where the value to delete is + set._values[toDeleteIndex] = lastvalue; + // Update the index for the moved value + set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex + } + + // Delete the slot where the moved value was stored + set._values.pop(); + + // Delete the index for the deleted slot + delete set._indexes[value]; + + return true; + } else { + return false; + } + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function _contains(Set storage set, bytes32 value) private view returns (bool) { + return set._indexes[value] != 0; + } + + /** + * @dev Returns the number of values on the set. O(1). + */ + function _length(Set storage set) private view returns (uint256) { + return set._values.length; + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function _at(Set storage set, uint256 index) private view returns (bytes32) { + return set._values[index]; + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function _values(Set storage set) private view returns (bytes32[] memory) { + return set._values; + } + + // Bytes32Set + + struct Bytes32Set { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { + return _add(set._inner, value); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { + return _remove(set._inner, value); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { + return _contains(set._inner, value); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(Bytes32Set storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { + return _at(set._inner, index); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { + return _values(set._inner); + } + + // AddressSet + + struct AddressSet { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(AddressSet storage set, address value) internal returns (bool) { + return _add(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(AddressSet storage set, address value) internal returns (bool) { + return _remove(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(AddressSet storage set, address value) internal view returns (bool) { + return _contains(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(AddressSet storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(AddressSet storage set, uint256 index) internal view returns (address) { + return address(uint160(uint256(_at(set._inner, index)))); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(AddressSet storage set) internal view returns (address[] memory) { + bytes32[] memory store = _values(set._inner); + address[] memory result; + + assembly { + result := store + } + + return result; + } + + // UintSet + + struct UintSet { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(UintSet storage set, uint256 value) internal returns (bool) { + return _add(set._inner, bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(UintSet storage set, uint256 value) internal returns (bool) { + return _remove(set._inner, bytes32(value)); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(UintSet storage set, uint256 value) internal view returns (bool) { + return _contains(set._inner, bytes32(value)); + } + + /** + * @dev Returns the number of values on the set. O(1). + */ + function length(UintSet storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintSet storage set, uint256 index) internal view returns (uint256) { + return uint256(_at(set._inner, index)); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(UintSet storage set) internal view returns (uint256[] memory) { + bytes32[] memory store = _values(set._inner); + uint256[] memory result; + + assembly { + result := store + } + + return result; + } +} diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index b6cdb4ec3..84d76159e 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -1,14 +1,35 @@ -make -C certora munged +# make -C certora munged -for f in certora/harnesses/Wizard*.sol -do - echo "Processing $f" - file=$(basename $f) - echo ${file%.*} - certoraRun certora/harnesses/$file \ - --verify ${file%.*}:certora/specs/sanity.spec "$@" \ - --solc solc8.2 --staging shelly/forSasha \ +# for f in certora/harnesses/Wizard*.sol +# do +# echo "Processing $f" +# file=$(basename $f) +# echo ${file%.*} +# certoraRun certora/harnesses/$file \ +# --verify ${file%.*}:certora/specs/sanity.spec "$@" \ +# --solc solc8.2 --staging shelly/forSasha \ +# --optimistic_loop \ +# --msg "checking sanity on ${file%.*}" +# --settings -copyLoopUnroll=4 +# done + +# TimelockController +# certoraRun \ +# certora/harnesses/TimelockControllerHarness.sol \ +# --verify TimelockControllerHarness:certora/specs/sanity.spec \ +# --solc solc8.2 \ +# --optimistic_loop \ +# --cloud \ +# --msg "sanity" + + +# Votes +certoraRun \ + certora/harnesses/VotesHarness.sol \ + --verify VotesHarness:certora/specs/sanity.spec \ + --solc solc8.2 \ --optimistic_loop \ - --msg "checking sanity on ${file%.*}" - --settings -copyLoopUnroll=4 -done + --cloud \ + --settings -strictDecompiler=false,-assumeUnwindCond \ + --msg "sanityVotes" + From 61fa061ecf805257c754baa8e02561b08f170188 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 8 Mar 2022 16:38:11 +0000 Subject: [PATCH 141/254] erc20votes pointers workaround and preset --- .../munged/governance/TimelockController.sol | 2 +- .../token/ERC20/extensions/ERC20Votes.sol | 49 ++++++++++++++++--- certora/scripts/sanity.sh | 28 +++++------ certora/scripts/verifyERC20Votes.sh | 8 +++ certora/scripts/verifyTimelock.sh | 7 +++ certora/specs/ERC20Votes.spec | 6 +++ certora/specs/TimelockController.spec | 36 ++++++++++++++ certora/specs/sanity.spec | 3 +- 8 files changed, 115 insertions(+), 24 deletions(-) create mode 100644 certora/scripts/verifyERC20Votes.sh create mode 100644 certora/scripts/verifyTimelock.sh create mode 100644 certora/specs/ERC20Votes.spec create mode 100644 certora/specs/TimelockController.spec diff --git a/certora/munged/governance/TimelockController.sol b/certora/munged/governance/TimelockController.sol index c375f0744..621ebd87b 100644 --- a/certora/munged/governance/TimelockController.sol +++ b/certora/munged/governance/TimelockController.sol @@ -353,4 +353,4 @@ contract TimelockController is AccessControl { emit MinDelayChange(_minDelay, newDelay); _minDelay = newDelay; } -} +} diff --git a/certora/munged/token/ERC20/extensions/ERC20Votes.sol b/certora/munged/token/ERC20/extensions/ERC20Votes.sol index c0e88bc19..be127239b 100644 --- a/certora/munged/token/ERC20/extensions/ERC20Votes.sol +++ b/certora/munged/token/ERC20/extensions/ERC20Votes.sol @@ -163,7 +163,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { super._mint(account, amount); require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); - _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); + _writeCheckpointAdd(_totalSupplyCheckpoints, amount); // HARNESS: new version without pointer } /** @@ -172,7 +172,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { function _burn(address account, uint256 amount) internal virtual override { super._burn(account, amount); - _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); + _writeCheckpointSub(_totalSupplyCheckpoints, amount); // HARNESS: new version without pointer } /** @@ -187,7 +187,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { ) internal virtual override { super._afterTokenTransfer(from, to, amount); - _moveVotingPower(delegates(from), delegates(to), amount); + _moveVotingPower(delegates(from), delegates(to), amount); } /** @@ -212,25 +212,25 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { ) private { if (src != dst && amount > 0) { if (src != address(0)) { - (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); + (uint256 oldWeight, uint256 newWeight) = _writeCheckpointSub(_checkpoints[src], amount); // HARNESS: new version without pointer emit DelegateVotesChanged(src, oldWeight, newWeight); } if (dst != address(0)) { - (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); + (uint256 oldWeight, uint256 newWeight) = _writeCheckpointAdd(_checkpoints[dst], amount); // HARNESS: new version without pointer emit DelegateVotesChanged(dst, oldWeight, newWeight); } } } - function _writeCheckpoint( + // HARNESS: split _writeCheckpoint() to two functions as a workaround for function pointers that cannot be managed by the tool + function _writeCheckpointAdd( Checkpoint[] storage ckpts, - function(uint256, uint256) view returns (uint256) op, uint256 delta ) private returns (uint256 oldWeight, uint256 newWeight) { uint256 pos = ckpts.length; oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; - newWeight = op(oldWeight, delta); + newWeight = _add(oldWeight, delta); if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); @@ -239,6 +239,39 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { } } + function _writeCheckpointSub( + Checkpoint[] storage ckpts, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; + newWeight = _subtract(oldWeight, delta); + + if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { + ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); + } else { + ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + } + } + + // backup of original function + // + // function _writeCheckpoint( + // Checkpoint[] storage ckpts, + // function(uint256, uint256) view returns (uint256) op, + // uint256 delta + // ) private returns (uint256 oldWeight, uint256 newWeight) { + // uint256 pos = ckpts.length; + // oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; + // newWeight = op(oldWeight, delta); + // + // if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { + // ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); + // } else { + // ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + // } + // } + function _add(uint256 a, uint256 b) private pure returns (uint256) { return a + b; } diff --git a/certora/scripts/sanity.sh b/certora/scripts/sanity.sh index 84d76159e..afead788d 100644 --- a/certora/scripts/sanity.sh +++ b/certora/scripts/sanity.sh @@ -14,22 +14,22 @@ # done # TimelockController -# certoraRun \ -# certora/harnesses/TimelockControllerHarness.sol \ -# --verify TimelockControllerHarness:certora/specs/sanity.spec \ -# --solc solc8.2 \ -# --optimistic_loop \ -# --cloud \ -# --msg "sanity" - - -# Votes certoraRun \ - certora/harnesses/VotesHarness.sol \ - --verify VotesHarness:certora/specs/sanity.spec \ + certora/harnesses/TimelockControllerHarness.sol \ + --verify TimelockControllerHarness:certora/specs/sanity.spec \ --solc solc8.2 \ --optimistic_loop \ --cloud \ - --settings -strictDecompiler=false,-assumeUnwindCond \ - --msg "sanityVotes" + --msg "sanity and keccak check" + + +# Votes +# certoraRun \ +# certora/harnesses/VotesHarness.sol \ +# --verify VotesHarness:certora/specs/sanity.spec \ +# --solc solc8.2 \ +# --optimistic_loop \ +# --cloud \ +# --settings -strictDecompiler=false,-assumeUnwindCond \ +# --msg "sanityVotes" diff --git a/certora/scripts/verifyERC20Votes.sh b/certora/scripts/verifyERC20Votes.sh new file mode 100644 index 000000000..5d70e1f25 --- /dev/null +++ b/certora/scripts/verifyERC20Votes.sh @@ -0,0 +1,8 @@ +certoraRun \ + certora/harnesses/ERC20VotesHarness.sol \ + --verify ERC20VotesHarness:certora/specs/ERC20Votes.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --cloud \ + --settings -strictDecompiler=false,-assumeUnwindCond \ + --msg "sanityVotes" diff --git a/certora/scripts/verifyTimelock.sh b/certora/scripts/verifyTimelock.sh new file mode 100644 index 000000000..e8ac5548b --- /dev/null +++ b/certora/scripts/verifyTimelock.sh @@ -0,0 +1,7 @@ +certoraRun \ + certora/harnesses/TimelockControllerHarness.sol \ + --verify TimelockControllerHarness:certora/specs/TimelockController.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --cloud \ + --msg "sanity" \ No newline at end of file diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec new file mode 100644 index 000000000..b08c6cf4e --- /dev/null +++ b/certora/specs/ERC20Votes.spec @@ -0,0 +1,6 @@ +rule sanity(method f) { + env e; + calldataarg arg; + f(e, arg); + assert false; +} \ No newline at end of file diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec new file mode 100644 index 000000000..9c0b99c5f --- /dev/null +++ b/certora/specs/TimelockController.spec @@ -0,0 +1,36 @@ +methods { + // hashOperation(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt) returns(bytes32) => uniqueHashGhost(target, value, data, predecessor, salt) +} + +// ghost uniqueHashGhost(address, uint256, bytes, bytes32, bytes32) returns bytes32; +// +// Assuming the hash is deterministic, and correlates the trio properly +// function hashUniquness(address target1, uint256 value1, bytes data1, bytes32 predecessor1, bytes32 salt1, +// address target2, uint256 value2, bytes data2, bytes32 predecessor2, bytes32 salt2){ +// require ((target1 != target2) || (value1 != value2) || (data1 != data2) || (predecessor1 != predecessor2) || (salt1 != salt2)) <=> +// (uniqueHashGhost(target1, value1, data1, predecessor1, salt1) != uniqueHashGhost(target2, value2, data2, predecessor2, salt2)); +// } +// +// +// rule keccakCheck(method f, env e){ +// address target; +// uint256 value; +// bytes data; +// bytes32 predecessor; +// bytes32 salt; +// +// hashUniquness(target, value, data, predecessor, salt, +// target, value, data, predecessor, salt); +// +// bytes32 a = hashOperation(e, target, value, data, predecessor, salt); +// bytes32 b = hashOperation(e, target, value, data, predecessor, salt); +// +// assert a == b, "hashes are different"; +// } + +rule sanity(method f) { + env e; + calldataarg arg; + f(e, arg); + assert false; +} \ No newline at end of file diff --git a/certora/specs/sanity.spec b/certora/specs/sanity.spec index e08f68845..bd184841a 100644 --- a/certora/specs/sanity.spec +++ b/certora/specs/sanity.spec @@ -5,7 +5,8 @@ How it works: - If there is a non-reverting execution path, we reach the false assertion, and the sanity fails. - If all execution paths are reverting, we never call the assertion, and the method will pass this rule vacuously. */ - + + rule sanity(method f) { env e; calldataarg arg; From 3cb87abec13dbca0ec7c51021188c0cd7d0966f5 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 8 Mar 2022 19:20:55 +0000 Subject: [PATCH 142/254] removed flags for erc20Votes script --- certora/scripts/verifyERC20Votes.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/certora/scripts/verifyERC20Votes.sh b/certora/scripts/verifyERC20Votes.sh index 5d70e1f25..bb8c8e749 100644 --- a/certora/scripts/verifyERC20Votes.sh +++ b/certora/scripts/verifyERC20Votes.sh @@ -4,5 +4,4 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --cloud \ - --settings -strictDecompiler=false,-assumeUnwindCond \ --msg "sanityVotes" From 1900c86c99c651c5a45c98248c816259e79462ec Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Tue, 8 Mar 2022 19:58:23 +0000 Subject: [PATCH 143/254] removed unnecessary harness from the first iteration --- certora/harnesses/ERC20VotesHarness.sol | 23 ----------------------- certora/specs/GovernorCountingSimple.spec | 4 ---- 2 files changed, 27 deletions(-) diff --git a/certora/harnesses/ERC20VotesHarness.sol b/certora/harnesses/ERC20VotesHarness.sol index 5067ecfba..3bf03732f 100644 --- a/certora/harnesses/ERC20VotesHarness.sol +++ b/certora/harnesses/ERC20VotesHarness.sol @@ -2,27 +2,4 @@ import "../munged/token/ERC20/extensions/ERC20Votes.sol"; contract ERC20VotesHarness is ERC20Votes { constructor(string memory name, string memory symbol) ERC20Permit(name) ERC20(name, symbol) {} - - mapping(address => mapping(uint256 => uint256)) public _getPastVotes; - - function _afterTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual override { - super._afterTokenTransfer(from, to, amount); - _getPastVotes[from][block.number] -= amount; - _getPastVotes[to][block.number] += amount; - } - - /** - * @dev Change delegation for `delegator` to `delegatee`. - * - * Emits events {DelegateChanged} and {DelegateVotesChanged}. - */ - function _delegate(address delegator, address delegatee) internal virtual override{ - super._delegate(delegator, delegatee); - _getPastVotes[delegator][block.number] -= balanceOf(delegator); - _getPastVotes[delegatee][block.number] += balanceOf(delegator); - } } diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 6d2d5fbb7..c5dcecddf 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -1,7 +1,5 @@ import "GovernorBase.spec" -using ERC20VotesHarness as erc20votes - methods { ghost_sum_vote_power_by_id(uint256) returns uint256 envfree @@ -11,8 +9,6 @@ methods { quorumNumerator() returns uint256 _executor() returns address - erc20votes._getPastVotes(address, uint256) returns uint256 - getExecutor() returns address timelock() returns address From 7caa9bbb2c61ccded0fb05819c629034cb2c9323 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Fri, 11 Mar 2022 00:29:03 +0000 Subject: [PATCH 144/254] TimelockController wating for hash fix --- certora/harnesses/AccessControlHarness.sol | 10 + certora/munged/access/AccessControl.sol | 2 +- .../munged/governance/TimelockController.sol | 4 +- certora/scripts/verifyTimelock.sh | 8 +- certora/specs/TimelockController.spec | 349 ++++++++++++++++-- 5 files changed, 336 insertions(+), 37 deletions(-) create mode 100644 certora/harnesses/AccessControlHarness.sol diff --git a/certora/harnesses/AccessControlHarness.sol b/certora/harnesses/AccessControlHarness.sol new file mode 100644 index 000000000..3ae6e7e8c --- /dev/null +++ b/certora/harnesses/AccessControlHarness.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol) + +pragma solidity ^0.8.0; + +import "../munged/access/AccessControl.sol"; + +contract AccessControlHarness is AccessControl { + +} diff --git a/certora/munged/access/AccessControl.sol b/certora/munged/access/AccessControl.sol index 2ac203867..dec22fe8e 100644 --- a/certora/munged/access/AccessControl.sol +++ b/certora/munged/access/AccessControl.sol @@ -93,7 +93,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 { * * _Available since v4.6._ */ - function _checkRole(bytes32 role) internal view virtual { + function _checkRole(bytes32 role) public view virtual { // HARNESS: internal -> public _checkRole(role, _msgSender()); } diff --git a/certora/munged/governance/TimelockController.sol b/certora/munged/governance/TimelockController.sol index 621ebd87b..da38df8f3 100644 --- a/certora/munged/governance/TimelockController.sol +++ b/certora/munged/governance/TimelockController.sol @@ -24,10 +24,10 @@ contract TimelockController is AccessControl { bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); - uint256 internal constant _DONE_TIMESTAMP = uint256(1); + uint256 public constant _DONE_TIMESTAMP = uint256(1); // HARNESS: internal -> public mapping(bytes32 => uint256) private _timestamps; - uint256 private _minDelay; + uint256 public _minDelay; // HARNESS: private -> public /** * @dev Emitted when a call is scheduled as part of operation `id`. diff --git a/certora/scripts/verifyTimelock.sh b/certora/scripts/verifyTimelock.sh index e8ac5548b..696ab2595 100644 --- a/certora/scripts/verifyTimelock.sh +++ b/certora/scripts/verifyTimelock.sh @@ -1,7 +1,9 @@ certoraRun \ - certora/harnesses/TimelockControllerHarness.sol \ + certora/harnesses/TimelockControllerHarness.sol certora/harnesses/AccessControlHarness.sol \ --verify TimelockControllerHarness:certora/specs/TimelockController.spec \ --solc solc8.2 \ --optimistic_loop \ - --cloud \ - --msg "sanity" \ No newline at end of file + --cloud alex/uhf-more-precision \ + --rule_sanity \ + --msg "sanity flag check" + \ No newline at end of file diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec index 9c0b99c5f..b8a15cbe3 100644 --- a/certora/specs/TimelockController.spec +++ b/certora/specs/TimelockController.spec @@ -1,36 +1,323 @@ +using AccessControlHarness as AC + methods { - // hashOperation(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt) returns(bytes32) => uniqueHashGhost(target, value, data, predecessor, salt) + getTimestamp(bytes32) returns(uint256) envfree + _DONE_TIMESTAMP() returns(uint256) envfree + _minDelay() returns(uint256) envfree + getMinDelay() returns(uint256) envfree + + cancel(bytes32) + schedule(address, uint256, bytes, bytes32, bytes32, uint256) + execute(address, uint256, bytes, bytes32, bytes32) + + hashOperation(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt) returns(bytes32) envfree // => uniqueHashGhost(target, value, data, predecessor, salt) } -// ghost uniqueHashGhost(address, uint256, bytes, bytes32, bytes32) returns bytes32; -// -// Assuming the hash is deterministic, and correlates the trio properly -// function hashUniquness(address target1, uint256 value1, bytes data1, bytes32 predecessor1, bytes32 salt1, -// address target2, uint256 value2, bytes data2, bytes32 predecessor2, bytes32 salt2){ -// require ((target1 != target2) || (value1 != value2) || (data1 != data2) || (predecessor1 != predecessor2) || (salt1 != salt2)) <=> -// (uniqueHashGhost(target1, value1, data1, predecessor1, salt1) != uniqueHashGhost(target2, value2, data2, predecessor2, salt2)); -// } -// -// -// rule keccakCheck(method f, env e){ -// address target; -// uint256 value; -// bytes data; -// bytes32 predecessor; -// bytes32 salt; -// -// hashUniquness(target, value, data, predecessor, salt, -// target, value, data, predecessor, salt); -// -// bytes32 a = hashOperation(e, target, value, data, predecessor, salt); -// bytes32 b = hashOperation(e, target, value, data, predecessor, salt); -// -// assert a == b, "hashes are different"; -// } -rule sanity(method f) { - env e; - calldataarg arg; - f(e, arg); - assert false; + +//////////////////////////////////////////////////////////////////////////// +// Definitions // +//////////////////////////////////////////////////////////////////////////// + + +definition unset(bytes32 id) returns bool = + getTimestamp(id) == 0; + +definition pending(bytes32 id) returns bool = + getTimestamp(id) > _DONE_TIMESTAMP(); + +definition ready(bytes32 id, env e) returns bool = + getTimestamp(id) > _DONE_TIMESTAMP() && getTimestamp(id) <= e.block.timestamp; + +definition done(bytes32 id) returns bool = + getTimestamp(id) == _DONE_TIMESTAMP(); + + + +//////////////////////////////////////////////////////////////////////////// +// Functions // +//////////////////////////////////////////////////////////////////////////// + + +function hashIdCorrelation(bytes32 id, address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt){ + require data.length < 3; + require hashOperation(target, value, data, predecessor, salt) == id; +} + + +//////////////////////////////////////////////////////////////////////////// +// Ghosts // +//////////////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////////////// +// Invariants // +//////////////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////////////// +// Rules // +//////////////////////////////////////////////////////////////////////////// + + +rule keccakCheck(method f, env e){ + address target; + uint256 value; + bytes data; + bytes32 predecessor; + bytes32 salt; + + require data.length < 3; + + bytes32 a = hashOperation(target, value, data, predecessor, salt); + bytes32 b = hashOperation(target, value, data, predecessor, salt); + + assert a == b, "hashes are different"; +} + + + +///////////////////////////////////////////////////////////// +// STATE TRANSITIONS +///////////////////////////////////////////////////////////// + + +// STATUS - verified +// unset() -> unset() || pending() only +rule unsetPendingTransitionGeneral(method f, env e){ + bytes32 id; + + require unset(id); + require e.block.timestamp > 1; + + calldataarg args; + f(e, args); + + assert pending(id) || unset(id); +} + + +// STATUS - verified +// unset() -> pending() via schedule() and scheduleBatch() only +rule unsetPendingTransitionMethods(method f, env e){ + bytes32 id; + + require unset(id); + + calldataarg args; + f(e, args); + + assert pending(id) => f.selector == schedule(address, uint256, bytes, bytes32, bytes32, uint256).selector + || f.selector == scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256).selector , "Why do we need to follow the schedule?"; +} + + +// STATUS - verified +// ready() -> done() via execute() and executeBatch() only +rule readyDoneTransition(method f, env e){ + bytes32 id; + + require ready(id, e); + + calldataarg args; + f(e, args); + + assert done(id) => f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector + || f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector , "It's not done yet!"; +} + + +// STATUS - verified +// pending() -> cancelled() via cancel() only +rule pendingCancelledTransition(method f, env e){ + bytes32 id; + + require pending(id); + + calldataarg args; + f(e, args); + + assert unset(id) => f.selector == cancel(bytes32).selector, "How you dare to cancel me?"; +} + + +// STATUS - verified +// done() -> nowhere +rule doneToNothingTransition(method f, env e){ + bytes32 id; + + require done(id); + + calldataarg args; + f(e, args); + + assert done(id), "Did you find a way to escape? There is no way! HA-HA-HA"; +} + + + +///////////////////////////////////////////////////////////// +// THE REST +///////////////////////////////////////////////////////////// + + +// STATUS - verified +// only TimelockController contract can change minDealy +rule minDealyOnlyChange(method f, env e){ + uint256 delayBefore = _minDelay(); + + calldataarg args; + f(e, args); + + uint256 delayAfter = _minDelay(); + + assert delayBefore != delayAfter => e.msg.sender == currentContract, "You cannot change your destiny! Only I can!"; +} + + +// STATUS - verified +// Only proposers can schedule an operation +rule scheduleOnlyWay(method f, env e){ + uint256 delayBefore = _minDelay(); + + calldataarg args; + f(e, args); + + uint256 delayAfter = _minDelay(); + + assert delayBefore != delayAfter => e.msg.sender == currentContract, "You cannot change your destiny! Only I can!"; +} + + +// STATUS - in progress (need working hash) +// execute() is the only way to set timestamp to 1 +rule getTimestampOnlyChange(method f, env e){ + bytes32 id; + address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; + + require getTimestamp(id) != 1; + hashIdCorrelation(id, target, value, data, predecessor, salt); + + calldataarg args; + // write helper function with values from hashOperation() call; + f(e, args); + + assert getTimestamp(id) == 1 => f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector + || f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector , "Did you find a way to break the system?"; +} + + +// STATUS - in progress (need working hash) +// scheduled operation timestamp == block.timestamp + delay (kind of unit test) +rule scheduleCheck(method f, env e){ + bytes32 id; + + address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; + + require getTimestamp(id) < e.block.timestamp; + // require getMinDelay() > 0; + hashIdCorrelation(id, target, value, data, predecessor, salt); + + schedule(e, target, value, data, predecessor, salt, delay); + + assert getTimestamp(id) == to_uint256(e.block.timestamp + getMinDelay()), "Time doesn't obey to mortal souls"; +} + + +// STATUS - in progress (need working hash) +// Cannot call execute on a pending (not ready) operation +rule cannotCallExecute(method f, env e){ + address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; + bytes32 id; + + hashIdCorrelation(id, target, value, data, predecessor, salt); + require pending(id) && !ready(id, e); + + execute@withrevert(e, target, value, data, predecessor, salt); + + assert lastReverted, "you go against execution nature"; +} + + +// STATUS - in progress (need working hash) +// in unset() execute() reverts +rule executeRevertFromUnset(method f, env e){ + address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; + bytes32 id; + + hashIdCorrelation(id, target, value, data, predecessor, salt); + require unset(id); + + execute@withrevert(e, target, value, data, predecessor, salt); + + assert lastReverted, "you go against execution nature"; +} + + +// STATUS - verified +// Execute reverts => state returns to pending +rule executeRevertEffectCheck(method f, env e){ + address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; + bytes32 id; + + hashIdCorrelation(id, target, value, data, predecessor, salt); + require pending(id) && !ready(id, e); + + execute@withrevert(e, target, value, data, predecessor, salt); + + assert lastReverted => pending(id) && !ready(id, e), "you go against execution nature"; +} + + +// STATUS - verified +// Canceled operations cannot be executed → can’t move from canceled to ready +rule cancelledNotExecuted(method f, env e){ + bytes32 id; + + require unset(id); + require e.block.timestamp > 1; + + calldataarg args; + f(e, args); + + assert !done(id), "The ship is not a creature of the air"; +} + + +// STATUS - in progress (need working hash) +// Only proposers can schedule an operation +rule onlyProposer(method f, env e){ + bytes32 id; + bytes32 role; + address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; + + require unset(id); + hashIdCorrelation(id, target, value, data, predecessor, salt); + + AC._checkRole@withrevert(e, role); + + bool isCheckRoleReverted = lastReverted; + + schedule@withrevert(e, target, value, data, predecessor, salt, delay); + + bool isScheduleReverted = lastReverted; + + assert isCheckRoleReverted => isScheduleReverted, "Enemy was detected"; +} + + +// STATUS - in progress +// Ready = has waited minimum period after pending +rule cooldown(method f, env e, env e2){ + bytes32 id; + + require unset(id); + + calldataarg args; + f(e, args); + + // e.block.timestamp - delay > time scheduled => ready() + assert e.block.timestamp >= getTimestamp(id) => ready(id, e), "No rush! When I'm ready, I'm ready"; } \ No newline at end of file From 62d60a5890101ecb37dcafecc07234bf827ab091 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Sun, 20 Mar 2022 22:36:48 +0000 Subject: [PATCH 145/254] Timelock, erc20Wrapper and erc20FlashMint verification --- certora/harnesses/ERC20FlashMintHarness.sol | 5 + certora/harnesses/ERC20WrapperHarness.sol | 9 +- .../IERC3156FlashBorrowerHarness.sol | 20 ++ certora/helpers/DummyERC20A.sol | 5 + certora/helpers/DummyERC20B.sol | 5 + certora/helpers/DummyERC20Impl.sol | 57 +++++ .../munged/governance/TimelockController.sol | 6 + certora/munged/token/ERC20/ERC20.sol | 4 +- .../token/ERC20/extensions/ERC20FlashMint.sol | 6 +- .../token/ERC20/extensions/ERC20Wrapper.sol | 2 +- certora/scripts/verifyERC20FlashMint.sh | 10 + certora/scripts/verifyERC20Wrapper.sh | 10 + certora/scripts/verifyTimelock.sh | 5 +- certora/specs/ERC20FlashMint.spec | 37 ++++ certora/specs/ERC20Wrapper.spec | 203 ++++++++++++++++++ certora/specs/TimelockController.spec | 17 +- certora/specs/erc20.spec | 12 ++ 17 files changed, 400 insertions(+), 13 deletions(-) create mode 100644 certora/harnesses/ERC20FlashMintHarness.sol create mode 100644 certora/harnesses/IERC3156FlashBorrowerHarness.sol create mode 100644 certora/helpers/DummyERC20A.sol create mode 100644 certora/helpers/DummyERC20B.sol create mode 100644 certora/helpers/DummyERC20Impl.sol create mode 100644 certora/scripts/verifyERC20FlashMint.sh create mode 100644 certora/scripts/verifyERC20Wrapper.sh create mode 100644 certora/specs/ERC20FlashMint.spec create mode 100644 certora/specs/ERC20Wrapper.spec create mode 100644 certora/specs/erc20.spec diff --git a/certora/harnesses/ERC20FlashMintHarness.sol b/certora/harnesses/ERC20FlashMintHarness.sol new file mode 100644 index 000000000..aec8c2238 --- /dev/null +++ b/certora/harnesses/ERC20FlashMintHarness.sol @@ -0,0 +1,5 @@ +import "../munged/token/ERC20/extensions/ERC20FlashMint.sol"; + +contract ERC20FlashMintHarness is ERC20FlashMint { + constructor(string memory name, string memory symbol) ERC20(name, symbol) {} +} diff --git a/certora/harnesses/ERC20WrapperHarness.sol b/certora/harnesses/ERC20WrapperHarness.sol index 23e37df11..b000afde4 100644 --- a/certora/harnesses/ERC20WrapperHarness.sol +++ b/certora/harnesses/ERC20WrapperHarness.sol @@ -6,5 +6,12 @@ contract ERC20WrapperHarness is ERC20Wrapper { ERC20Wrapper(underlyingToken) ERC20(_name, _symbol) {} -} + function underlyingTotalSupply() public view returns (uint256) { + return underlying.totalSupply(); + } + + function underlyingBalanceOf(address account) public view returns (uint256) { + return underlying.balanceOf(account); + } +} \ No newline at end of file diff --git a/certora/harnesses/IERC3156FlashBorrowerHarness.sol b/certora/harnesses/IERC3156FlashBorrowerHarness.sol new file mode 100644 index 000000000..097a5c2fd --- /dev/null +++ b/certora/harnesses/IERC3156FlashBorrowerHarness.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashBorrower.sol) + +import "../munged/interfaces/IERC3156FlashBorrower.sol"; + +pragma solidity ^0.8.0; + +contract IERC3156FlashBorrowerHarness is IERC3156FlashBorrower { + bytes32 somethingToReturn; + + function onFlashLoan( + address initiator, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) external override returns (bytes32){ + return somethingToReturn; + } +} diff --git a/certora/helpers/DummyERC20A.sol b/certora/helpers/DummyERC20A.sol new file mode 100644 index 000000000..188b92608 --- /dev/null +++ b/certora/helpers/DummyERC20A.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; +import "./DummyERC20Impl.sol"; + +contract DummyERC20A is DummyERC20Impl {} \ No newline at end of file diff --git a/certora/helpers/DummyERC20B.sol b/certora/helpers/DummyERC20B.sol new file mode 100644 index 000000000..0f97f1efc --- /dev/null +++ b/certora/helpers/DummyERC20B.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; +import "./DummyERC20Impl.sol"; + +contract DummyERC20B is DummyERC20Impl {} \ No newline at end of file diff --git a/certora/helpers/DummyERC20Impl.sol b/certora/helpers/DummyERC20Impl.sol new file mode 100644 index 000000000..42e7f2302 --- /dev/null +++ b/certora/helpers/DummyERC20Impl.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; + +// with mint +contract DummyERC20Impl { + uint256 t; + mapping (address => uint256) b; + mapping (address => mapping (address => uint256)) a; + + string public name; + string public symbol; + uint public decimals; + + function myAddress() public returns (address) { + return address(this); + } + + function add(uint a, uint b) internal pure returns (uint256) { + uint c = a +b; + require (c >= a); + return c; + } + function sub(uint a, uint b) internal pure returns (uint256) { + require (a>=b); + return a-b; + } + + function totalSupply() external view returns (uint256) { + return t; + } + function balanceOf(address account) external view returns (uint256) { + return b[account]; + } + function transfer(address recipient, uint256 amount) external returns (bool) { + b[msg.sender] = sub(b[msg.sender], amount); + b[recipient] = add(b[recipient], amount); + return true; + } + function allowance(address owner, address spender) external view returns (uint256) { + return a[owner][spender]; + } + function approve(address spender, uint256 amount) external returns (bool) { + a[msg.sender][spender] = amount; + return true; + } + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool) { + b[sender] = sub(b[sender], amount); + b[recipient] = add(b[recipient], amount); + a[sender][msg.sender] = sub(a[sender][msg.sender], amount); + return true; + } +} \ No newline at end of file diff --git a/certora/munged/governance/TimelockController.sol b/certora/munged/governance/TimelockController.sol index da38df8f3..3da7e2170 100644 --- a/certora/munged/governance/TimelockController.sol +++ b/certora/munged/governance/TimelockController.sol @@ -353,4 +353,10 @@ contract TimelockController is AccessControl { emit MinDelayChange(_minDelay, newDelay); _minDelay = newDelay; } + + + + function scheduleCheck1(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) { + require(false); + } } diff --git a/certora/munged/token/ERC20/ERC20.sol b/certora/munged/token/ERC20/ERC20.sol index dc963af88..fec3327ea 100644 --- a/certora/munged/token/ERC20/ERC20.sol +++ b/certora/munged/token/ERC20/ERC20.sol @@ -277,7 +277,7 @@ contract ERC20 is Context, IERC20, IERC20Metadata { * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ - function _burn(address account, uint256 amount) internal virtual { + function _burn(address account, uint256 amount) public virtual returns (bool) { // HARNESS: internal -> public require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); @@ -292,6 +292,8 @@ contract ERC20 is Context, IERC20, IERC20Metadata { emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); + + return true; } /** diff --git a/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol b/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol index cbcf3b60f..2a811cacf 100644 --- a/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol +++ b/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol @@ -40,9 +40,11 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. amount; - return 0; + return fee; // HARNESS: made "return" nonzero } + uint256 public fee; // HARNESS: added it to simulate random fee amount + /** * @dev Performs a flash loan. New tokens are minted and sent to the * `receiver`, who is required to implement the {IERC3156FlashBorrower} @@ -70,7 +72,7 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { uint256 fee = flashFee(token, amount); _mint(address(receiver), amount); require( - receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, + receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, // HAVOC_ALL "ERC20FlashMint: invalid return value" ); uint256 currentAllowance = allowance(address(receiver), address(this)); diff --git a/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol b/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol index 32a3b830a..b7027441f 100644 --- a/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol +++ b/certora/munged/token/ERC20/extensions/ERC20Wrapper.sol @@ -44,7 +44,7 @@ abstract contract ERC20Wrapper is ERC20 { * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. */ - function _recover(address account) internal virtual returns (uint256) { + function _recover(address account) public virtual returns (uint256) { // HARNESS: internal -> public uint256 value = underlying.balanceOf(address(this)) - totalSupply(); _mint(account, value); return value; diff --git a/certora/scripts/verifyERC20FlashMint.sh b/certora/scripts/verifyERC20FlashMint.sh new file mode 100644 index 000000000..84ec58eff --- /dev/null +++ b/certora/scripts/verifyERC20FlashMint.sh @@ -0,0 +1,10 @@ +certoraRun \ + certora/harnesses/ERC20FlashMintHarness.sol certora/harnesses/IERC3156FlashBorrowerHarness.sol \ + certora/munged/token/ERC20/ERC20.sol certora/helpers/DummyERC20A.sol certora/helpers/DummyERC20B.sol \ + --verify ERC20FlashMintHarness:certora/specs/ERC20FlashMint.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --staging \ + --rule_sanity \ + --msg "letsWatchItBurns" + \ No newline at end of file diff --git a/certora/scripts/verifyERC20Wrapper.sh b/certora/scripts/verifyERC20Wrapper.sh new file mode 100644 index 000000000..e4557724f --- /dev/null +++ b/certora/scripts/verifyERC20Wrapper.sh @@ -0,0 +1,10 @@ +certoraRun \ + certora/harnesses/ERC20WrapperHarness.sol \ + certora/helpers/DummyERC20A.sol certora/helpers/DummyERC20B.sol \ + --verify ERC20WrapperHarness:certora/specs/ERC20Wrapper.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --staging \ + --rule_sanity \ + --msg "all check" + \ No newline at end of file diff --git a/certora/scripts/verifyTimelock.sh b/certora/scripts/verifyTimelock.sh index 696ab2595..718c13c60 100644 --- a/certora/scripts/verifyTimelock.sh +++ b/certora/scripts/verifyTimelock.sh @@ -3,7 +3,8 @@ certoraRun \ --verify TimelockControllerHarness:certora/specs/TimelockController.spec \ --solc solc8.2 \ --optimistic_loop \ - --cloud alex/uhf-more-precision \ + --staging alex/unify-hash-functions \ --rule_sanity \ - --msg "sanity flag check" + --rule "$1" \ + --msg "$1 false check with hash" \ No newline at end of file diff --git a/certora/specs/ERC20FlashMint.spec b/certora/specs/ERC20FlashMint.spec new file mode 100644 index 000000000..db8cddcad --- /dev/null +++ b/certora/specs/ERC20FlashMint.spec @@ -0,0 +1,37 @@ +import "erc20.spec" + +methods { + onFlashLoan(address, address, uint256, uint256, bytes) => HAVOC_ALL // HAVOC_ECF + + _burn(address account, uint256 amount) returns(bool) => specBurn(account, amount); +} + +ghost mapping(address => uint256) trackedBurnAmount; + +function specBurn(address account, uint256 amount) returns bool { // retuns needed to overcome current CVL limitations: "could not type expression "specBurn(account,amount)", message: A summary must return a simple type, but specBurn(account,amount) returns 'void'" + trackedBurnAmount[account] = amount; + return true; +} + +// ghost to save args that were passed to burn function +// summarize burn +// assert ghost == amount + fee + + +// STATUS - in progress +// HAVOC_ALL - everything is havoced => violation +// HAVOC_ECF - verified +// https://vaas-stg.certora.com/output/3106/8795450b626f2ca53a2b/?anonymousKey=dd774da10cc595e4e38357af9e4f50bf2c0cb02a +// fee + flashLoan amount is burned +rule letsWatchItBurns(env e){ + address receiver; address token; uint256 amount; bytes data; + require amount > 0; + + uint256 feeBefore = flashFee(e, token, amount); + + flashLoan(e, receiver, token, amount, data); + + uint256 burned = trackedBurnAmount[receiver]; + + assert to_mathint(amount + feeBefore) == burned, "cheater"; +} \ No newline at end of file diff --git a/certora/specs/ERC20Wrapper.spec b/certora/specs/ERC20Wrapper.spec new file mode 100644 index 000000000..6e40c61d2 --- /dev/null +++ b/certora/specs/ERC20Wrapper.spec @@ -0,0 +1,203 @@ +import "erc20.spec" + +methods { + underlying() returns(address) envfree + underlyingTotalSupply() returns(uint256) envfree + underlyingBalanceOf(address) returns(uint256) envfree + + depositFor(address, uint256) returns(bool) + withdrawTo(address, uint256) returns(bool) + _recover(address) returns(uint256) +} + + +// STATUS - verified +// totalsupply of wrapped should be less than or equal to underlying (assuming no transfer they should be equal) - solvency +invariant whatAboutTotal(env e) + totalSupply(e) <= underlyingTotalSupply() + filtered { f -> f.selector != certorafallback_0().selector } + { + preserved { + require underlyingBalanceOf(currentContract) <= underlyingTotalSupply(); + require underlying() != currentContract; + } + preserved depositFor(address account, uint256 amount) with (env e2){ + require underlyingBalanceOf(currentContract) <= underlyingTotalSupply(); + require totalSupply(e) + amount <= underlyingTotalSupply(); + require underlying() != currentContract; + } + } + + +// STATUS - verified +// check correct values update by depositFor() +rule depositForSpecBasic(env e){ + address account; uint256 amount; + + require e.msg.sender != currentContract; + require underlying() != currentContract; + + uint256 wrapperTotalBefore = totalSupply(e); + uint256 underlyingTotalBefore = underlyingTotalSupply(); + uint256 underlyingThisBalanceBefore = underlyingBalanceOf(currentContract); + + depositFor(e, account, amount); + + uint256 wrapperTotalAfter = totalSupply(e); + uint256 underlyingTotalAfter = underlyingTotalSupply(); + uint256 underlyingThisBalanceAfter = underlyingBalanceOf(currentContract); + + assert wrapperTotalBefore == wrapperTotalAfter - amount, "wrapper total wrong update"; + assert underlyingTotalBefore == underlyingTotalAfter, "underlying total was updated"; + assert underlyingThisBalanceBefore == underlyingThisBalanceAfter - amount, "underlying this balance wrong update"; +} + + +rule depositForSpecWrapper(env e){ + address account; uint256 amount; + + require e.msg.sender != currentContract; + require underlying() != currentContract; + + uint256 wrapperUserBalanceBefore = balanceOf(e, account); + uint256 wrapperSenderBalanceBefore = balanceOf(e, e.msg.sender); + + depositFor(e, account, amount); + + uint256 wrapperUserBalanceAfter = balanceOf(e, account); + uint256 wrapperSenderBalanceAfter = balanceOf(e, e.msg.sender); + + assert account == e.msg.sender => wrapperUserBalanceBefore == wrapperSenderBalanceBefore + && wrapperUserBalanceAfter == wrapperSenderBalanceAfter + && wrapperUserBalanceBefore == wrapperUserBalanceAfter - amount, "wrapper balances wrong update"; + assert account != e.msg.sender => wrapperUserBalanceBefore == wrapperUserBalanceAfter - amount + && wrapperSenderBalanceBefore == wrapperSenderBalanceAfter, "wrapper balances wrong update"; +} + + +rule depositForSpecUnderlying(env e){ + address account; uint256 amount; + + require e.msg.sender != currentContract; + require underlying() != currentContract; + + uint256 underlyingSenderBalanceBefore = underlyingBalanceOf(e.msg.sender); + uint256 underlyingUserBalanceBefore = underlyingBalanceOf(account); + + depositFor(e, account, amount); + + uint256 underlyingSenderBalanceAfter = underlyingBalanceOf(e.msg.sender); + uint256 underlyingUserBalanceAfter = underlyingBalanceOf(account); + + assert account == e.msg.sender => underlyingSenderBalanceBefore == underlyingUserBalanceBefore + && underlyingSenderBalanceAfter == underlyingUserBalanceAfter + && underlyingSenderBalanceBefore == underlyingSenderBalanceAfter + amount, "underlying balances wrong update"; + + assert account != e.msg.sender && account == currentContract => underlyingSenderBalanceBefore == underlyingSenderBalanceAfter + amount + && underlyingUserBalanceBefore == underlyingUserBalanceAfter - amount, "underlying balances wrong update"; + + assert account != e.msg.sender && account != currentContract => underlyingSenderBalanceBefore == underlyingSenderBalanceAfter + amount + && underlyingUserBalanceBefore == underlyingUserBalanceAfter, "underlying balances wrong update"; +} + + +// STATUS - verified +// check correct values update by withdrawTo() +rule withdrawToSpecBasic(env e){ + address account; uint256 amount; + + require e.msg.sender != currentContract; + require underlying() != currentContract; + + uint256 wrapperTotalBefore = totalSupply(e); + uint256 underlyingTotalBefore = underlyingTotalSupply(); + + withdrawTo(e, account, amount); + + uint256 wrapperTotalAfter = totalSupply(e); + uint256 underlyingTotalAfter = underlyingTotalSupply(); + + assert wrapperTotalBefore == wrapperTotalAfter + amount, "wrapper total wrong update"; + assert underlyingTotalBefore == underlyingTotalAfter, "underlying total was updated"; +} + + +rule withdrawToSpecWrapper(env e){ + address account; uint256 amount; + + require e.msg.sender != currentContract; + require underlying() != currentContract; + + uint256 wrapperUserBalanceBefore = balanceOf(e, account); + uint256 wrapperSenderBalanceBefore = balanceOf(e, e.msg.sender); + + withdrawTo(e, account, amount); + + uint256 wrapperUserBalanceAfter = balanceOf(e, account); + uint256 wrapperSenderBalanceAfter = balanceOf(e, e.msg.sender); + + assert account == e.msg.sender => wrapperUserBalanceBefore == wrapperSenderBalanceBefore + && wrapperUserBalanceAfter == wrapperSenderBalanceAfter + && wrapperUserBalanceBefore == wrapperUserBalanceAfter + amount, "wrapper user balance wrong update"; + assert account != e.msg.sender => wrapperSenderBalanceBefore == wrapperSenderBalanceAfter + amount + && wrapperUserBalanceBefore == wrapperUserBalanceAfter, "wrapper user balance wrong update"; +} + + +rule withdrawToSpecUnderlying(env e){ + address account; uint256 amount; + + require e.msg.sender != currentContract; + require underlying() != currentContract; + + uint256 underlyingSenderBalanceBefore = underlyingBalanceOf(e.msg.sender); + uint256 underlyingUserBalanceBefore = underlyingBalanceOf(account); + uint256 underlyingThisBalanceBefore = underlyingBalanceOf(currentContract); + + withdrawTo(e, account, amount); + + uint256 underlyingSenderBalanceAfter = underlyingBalanceOf(e.msg.sender); + uint256 underlyingUserBalanceAfter = underlyingBalanceOf(account); + uint256 underlyingThisBalanceAfter = underlyingBalanceOf(currentContract); + + assert account == e.msg.sender => underlyingSenderBalanceBefore == underlyingUserBalanceBefore + && underlyingSenderBalanceAfter == underlyingUserBalanceAfter + && underlyingUserBalanceBefore == underlyingUserBalanceAfter - amount, "underlying balances wrong update (acc == sender)"; + + assert account != e.msg.sender && account == currentContract => underlyingUserBalanceBefore == underlyingUserBalanceAfter + && underlyingSenderBalanceBefore == underlyingSenderBalanceAfter, "underlying balances wrong update (acc == contract)"; + assert account != e.msg.sender && account != currentContract => underlyingUserBalanceBefore == underlyingUserBalanceAfter - amount + && underlyingSenderBalanceBefore == underlyingSenderBalanceAfter + && underlyingThisBalanceBefore == underlyingThisBalanceAfter + amount, "underlying balances wrong update (acc != contract)"; +} + + +// STATUS - verified +// check correct values update by _recover() +rule recoverSpec(env e){ + address account; uint256 amount; // e.msg.sender + require underlying() != currentContract; + + require e.msg.sender != currentContract; + + uint256 wrapperTotalBefore = totalSupply(e); + uint256 wrapperUserBalanceBefore = balanceOf(e, account); + uint256 wrapperSenderBalanceBefore = balanceOf(e, e.msg.sender); + + uint256 underlyingThisBalanceBefore = underlyingBalanceOf(currentContract); + + mathint value = underlyingThisBalanceBefore - wrapperTotalBefore; + + _recover(e, account); + + uint256 wrapperTotalAfter = totalSupply(e); + uint256 wrapperUserBalanceAfter = balanceOf(e, account); + uint256 wrapperSenderBalanceAfter = balanceOf(e, e.msg.sender); + + assert wrapperTotalBefore == wrapperTotalAfter - value, "wrapper total wrong update"; + assert e.msg.sender == account => wrapperUserBalanceBefore == wrapperSenderBalanceBefore + && wrapperUserBalanceAfter == wrapperSenderBalanceAfter + && wrapperUserBalanceBefore == wrapperUserBalanceAfter - value, "wrapper balances wrong update"; + assert e.msg.sender != account => wrapperUserBalanceBefore == wrapperUserBalanceAfter - value + && wrapperSenderBalanceBefore == wrapperSenderBalanceAfter, "wrapper balances wrong update"; +} \ No newline at end of file diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec index b8a15cbe3..f7896a1b7 100644 --- a/certora/specs/TimelockController.spec +++ b/certora/specs/TimelockController.spec @@ -40,7 +40,7 @@ definition done(bytes32 id) returns bool = function hashIdCorrelation(bytes32 id, address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt){ - require data.length < 3; + require data.length < 7; require hashOperation(target, value, data, predecessor, salt) == id; } @@ -241,16 +241,20 @@ rule cannotCallExecute(method f, env e){ } -// STATUS - in progress (need working hash) +// STATUS - in progress // in unset() execute() reverts -rule executeRevertFromUnset(method f, env e){ +rule executeRevertFromUnset(method f, env e, env e2){ address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; bytes32 id; - hashIdCorrelation(id, target, value, data, predecessor, salt); + // hashIdCorrelation(id, target, value, data, predecessor, salt); + require data.length < 4; + require hashOperation(target, value, data, predecessor, salt) == id; require unset(id); - execute@withrevert(e, target, value, data, predecessor, salt); + scheduleCheck1@withrevert(e, id); + + // execute@withrevert(e, target, value, data, predecessor, salt); assert lastReverted, "you go against execution nature"; } @@ -266,6 +270,7 @@ rule executeRevertEffectCheck(method f, env e){ require pending(id) && !ready(id, e); execute@withrevert(e, target, value, data, predecessor, salt); + bool reverted = lastReverted; assert lastReverted => pending(id) && !ready(id, e), "you go against execution nature"; } @@ -286,7 +291,7 @@ rule cancelledNotExecuted(method f, env e){ } -// STATUS - in progress (need working hash) +// STATUS - in progress // Only proposers can schedule an operation rule onlyProposer(method f, env e){ bytes32 id; diff --git a/certora/specs/erc20.spec b/certora/specs/erc20.spec new file mode 100644 index 000000000..b12fec167 --- /dev/null +++ b/certora/specs/erc20.spec @@ -0,0 +1,12 @@ +// erc20 methods +methods { + name() returns (string) => DISPATCHER(true) + symbol() returns (string) => DISPATCHER(true) + decimals() returns (string) => DISPATCHER(true) + totalSupply() returns (uint256) => DISPATCHER(true) + balanceOf(address) returns (uint256) => DISPATCHER(true) + allowance(address,address) returns (uint) => DISPATCHER(true) + approve(address,uint256) returns (bool) => DISPATCHER(true) + transfer(address,uint256) returns (bool) => DISPATCHER(true) + transferFrom(address,address,uint256) returns (bool) => DISPATCHER(true) +} From 8d9ab176d7eac42554f7c151f71ed1b5a969e77b Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 21 Mar 2022 17:58:21 +0000 Subject: [PATCH 146/254] Timelock hash bug, example for Alex --- certora/munged/governance/TimelockController.sol | 3 ++- certora/specs/TimelockController.spec | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/certora/munged/governance/TimelockController.sol b/certora/munged/governance/TimelockController.sol index 3da7e2170..c6d30c665 100644 --- a/certora/munged/governance/TimelockController.sol +++ b/certora/munged/governance/TimelockController.sol @@ -357,6 +357,7 @@ contract TimelockController is AccessControl { function scheduleCheck1(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) { - require(false); + bool tmp = false; + require(tmp); } } diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec index f7896a1b7..b4dc024ce 100644 --- a/certora/specs/TimelockController.spec +++ b/certora/specs/TimelockController.spec @@ -249,7 +249,7 @@ rule executeRevertFromUnset(method f, env e, env e2){ // hashIdCorrelation(id, target, value, data, predecessor, salt); require data.length < 4; - require hashOperation(target, value, data, predecessor, salt) == id; + // require hashOperation(target, value, data, predecessor, salt) == id; require unset(id); scheduleCheck1@withrevert(e, id); From 6c5d33ba221440f365c7ba481d9741999f948780 Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Mon, 21 Mar 2022 11:13:24 -0700 Subject: [PATCH 147/254] run scripts --- certora/scripts/ERC20VotesRule.sh | 23 +++++++++++++++++++++++ certora/scripts/verifyERC20Votes.sh | 20 ++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 certora/scripts/ERC20VotesRule.sh diff --git a/certora/scripts/ERC20VotesRule.sh b/certora/scripts/ERC20VotesRule.sh new file mode 100644 index 000000000..a29fd1ff9 --- /dev/null +++ b/certora/scripts/ERC20VotesRule.sh @@ -0,0 +1,23 @@ +if [ -z "$2" ] + then + echo "Incorrect number of arguments" + echo "" + echo "Usage: (from git root)" + echo " ./certora/scripts/`basename $0` [message describing the run] [rule or invariant]" + echo "" + exit 1 +fi + +rule=$1 +msg=$2 +shift 2 + +certoraRun \ + certora/harnesses/ERC20VotesHarness.sol \ + --verify ERC20VotesHarness:certora/specs/ERC20Votes.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --rule ${rule} \ + --msg "${msg}" \ + --staging \ + # --rule_sanity \ \ No newline at end of file diff --git a/certora/scripts/verifyERC20Votes.sh b/certora/scripts/verifyERC20Votes.sh index bb8c8e749..15f282307 100644 --- a/certora/scripts/verifyERC20Votes.sh +++ b/certora/scripts/verifyERC20Votes.sh @@ -1,7 +1,23 @@ +make -C certora munged + +if [ -z "$1" ] + then + echo "Incorrect number of arguments" + echo "" + echo "Usage: (from git root)" + echo " ./certora/scripts/`basename $0` [message describing the run]" + echo "" + exit 1 +fi + +msg=$1 +shift 1 + certoraRun \ certora/harnesses/ERC20VotesHarness.sol \ --verify ERC20VotesHarness:certora/specs/ERC20Votes.spec \ --solc solc8.2 \ --optimistic_loop \ - --cloud \ - --msg "sanityVotes" + --loop_iter 4 \ + --staging \ + --msg "${msg}" From 8318470ccaf7d5343683a915f6d516ecf8451367 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 23 Mar 2022 19:12:16 +0000 Subject: [PATCH 148/254] flashMint cleaning --- certora/specs/ERC20FlashMint.spec | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/certora/specs/ERC20FlashMint.spec b/certora/specs/ERC20FlashMint.spec index db8cddcad..511eaf84f 100644 --- a/certora/specs/ERC20FlashMint.spec +++ b/certora/specs/ERC20FlashMint.spec @@ -1,7 +1,7 @@ import "erc20.spec" methods { - onFlashLoan(address, address, uint256, uint256, bytes) => HAVOC_ALL // HAVOC_ECF + onFlashLoan(address, address, uint256, uint256, bytes) => HAVOC_ALL _burn(address account, uint256 amount) returns(bool) => specBurn(account, amount); } @@ -13,19 +13,11 @@ function specBurn(address account, uint256 amount) returns bool { // retuns ne return true; } -// ghost to save args that were passed to burn function -// summarize burn -// assert ghost == amount + fee - -// STATUS - in progress -// HAVOC_ALL - everything is havoced => violation -// HAVOC_ECF - verified -// https://vaas-stg.certora.com/output/3106/8795450b626f2ca53a2b/?anonymousKey=dd774da10cc595e4e38357af9e4f50bf2c0cb02a +// STATUS - verified // fee + flashLoan amount is burned rule letsWatchItBurns(env e){ address receiver; address token; uint256 amount; bytes data; - require amount > 0; uint256 feeBefore = flashFee(e, token, amount); From 5153c462d50890d82cb3dc11b0c0272a5c6cf639 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Wed, 23 Mar 2022 19:42:14 +0000 Subject: [PATCH 149/254] wrapper counterexample to check --- certora/specs/ERC20Wrapper.spec | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/certora/specs/ERC20Wrapper.spec b/certora/specs/ERC20Wrapper.spec index 6e40c61d2..126be7297 100644 --- a/certora/specs/ERC20Wrapper.spec +++ b/certora/specs/ERC20Wrapper.spec @@ -12,7 +12,7 @@ methods { // STATUS - verified -// totalsupply of wrapped should be less than or equal to underlying (assuming no transfer they should be equal) - solvency +// totalsupply of wrapped should be less than or equal to underlying (assuming no external transfer) - solvency invariant whatAboutTotal(env e) totalSupply(e) <= underlyingTotalSupply() filtered { f -> f.selector != certorafallback_0().selector } @@ -29,6 +29,18 @@ invariant whatAboutTotal(env e) } +// STATUS - in progress +// https://vaas-stg.certora.com/output/3106/a5f4943cd2987dccab94/?anonymousKey=9428fb1588845c0222c2abe5b00dedd59c925870 +// totalsupply of wrapped should be less than or equal to the underlying balanceOf contract (assuming no external transfer) - solvency +invariant underTotalAndContractBalanceOfCorrelation(env e) + totalSupply(e) <= underlyingBalanceOf(currentContract) + { + preserved { + require underlying() != currentContract; + } + } + + // STATUS - verified // check correct values update by depositFor() rule depositForSpecBasic(env e){ From 6895946f41521a695cc61772236558c60dc125ba Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Fri, 25 Mar 2022 12:57:16 -0700 Subject: [PATCH 150/254] updated rules --- certora/applyHarness.patch | 337 +++++++++++++----- certora/harnesses/ERC20VotesHarness.sol | 17 + .../token/ERC20/extensions/ERC20Votes.sol | 11 +- certora/scripts/verifyERC20Votes.sh | 1 - certora/specs/ERC20Votes.spec | 310 +++++++++++++++- 5 files changed, 571 insertions(+), 105 deletions(-) diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 42b10fab5..e7b66d707 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,101 +1,242 @@ -diff -ruN .gitignore .gitignore ---- .gitignore 1969-12-31 19:00:00.000000000 -0500 -+++ .gitignore 2021-12-09 14:46:33.923637220 -0500 -@@ -0,0 +1,2 @@ -+* -+!.gitignore -diff -ruN governance/compatibility/GovernorCompatibilityBravo.sol governance/compatibility/GovernorCompatibilityBravo.sol ---- governance/compatibility/GovernorCompatibilityBravo.sol 2021-12-03 15:24:56.523654357 -0500 -+++ governance/compatibility/GovernorCompatibilityBravo.sol 2021-12-09 14:46:33.923637220 -0500 -@@ -245,7 +245,7 @@ - /** - * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. - */ -- function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { -+ function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal - ProposalDetails storage details = _proposalDetails[proposalId]; - return quorum(proposalSnapshot(proposalId)) <= details.forVotes; - } -@@ -253,7 +253,7 @@ - /** - * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be scritly over the againstVotes. - */ -- function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { -+ function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal - ProposalDetails storage details = _proposalDetails[proposalId]; - return details.forVotes > details.againstVotes; - } -diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol ---- governance/extensions/GovernorCountingSimple.sol 2021-12-03 15:24:56.523654357 -0500 -+++ governance/extensions/GovernorCountingSimple.sol 2021-12-09 14:46:33.923637220 -0500 -@@ -64,7 +64,7 @@ - /** - * @dev See {Governor-_quorumReached}. - */ -- function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { -+ function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { - ProposalVote storage proposalvote = _proposalVotes[proposalId]; - - return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes; -@@ -73,7 +73,7 @@ - /** - * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. - */ -- function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { -+ function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { - ProposalVote storage proposalvote = _proposalVotes[proposalId]; - - return proposalvote.forVotes > proposalvote.againstVotes; -diff -ruN governance/extensions/GovernorTimelockControl.sol governance/extensions/GovernorTimelockControl.sol ---- governance/extensions/GovernorTimelockControl.sol 2021-12-03 15:24:56.523654357 -0500 -+++ governance/extensions/GovernorTimelockControl.sol 2021-12-09 14:46:33.923637220 -0500 -@@ -111,7 +111,7 @@ - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal virtual override { -- _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); -+ _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); - } - - /** -diff -ruN governance/Governor.sol governance/Governor.sol ---- governance/Governor.sol 2021-12-03 15:24:56.523654357 -0500 -+++ governance/Governor.sol 2021-12-09 14:46:56.411503587 -0500 -@@ -38,8 +38,8 @@ - - string private _name; - -- mapping(uint256 => ProposalCore) private _proposals; -- -+ mapping(uint256 => ProposalCore) public _proposals; -+ - /** - * @dev Restrict access to governor executing address. Some module might override the _executor function to make - * sure this modifier is consistant with the execution model. -@@ -167,12 +167,12 @@ - /** - * @dev Amount of votes already cast passes the threshold limit. - */ -- function _quorumReached(uint256 proposalId) internal view virtual returns (bool); -+ function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal - - /** - * @dev Is the proposal successful or not. - */ -- function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool); -+ function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal - - /** - * @dev Register a vote with a given support and voting weight. -diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol ---- token/ERC20/extensions/ERC20Votes.sol 2021-12-03 15:24:56.527654330 -0500 -+++ token/ERC20/extensions/ERC20Votes.sol 2021-12-09 14:46:33.927637196 -0500 -@@ -84,7 +84,7 @@ +diff -ruN access/AccessControl.sol access/AccessControl.sol +--- access/AccessControl.sol 2022-03-02 09:14:55.000000000 -0800 ++++ access/AccessControl.sol 2022-03-24 16:41:46.000000000 -0700 +@@ -93,7 +93,7 @@ * - * - `blockNumber` must have been already mined + * _Available since v4.6._ */ -- function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) { -+ function getPastVotes(address account, uint256 blockNumber) public view virtual returns (uint256) { - require(blockNumber < block.number, "ERC20Votes: block not yet mined"); - return _checkpointsLookup(_checkpoints[account], blockNumber); +- function _checkRole(bytes32 role) internal view virtual { ++ function _checkRole(bytes32 role) public view virtual { // HARNESS: internal -> public + _checkRole(role, _msgSender()); } + +diff -ruN governance/TimelockController.sol governance/TimelockController.sol +--- governance/TimelockController.sol 2022-03-02 09:14:55.000000000 -0800 ++++ governance/TimelockController.sol 2022-03-24 16:41:46.000000000 -0700 +@@ -24,10 +24,10 @@ + bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); + bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); + bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); +- uint256 internal constant _DONE_TIMESTAMP = uint256(1); ++ uint256 public constant _DONE_TIMESTAMP = uint256(1); // HARNESS: internal -> public + + mapping(bytes32 => uint256) private _timestamps; +- uint256 private _minDelay; ++ uint256 public _minDelay; // HARNESS: private -> public + + /** + * @dev Emitted when a call is scheduled as part of operation `id`. +@@ -353,4 +353,11 @@ + emit MinDelayChange(_minDelay, newDelay); + _minDelay = newDelay; + } +-} ++ ++ ++ ++ function scheduleCheck1(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) { ++ bool tmp = false; ++ require(tmp); ++ } ++} +diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol +--- governance/utils/Votes.sol 2022-03-02 09:14:55.000000000 -0800 ++++ governance/utils/Votes.sol 2022-03-24 16:41:46.000000000 -0700 +@@ -207,5 +207,5 @@ + /** + * @dev Must return the voting units held by an account. + */ +- function _getVotingUnits(address) internal virtual returns (uint256); ++ function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public + } +diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol +--- token/ERC20/ERC20.sol 2022-03-02 09:14:55.000000000 -0800 ++++ token/ERC20/ERC20.sol 2022-03-24 16:41:46.000000000 -0700 +@@ -277,7 +277,7 @@ + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ +- function _burn(address account, uint256 amount) internal virtual { ++ function _burn(address account, uint256 amount) public virtual returns (bool) { // HARNESS: internal -> public + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); +@@ -292,6 +292,8 @@ + emit Transfer(account, address(0), amount); + + _afterTokenTransfer(account, address(0), amount); ++ ++ return true; + } + + /** +diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol +--- token/ERC20/extensions/ERC20FlashMint.sol 2022-03-02 09:14:55.000000000 -0800 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-03-24 16:41:46.000000000 -0700 +@@ -40,9 +40,11 @@ + require(token == address(this), "ERC20FlashMint: wrong token"); + // silence warning about unused variable without the addition of bytecode. + amount; +- return 0; ++ return fee; // HARNESS: made "return" nonzero + } + ++ uint256 public fee; // HARNESS: added it to simulate random fee amount ++ + /** + * @dev Performs a flash loan. New tokens are minted and sent to the + * `receiver`, who is required to implement the {IERC3156FlashBorrower} +@@ -70,7 +72,7 @@ + uint256 fee = flashFee(token, amount); + _mint(address(receiver), amount); + require( +- receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, ++ receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, // HAVOC_ALL + "ERC20FlashMint: invalid return value" + ); + uint256 currentAllowance = allowance(address(receiver), address(this)); +diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol +--- token/ERC20/extensions/ERC20Votes.sol 2022-03-02 09:14:55.000000000 -0800 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-03-24 17:15:51.000000000 -0700 +@@ -33,8 +33,8 @@ + bytes32 private constant _DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + +- mapping(address => address) private _delegates; +- mapping(address => Checkpoint[]) private _checkpoints; ++ mapping(address => address) public _delegates; ++ mapping(address => Checkpoint[]) public _checkpoints; + Checkpoint[] private _totalSupplyCheckpoints; + + /** +@@ -152,7 +152,7 @@ + /** + * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). + */ +- function _maxSupply() internal view virtual returns (uint224) { ++ function _maxSupply() public view virtual returns (uint224) { //harnessed to public + return type(uint224).max; + } + +@@ -163,16 +163,17 @@ + super._mint(account, amount); + require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); + +- _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); ++ _writeCheckpointAdd(_totalSupplyCheckpoints, amount); // HARNESS: new version without pointer + } + + /** + * @dev Snapshots the totalSupply after it has been decreased. + */ +- function _burn(address account, uint256 amount) internal virtual override { ++ function _burn(address account, uint256 amount) public virtual override returns (bool){ // HARNESS: internal -> public (to comply with the ERC20 harness) + super._burn(account, amount); + +- _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); ++ _writeCheckpointSub(_totalSupplyCheckpoints, amount); // HARNESS: new version without pointer ++ return true; + } + + /** +@@ -187,7 +188,7 @@ + ) internal virtual override { + super._afterTokenTransfer(from, to, amount); + +- _moveVotingPower(delegates(from), delegates(to), amount); ++ _moveVotingPower(delegates(from), delegates(to), amount); + } + + /** +@@ -195,7 +196,7 @@ + * + * Emits events {DelegateChanged} and {DelegateVotesChanged}. + */ +- function _delegate(address delegator, address delegatee) internal virtual { ++ function _delegate(address delegator, address delegatee) public virtual { // HARNESSED TO MAKE PUBLIC + address currentDelegate = delegates(delegator); + uint256 delegatorBalance = balanceOf(delegator); + _delegates[delegator] = delegatee; +@@ -212,25 +213,25 @@ + ) private { + if (src != dst && amount > 0) { + if (src != address(0)) { +- (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); ++ (uint256 oldWeight, uint256 newWeight) = _writeCheckpointSub(_checkpoints[src], amount); // HARNESS: new version without pointer + emit DelegateVotesChanged(src, oldWeight, newWeight); + } + + if (dst != address(0)) { +- (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); ++ (uint256 oldWeight, uint256 newWeight) = _writeCheckpointAdd(_checkpoints[dst], amount); // HARNESS: new version without pointer + emit DelegateVotesChanged(dst, oldWeight, newWeight); + } + } + } + +- function _writeCheckpoint( ++ // HARNESS: split _writeCheckpoint() to two functions as a workaround for function pointers that cannot be managed by the tool ++ function _writeCheckpointAdd( + Checkpoint[] storage ckpts, +- function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; +- newWeight = op(oldWeight, delta); ++ newWeight = _add(oldWeight, delta); + + if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { + ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); +@@ -239,6 +240,39 @@ + } + } + ++ function _writeCheckpointSub( ++ Checkpoint[] storage ckpts, ++ uint256 delta ++ ) private returns (uint256 oldWeight, uint256 newWeight) { ++ uint256 pos = ckpts.length; ++ oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; ++ newWeight = _subtract(oldWeight, delta); ++ ++ if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { ++ ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); ++ } else { ++ ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); ++ } ++ } ++ ++ // backup of original function ++ // ++ // function _writeCheckpoint( ++ // Checkpoint[] storage ckpts, ++ // function(uint256, uint256) view returns (uint256) op, ++ // uint256 delta ++ // ) private returns (uint256 oldWeight, uint256 newWeight) { ++ // uint256 pos = ckpts.length; ++ // oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; ++ // newWeight = op(oldWeight, delta); ++ // ++ // if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { ++ // ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); ++ // } else { ++ // ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); ++ // } ++ // } ++ + function _add(uint256 a, uint256 b) private pure returns (uint256) { + return a + b; + } +diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol +--- token/ERC20/extensions/ERC20Wrapper.sol 2022-03-02 09:14:55.000000000 -0800 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-03-24 16:41:46.000000000 -0700 +@@ -44,7 +44,7 @@ + * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal + * function that can be exposed with access control if desired. + */ +- function _recover(address account) internal virtual returns (uint256) { ++ function _recover(address account) public virtual returns (uint256) { // HARNESS: internal -> public + uint256 value = underlying.balanceOf(address(this)) - totalSupply(); + _mint(account, value); + return value; diff --git a/certora/harnesses/ERC20VotesHarness.sol b/certora/harnesses/ERC20VotesHarness.sol index 3bf03732f..14b170bbf 100644 --- a/certora/harnesses/ERC20VotesHarness.sol +++ b/certora/harnesses/ERC20VotesHarness.sol @@ -2,4 +2,21 @@ import "../munged/token/ERC20/extensions/ERC20Votes.sol"; contract ERC20VotesHarness is ERC20Votes { constructor(string memory name, string memory symbol) ERC20Permit(name) ERC20(name, symbol) {} + + function ckptFromBlock(address account, uint32 pos) public view returns (uint32) { + return _checkpoints[account][pos].fromBlock; + } + + function ckptVotes(address account, uint32 pos) public view returns (uint224) { + return _checkpoints[account][pos].fromBlock; + } + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } } + diff --git a/certora/munged/token/ERC20/extensions/ERC20Votes.sol b/certora/munged/token/ERC20/extensions/ERC20Votes.sol index be127239b..a8de4825e 100644 --- a/certora/munged/token/ERC20/extensions/ERC20Votes.sol +++ b/certora/munged/token/ERC20/extensions/ERC20Votes.sol @@ -33,8 +33,8 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - mapping(address => address) private _delegates; - mapping(address => Checkpoint[]) private _checkpoints; + mapping(address => address) public _delegates; + mapping(address => Checkpoint[]) public _checkpoints; Checkpoint[] private _totalSupplyCheckpoints; /** @@ -152,7 +152,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { /** * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). */ - function _maxSupply() internal view virtual returns (uint224) { + function _maxSupply() public view virtual returns (uint224) { //harnessed to public return type(uint224).max; } @@ -169,10 +169,11 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { /** * @dev Snapshots the totalSupply after it has been decreased. */ - function _burn(address account, uint256 amount) internal virtual override { + function _burn(address account, uint256 amount) public virtual override returns (bool){ // HARNESS: internal -> public (to comply with the ERC20 harness) super._burn(account, amount); _writeCheckpointSub(_totalSupplyCheckpoints, amount); // HARNESS: new version without pointer + return true; } /** @@ -195,7 +196,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { * * Emits events {DelegateChanged} and {DelegateVotesChanged}. */ - function _delegate(address delegator, address delegatee) internal virtual { + function _delegate(address delegator, address delegatee) public virtual { // HARNESSED TO MAKE PUBLIC address currentDelegate = delegates(delegator); uint256 delegatorBalance = balanceOf(delegator); _delegates[delegator] = delegatee; diff --git a/certora/scripts/verifyERC20Votes.sh b/certora/scripts/verifyERC20Votes.sh index 15f282307..7b23e29fe 100644 --- a/certora/scripts/verifyERC20Votes.sh +++ b/certora/scripts/verifyERC20Votes.sh @@ -19,5 +19,4 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --loop_iter 4 \ - --staging \ --msg "${msg}" diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index b08c6cf4e..7adb3cca1 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -1,6 +1,314 @@ +using ERC20VotesHarness as erc20votes + +methods { + // functions + checkpoints(address, uint32) envfree + numCheckpoints(address) returns (uint32) envfree + delegates(address) returns (address) envfree + getVotes(address) returns (uint256) envfree + getPastVotes(address, uint256) returns (uint256) + getPastTotalSupply(uint256) returns (uint256) + delegate(address) + _delegate(address, address) + delegateBySig(address, uint256, uint256, uint8, bytes32, bytes32) + totalSupply() returns (uint256) envfree + _maxSupply() returns (uint224) envfree + + // harnesss functions + ckptFromBlock(address, uint32) returns (uint32) envfree + ckptVotes(address, uint32) returns (uint224) envfree + mint(address, uint256) + burn(address, uint256) + + // solidity generated getters + _delegates(address) returns (address) envfree + + // external functions + + +} +// gets the most recent votes for a user +ghost userVotes(address) returns uint224; + +// sums the total votes for all users +ghost totalVotes() returns mathint { + axiom totalVotes() > 0; +} + + + +hook Sstore _checkpoints[KEY address account][INDEX uint32 index].votes uint224 newVotes (uint224 oldVotes) STORAGE { + havoc userVotes assuming + userVotes@new(account) == newVotes; + + havoc totalVotes assuming + totalVotes@new() == totalVotes@old() + to_mathint(newVotes) - to_mathint(userVotes(account)); +} + + +ghost lastFromBlock(address) returns uint32; + +ghost doubleFromBlock(address) returns bool { + init_state axiom forall address a. doubleFromBlock(a) == false; +} + + +hook Sstore _checkpoints[KEY address account][INDEX uint32 index].fromBlock uint32 newBlock (uint32 oldBlock) STORAGE { + havoc lastFromBlock assuming + lastFromBlock@new(account) == newBlock; + + havoc doubleFromBlock assuming + doubleFromBlock@new(account) == (newBlock == oldBlock); +} + rule sanity(method f) { env e; calldataarg arg; f(e, arg); assert false; -} \ No newline at end of file +} + +// something stupid just to see +invariant sanity_invariant() + totalSupply() >= 0 + +// sum of user balances is >= total amount of delegated votes +invariant votes_solvency() + to_mathint(totalSupply()) >= totalVotes() + +// for some checkpoint, the fromBlock is less than the current block number +// passes but fails rule sanity from hash on delegate by sig +invariant timestamp_constrains_fromBlock(address account, uint32 index, env e) + ckptFromBlock(account, index) < e.block.number +{ + preserved { + require index < numCheckpoints(account); + } +} + +// numCheckpoints are less than maxInt +// passes +invariant maxInt_constrains_numBlocks(address account) + numCheckpoints(account) <= 4294967295 // 2^32 + +// can't have more checkpoints for a given account than the last from block +invariant fromBlock_constrains_numBlocks(address account) + numCheckpoints(account) <= lastFromBlock(account) + +invariant unique_checkpoints(address account) + !doubleFromBlock(account) + +rule transfer_safe() { + env e; + uint256 amount; + address a; address b; + require a != b; + + uint256 votesA_pre = getVotes(a); + uint256 votesB_pre = getVotes(b); + + require votesA_pre == erc20votes.balanceOf(e, a); + require votesB_pre == erc20votes.balanceOf(e, b); + + mathint totalVotes_pre = totalVotes(); + + erc20votes.transferFrom(e, a, b, amount); + + mathint totalVotes_post = totalVotes(); + uint256 votesA_post = getVotes(a); + uint256 votesB_post = getVotes(b); + + assert totalVotes_pre == totalVotes_post, "transfer changed total supply"; + assert votesA_pre - votesA_post == amount, "a lost the proper amount of votes"; + assert votesB_post - votesB_pre == amount, "b lost the proper amount of votes"; +} + + +rule delegator_votes_removed() { + env e; + address delegator; address delegatee; + + require delegator != delegatee; + require delegates(delegator) == 0; // has not delegated + + uint256 pre = getVotes(delegator); + + _delegate(e, delegator, delegatee); + + uint256 balance = balanceOf(e, delegator); + + uint256 post = getVotes(delegator); + assert post == pre - balance, "delegator retained votes"; +} + +rule delegatee_receives_votes() { + env e; + address delegator; address delegatee; + require delegator != delegatee; + + uint256 delegator_bal = balanceOf(e, delegator); + uint256 votes_= getVotes(delegatee); + + _delegate(e, delegator, delegatee); + + uint256 _votes = getVotes(delegatee); + + assert _votes == votes_ + delegator_bal, "delegatee did not receive votes"; +} + +rule previous_delegatee_zerod() { + env e; + address delegator; address delegatee; address third; + + require delegator != delegatee; + require third != delegatee; + require third != delegator; + + uint256 delegator_bal = balanceOf(e, delegator); + uint256 votes_ = getVotes(third); + + _delegate(e, delegator, delegatee); + + uint256 _votes = getVotes(third); + + assert votes_ == votes_ - delegator_bal, "votes not removed from the previous delegatee"; +} + +// passes +rule delegate_contained() { + env e; + address delegator; address delegatee; address other; + + require delegator != delegatee; + require other != delegator; + require other != delegatee; + require other != delegates(delegator); + + uint256 votes_ = getVotes(other); + + _delegate(e, delegator, delegatee); + + uint256 _votes = getVotes(other); + + assert votes_ == _votes, "votes not contained"; +} + +// checks all of the above rules with front running +rule delegate_no_frontrunning(method f) { + env e; calldataarg args; + address delegator; address delegatee; address third; address other; + + require delegator != delegatee; + require delegates(delegator) == third; + require other != third; + + uint256 delegator_bal = erc20votes.balanceOf(e, delegator); + + uint256 dr_ = getVotes(delegator); + uint256 de_ = getVotes(delegatee); + uint256 third_ = getVotes(third); + uint256 other_ = getVotes(other); + + // require third is address for previous delegator + f(e, args); + _delegate(e, delegator, delegatee); + + uint256 _dr = getVotes(delegator); + uint256 _de = getVotes(delegatee); + uint256 _third = getVotes(third); + uint256 _other = getVotes(other); + + + // delegator loses all of their votes + // delegatee gains that many votes + // third loses any votes delegated to them + assert _dr == 0, "delegator retained votes"; + assert _de == de_ + delegator_bal, "delegatee not received votes"; + assert _third != 0 => _third == third_ - delegator_bal, "votes not removed from third"; + assert other_ == _other, "delegate not contained"; +} + +// passes +rule mint_increases_totalSupply() { + + env e; + uint256 amount; address account; + + uint256 fromBlock = e.block.number; + uint256 totalSupply_ = totalSupply(); + + mint(e, account, amount); + + uint256 _totalSupply = totalSupply(); + require _totalSupply < _maxSupply(); + + assert _totalSupply == totalSupply_ + amount, "totalSupply not increased properly"; + assert getPastTotalSupply(e, fromBlock) == totalSupply_ , "previous total supply not saved properly"; +} + +// passes +rule burn_decreases_totalSupply() { + env e; + uint256 amount; address account; + + uint256 fromBlock = e.block.number; + uint256 totalSupply_ = totalSupply(); + require totalSupply_ > balanceOf(e, account); + + burn(e, account, amount); + + uint256 _totalSupply = totalSupply(); + require _totalSupply < _maxSupply(); + + assert _totalSupply == totalSupply_ - amount, "totalSupply not decreased properly"; + assert getPastTotalSupply(e, fromBlock) == totalSupply_ , "previous total supply not saved properly"; +} + + + +// passes +rule mint_doesnt_increase_totalVotes() { + env e; + uint256 amount; address account; + + mathint totalVotes_ = totalVotes(); + + mint(e, account, amount); + + assert totalVotes() == totalVotes_, "totalVotes increased"; +} +// passes +rule burn_doesnt_decrease_totalVotes() { + env e; + uint256 amount; address account; + + mathint totalVotes_ = totalVotes(); + + burn(e, account, amount); + + assert totalVotes() == totalVotes_, "totalVotes decreased"; +} + +// // fails +// rule mint_increases_totalVotes() { +// env e; +// uint256 amount; address account; + +// mathint totalVotes_ = totalVotes(); + +// mint(e, account, amount); + +// assert totalVotes() == totalVotes_ + to_mathint(amount), "totalVotes not increased"; +// } + +// // fails +// rule burn_decreases_totalVotes() { +// env e; +// uint256 amount; address account; + +// mathint totalVotes_ = totalVotes(); + +// burn(e, account, amount); + +// assert totalVotes() == totalVotes_ - to_mathint(amount), "totalVotes not decreased"; +// } From a35ad6dfc30d78dfe4ad71baf1122f5d9791024b Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Sun, 27 Mar 2022 16:04:31 +0100 Subject: [PATCH 151/254] wrapper and timelockController cleaning --- .../token/ERC20/extensions/ERC20FlashMint.sol | 2 +- certora/scripts/verifyERC20FlashMint.sh | 2 +- certora/scripts/verifyERC20Wrapper.sh | 2 +- certora/specs/ERC20Wrapper.spec | 28 +++++++++---------- certora/specs/TimelockController.spec | 16 +---------- 5 files changed, 18 insertions(+), 32 deletions(-) diff --git a/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol b/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol index 2a811cacf..eb2cadd00 100644 --- a/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol +++ b/certora/munged/token/ERC20/extensions/ERC20FlashMint.sol @@ -72,7 +72,7 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { uint256 fee = flashFee(token, amount); _mint(address(receiver), amount); require( - receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, // HAVOC_ALL + receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, "ERC20FlashMint: invalid return value" ); uint256 currentAllowance = allowance(address(receiver), address(this)); diff --git a/certora/scripts/verifyERC20FlashMint.sh b/certora/scripts/verifyERC20FlashMint.sh index 84ec58eff..cd29f5b1a 100644 --- a/certora/scripts/verifyERC20FlashMint.sh +++ b/certora/scripts/verifyERC20FlashMint.sh @@ -6,5 +6,5 @@ certoraRun \ --optimistic_loop \ --staging \ --rule_sanity \ - --msg "letsWatchItBurns" + --msg "flashMint" \ No newline at end of file diff --git a/certora/scripts/verifyERC20Wrapper.sh b/certora/scripts/verifyERC20Wrapper.sh index e4557724f..c4aaef9ff 100644 --- a/certora/scripts/verifyERC20Wrapper.sh +++ b/certora/scripts/verifyERC20Wrapper.sh @@ -6,5 +6,5 @@ certoraRun \ --optimistic_loop \ --staging \ --rule_sanity \ - --msg "all check" + --msg "wrapper spec sanity check fixes" \ No newline at end of file diff --git a/certora/specs/ERC20Wrapper.spec b/certora/specs/ERC20Wrapper.spec index 126be7297..3ea288545 100644 --- a/certora/specs/ERC20Wrapper.spec +++ b/certora/specs/ERC20Wrapper.spec @@ -19,24 +19,22 @@ invariant whatAboutTotal(env e) { preserved { require underlyingBalanceOf(currentContract) <= underlyingTotalSupply(); - require underlying() != currentContract; } preserved depositFor(address account, uint256 amount) with (env e2){ - require underlyingBalanceOf(currentContract) <= underlyingTotalSupply(); require totalSupply(e) + amount <= underlyingTotalSupply(); - require underlying() != currentContract; } } -// STATUS - in progress -// https://vaas-stg.certora.com/output/3106/a5f4943cd2987dccab94/?anonymousKey=9428fb1588845c0222c2abe5b00dedd59c925870 +// STATUS - verified // totalsupply of wrapped should be less than or equal to the underlying balanceOf contract (assuming no external transfer) - solvency invariant underTotalAndContractBalanceOfCorrelation(env e) totalSupply(e) <= underlyingBalanceOf(currentContract) { - preserved { + preserved with (env e2) { require underlying() != currentContract; + require e.msg.sender != currentContract; + require e.msg.sender == e2.msg.sender; } } @@ -65,10 +63,12 @@ rule depositForSpecBasic(env e){ } +// STATUS - verified +// check correct values update by depositFor() rule depositForSpecWrapper(env e){ address account; uint256 amount; - require e.msg.sender != currentContract; + // require e.msg.sender != currentContract; require underlying() != currentContract; uint256 wrapperUserBalanceBefore = balanceOf(e, account); @@ -87,6 +87,8 @@ rule depositForSpecWrapper(env e){ } +// STATUS - verified +// check correct values update by depositFor() rule depositForSpecUnderlying(env e){ address account; uint256 amount; @@ -118,7 +120,6 @@ rule depositForSpecUnderlying(env e){ rule withdrawToSpecBasic(env e){ address account; uint256 amount; - require e.msg.sender != currentContract; require underlying() != currentContract; uint256 wrapperTotalBefore = totalSupply(e); @@ -134,10 +135,11 @@ rule withdrawToSpecBasic(env e){ } +// STATUS - verified +// check correct values update by withdrawTo() rule withdrawToSpecWrapper(env e){ address account; uint256 amount; - require e.msg.sender != currentContract; require underlying() != currentContract; uint256 wrapperUserBalanceBefore = balanceOf(e, account); @@ -156,6 +158,8 @@ rule withdrawToSpecWrapper(env e){ } +// STATUS - verified +// check correct values update by withdrawTo() rule withdrawToSpecUnderlying(env e){ address account; uint256 amount; @@ -187,15 +191,11 @@ rule withdrawToSpecUnderlying(env e){ // STATUS - verified // check correct values update by _recover() rule recoverSpec(env e){ - address account; uint256 amount; // e.msg.sender - require underlying() != currentContract; - - require e.msg.sender != currentContract; + address account; uint256 amount; uint256 wrapperTotalBefore = totalSupply(e); uint256 wrapperUserBalanceBefore = balanceOf(e, account); uint256 wrapperSenderBalanceBefore = balanceOf(e, e.msg.sender); - uint256 underlyingThisBalanceBefore = underlyingBalanceOf(currentContract); mathint value = underlyingThisBalanceBefore - wrapperTotalBefore; diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec index b4dc024ce..8efcbb508 100644 --- a/certora/specs/TimelockController.spec +++ b/certora/specs/TimelockController.spec @@ -177,20 +177,6 @@ rule minDealyOnlyChange(method f, env e){ } -// STATUS - verified -// Only proposers can schedule an operation -rule scheduleOnlyWay(method f, env e){ - uint256 delayBefore = _minDelay(); - - calldataarg args; - f(e, args); - - uint256 delayAfter = _minDelay(); - - assert delayBefore != delayAfter => e.msg.sender == currentContract, "You cannot change your destiny! Only I can!"; -} - - // STATUS - in progress (need working hash) // execute() is the only way to set timestamp to 1 rule getTimestampOnlyChange(method f, env e){ @@ -291,7 +277,7 @@ rule cancelledNotExecuted(method f, env e){ } -// STATUS - in progress +// STATUS - in progress (add schedule batch) // Only proposers can schedule an operation rule onlyProposer(method f, env e){ bytes32 id; From 3f1ee399102bb888577338a62e40fbb426ada4c7 Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Mon, 28 Mar 2022 12:05:33 -0700 Subject: [PATCH 152/254] call trace error --- certora/applyHarness.patch | 14 +++---- certora/harnesses/ERC20VotesHarness.sol | 10 +++++ certora/scripts/ERC20VotesRule.sh | 1 - certora/specs/ERC20Votes.spec | 50 +++++++++++++++++-------- 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index e7b66d707..7af5f82c8 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,6 +1,6 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol --- access/AccessControl.sol 2022-03-02 09:14:55.000000000 -0800 -+++ access/AccessControl.sol 2022-03-24 16:41:46.000000000 -0700 ++++ access/AccessControl.sol 2022-03-24 18:08:46.000000000 -0700 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -12,7 +12,7 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol diff -ruN governance/TimelockController.sol governance/TimelockController.sol --- governance/TimelockController.sol 2022-03-02 09:14:55.000000000 -0800 -+++ governance/TimelockController.sol 2022-03-24 16:41:46.000000000 -0700 ++++ governance/TimelockController.sol 2022-03-24 18:08:46.000000000 -0700 @@ -24,10 +24,10 @@ bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); @@ -41,7 +41,7 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol +} diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol --- governance/utils/Votes.sol 2022-03-02 09:14:55.000000000 -0800 -+++ governance/utils/Votes.sol 2022-03-24 16:41:46.000000000 -0700 ++++ governance/utils/Votes.sol 2022-03-24 18:08:46.000000000 -0700 @@ -207,5 +207,5 @@ /** * @dev Must return the voting units held by an account. @@ -51,7 +51,7 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol } diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol --- token/ERC20/ERC20.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/ERC20.sol 2022-03-24 16:41:46.000000000 -0700 ++++ token/ERC20/ERC20.sol 2022-03-24 18:08:46.000000000 -0700 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -72,7 +72,7 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol /** diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol --- token/ERC20/extensions/ERC20FlashMint.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-03-24 16:41:46.000000000 -0700 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-03-24 18:08:46.000000000 -0700 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -97,7 +97,7 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 uint256 currentAllowance = allowance(address(receiver), address(this)); diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol --- token/ERC20/extensions/ERC20Votes.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-03-24 17:15:51.000000000 -0700 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-03-25 13:13:49.000000000 -0700 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -230,7 +230,7 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote } diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol --- token/ERC20/extensions/ERC20Wrapper.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-03-24 16:41:46.000000000 -0700 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-03-24 18:08:46.000000000 -0700 @@ -44,7 +44,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. diff --git a/certora/harnesses/ERC20VotesHarness.sol b/certora/harnesses/ERC20VotesHarness.sol index 14b170bbf..b87879f80 100644 --- a/certora/harnesses/ERC20VotesHarness.sol +++ b/certora/harnesses/ERC20VotesHarness.sol @@ -18,5 +18,15 @@ contract ERC20VotesHarness is ERC20Votes { function burn(address account, uint256 amount) public { _burn(account, amount); } + + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { } + } diff --git a/certora/scripts/ERC20VotesRule.sh b/certora/scripts/ERC20VotesRule.sh index a29fd1ff9..6aac06c6c 100644 --- a/certora/scripts/ERC20VotesRule.sh +++ b/certora/scripts/ERC20VotesRule.sh @@ -19,5 +19,4 @@ certoraRun \ --optimistic_loop \ --rule ${rule} \ --msg "${msg}" \ - --staging \ # --rule_sanity \ \ No newline at end of file diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index 7adb3cca1..d3c95973d 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -10,7 +10,7 @@ methods { getPastTotalSupply(uint256) returns (uint256) delegate(address) _delegate(address, address) - delegateBySig(address, uint256, uint256, uint8, bytes32, bytes32) + // delegateBySig(address, uint256, uint256, uint8, bytes32, bytes32) totalSupply() returns (uint256) envfree _maxSupply() returns (uint224) envfree @@ -32,9 +32,13 @@ ghost userVotes(address) returns uint224; // sums the total votes for all users ghost totalVotes() returns mathint { - axiom totalVotes() > 0; + axiom totalVotes() >= 0; } +// helper + +invariant totalVotes_gte_accounts(address a, address b) + totalVotes() >= getVotes(a) + getVotes(b) hook Sstore _checkpoints[KEY address account][INDEX uint32 index].votes uint224 newVotes (uint224 oldVotes) STORAGE { @@ -95,32 +99,45 @@ invariant maxInt_constrains_numBlocks(address account) invariant fromBlock_constrains_numBlocks(address account) numCheckpoints(account) <= lastFromBlock(account) +// this fails, which makes sense because there is no require about the previous fromBlock invariant unique_checkpoints(address account) !doubleFromBlock(account) +// if an account has been delegated too, then both accounts must have a checkpoint +invariant delegated_implies_checkpoints(address delegator, address delegatee) + delegates(delegator) == delegatee => numCheckpoints(delegator) > 0 && numCheckpoints(delegatee) > 0 +{ preserved with (env e) { + require delegatee != 0; + require balanceOf(e, delegator) > 0; +}} + +// assumes neither account has delegated rule transfer_safe() { env e; uint256 amount; address a; address b; require a != b; + // requireInvariant totalVotes_gte_accounts(a, b); - uint256 votesA_pre = getVotes(a); - uint256 votesB_pre = getVotes(b); + address delegateA = delegates(a); + address delegateB = delegates(b); - require votesA_pre == erc20votes.balanceOf(e, a); - require votesB_pre == erc20votes.balanceOf(e, b); + uint256 votesA_pre = getVotes(delegateA); + uint256 votesB_pre = getVotes(delegateB); mathint totalVotes_pre = totalVotes(); erc20votes.transferFrom(e, a, b, amount); mathint totalVotes_post = totalVotes(); - uint256 votesA_post = getVotes(a); - uint256 votesB_post = getVotes(b); + uint256 votesA_post = getVotes(delegateA); + uint256 votesB_post = getVotes(delegateB); + // if an account that has not delegated transfers balance to an account that has, it will increase the total supply of votes assert totalVotes_pre == totalVotes_post, "transfer changed total supply"; - assert votesA_pre - votesA_post == amount, "a lost the proper amount of votes"; - assert votesB_post - votesB_pre == amount, "b lost the proper amount of votes"; + assert delegateA == delegates(a) && delegateB == delegates(b), "delegates changed by transfer"; + assert delegateA != 0 => votesA_pre - votesA_post == amount, "a lost the proper amount of votes"; + assert delegateB != 0 => votesB_post - votesB_pre == amount, "b lost the proper amount of votes"; } @@ -144,7 +161,9 @@ rule delegator_votes_removed() { rule delegatee_receives_votes() { env e; address delegator; address delegatee; + require delegator != delegatee; + require delegates(delegator) != delegatee; uint256 delegator_bal = balanceOf(e, delegator); uint256 votes_= getVotes(delegatee); @@ -156,13 +175,14 @@ rule delegatee_receives_votes() { assert _votes == votes_ + delegator_bal, "delegatee did not receive votes"; } -rule previous_delegatee_zerod() { +rule previous_delegatee_zeroed() { env e; address delegator; address delegatee; address third; - require delegator != delegatee; require third != delegatee; require third != delegator; + require delegates(delegator) == third; + // for third to have been delegated to, it must have a checkpoint uint256 delegator_bal = balanceOf(e, delegator); uint256 votes_ = getVotes(third); @@ -171,16 +191,14 @@ rule previous_delegatee_zerod() { uint256 _votes = getVotes(third); - assert votes_ == votes_ - delegator_bal, "votes not removed from the previous delegatee"; + assert _votes == votes_ - delegator_bal, "votes not removed from the previous delegatee"; } -// passes +// passes with rule sanity rule delegate_contained() { env e; address delegator; address delegatee; address other; - require delegator != delegatee; - require other != delegator; require other != delegatee; require other != delegates(delegator); From 92f07bae1ba9ae4b6b5914f0591304ec3bb1dfc1 Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Wed, 30 Mar 2022 17:12:30 -0700 Subject: [PATCH 153/254] typechecker error and skipped require bug --- certora/harnesses/ERC20VotesHarness.sol | 4 + certora/scripts/ERC20VotesRule.sh | 1 + certora/scripts/verifyERC20Votes.sh | 1 + certora/specs/ERC20Votes.spec | 160 +++++++++++++++++++----- 4 files changed, 137 insertions(+), 29 deletions(-) diff --git a/certora/harnesses/ERC20VotesHarness.sol b/certora/harnesses/ERC20VotesHarness.sol index b87879f80..c2cad2f6d 100644 --- a/certora/harnesses/ERC20VotesHarness.sol +++ b/certora/harnesses/ERC20VotesHarness.sol @@ -19,6 +19,10 @@ contract ERC20VotesHarness is ERC20Votes { _burn(account, amount); } + function unsafeNumCheckpoints(address account) public view returns (uint256) { + return _checkpoints[account].length; + } + function delegateBySig( address delegatee, uint256 nonce, diff --git a/certora/scripts/ERC20VotesRule.sh b/certora/scripts/ERC20VotesRule.sh index 6aac06c6c..64ff6a27d 100644 --- a/certora/scripts/ERC20VotesRule.sh +++ b/certora/scripts/ERC20VotesRule.sh @@ -19,4 +19,5 @@ certoraRun \ --optimistic_loop \ --rule ${rule} \ --msg "${msg}" \ + --staging "Eyal/SanityWithoutCallTrace" \ # --rule_sanity \ \ No newline at end of file diff --git a/certora/scripts/verifyERC20Votes.sh b/certora/scripts/verifyERC20Votes.sh index 7b23e29fe..b3b4e2035 100644 --- a/certora/scripts/verifyERC20Votes.sh +++ b/certora/scripts/verifyERC20Votes.sh @@ -19,4 +19,5 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --loop_iter 4 \ + --staging "Eyal/SanityWithoutCallTrace" \ --msg "${msg}" diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index d3c95973d..33bef7384 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -19,6 +19,7 @@ methods { ckptVotes(address, uint32) returns (uint224) envfree mint(address, uint256) burn(address, uint256) + unsafeNumCheckpoints(address) returns (uint256) envfree // solidity generated getters _delegates(address) returns (address) envfree @@ -32,13 +33,17 @@ ghost userVotes(address) returns uint224; // sums the total votes for all users ghost totalVotes() returns mathint { + init_state axiom totalVotes() == 0; axiom totalVotes() >= 0; } +ghost lastIndex(address) returns uint32; + // helper -invariant totalVotes_gte_accounts(address a, address b) - totalVotes() >= getVotes(a) + getVotes(b) +// blocked by tool error +invariant totalVotes_gte_accounts() + forall address a. forall address b. a != b => totalVotes() >= getVotes(a) + getVotes(b) hook Sstore _checkpoints[KEY address account][INDEX uint32 index].votes uint224 newVotes (uint224 oldVotes) STORAGE { @@ -47,6 +52,9 @@ hook Sstore _checkpoints[KEY address account][INDEX uint32 index].votes uint224 havoc totalVotes assuming totalVotes@new() == totalVotes@old() + to_mathint(newVotes) - to_mathint(userVotes(account)); + + havoc lastIndex assuming + lastIndex@new(account) == index; } @@ -57,12 +65,14 @@ ghost doubleFromBlock(address) returns bool { } + + hook Sstore _checkpoints[KEY address account][INDEX uint32 index].fromBlock uint32 newBlock (uint32 oldBlock) STORAGE { havoc lastFromBlock assuming lastFromBlock@new(account) == newBlock; havoc doubleFromBlock assuming - doubleFromBlock@new(account) == (newBlock == oldBlock); + doubleFromBlock@new(account) == (newBlock == lastFromBlock(account)); } rule sanity(method f) { @@ -77,8 +87,13 @@ invariant sanity_invariant() totalSupply() >= 0 // sum of user balances is >= total amount of delegated votes +// blocked by tool error invariant votes_solvency() to_mathint(totalSupply()) >= totalVotes() +{ preserved { + require forall address account. unsafeNumCheckpoints(account) < 4294967295; + requireInvariant totalVotes_gte_accounts(); +}} // for some checkpoint, the fromBlock is less than the current block number // passes but fails rule sanity from hash on delegate by sig @@ -90,54 +105,135 @@ invariant timestamp_constrains_fromBlock(address account, uint32 index, env e) } } -// numCheckpoints are less than maxInt -// passes -invariant maxInt_constrains_numBlocks(address account) - numCheckpoints(account) <= 4294967295 // 2^32 +// TODO add a note about this in the report +// // numCheckpoints are less than maxInt +// // passes because numCheckpoints does a safeCast +// invariant maxInt_constrains_numBlocks(address account) +// numCheckpoints(account) < 4294967295 // 2^32 + +// // fails because there are no checks to stop it +// invariant maxInt_constrains_ckptsLength(address account) +// unsafeNumCheckpoints(account) < 4294967295 // 2^32 // can't have more checkpoints for a given account than the last from block +// passes invariant fromBlock_constrains_numBlocks(address account) - numCheckpoints(account) <= lastFromBlock(account) - -// this fails, which makes sense because there is no require about the previous fromBlock -invariant unique_checkpoints(address account) - !doubleFromBlock(account) - -// if an account has been delegated too, then both accounts must have a checkpoint -invariant delegated_implies_checkpoints(address delegator, address delegatee) - delegates(delegator) == delegatee => numCheckpoints(delegator) > 0 && numCheckpoints(delegatee) > 0 -{ preserved with (env e) { - require delegatee != 0; - require balanceOf(e, delegator) > 0; + numCheckpoints(account) <= ckptFromBlock(account, numCheckpoints(account) - 1) +{ preserved with(env e) { + uint32 pos; + uint32 pos2; + requireInvariant fromBlock_greaterThanEq_pos(account, pos); + requireInvariant fromBlock_increasing(account, pos, pos2); + require e.block.number >= ckptFromBlock(account, numCheckpoints(account) - 1); // this should be true from the invariant above!! }} +// for any given checkpoint, the fromBlock must be greater than the checkpoint +// this proves the above invariant in combination with the below invariant +// if checkpoint has a greater fromBlock than the last, and the FromBlock is always greater than the pos. +// Then the number of positions must be less than the currentFromBlock +// ^note that the tool is assuming it's possible for the starting fromBlock to be 0 or anything, and does not know the current starting block +// passes + rule sanity +invariant fromBlock_greaterThanEq_pos(address account, uint32 pos) + ckptFromBlock(account, pos) >= pos + +// a larger index must have a larger fromBlock +// passes + rule sanity +invariant fromBlock_increasing(address account, uint32 pos, uint32 pos2) + pos > pos2 => ckptFromBlock(account, pos) > ckptFromBlock(account, pos2) + + +invariant no_delegate_no_checkpoints(address account) + delegates(account) == 0x0 => numCheckpoints(account) == 0 +{ preserved delegate(address delegatee) with(env e) { + require delegatee != 0; +} preserved _delegate(address delegator, address delegatee) with(env e) { + require delegatee != 0; +}} + +// converted from an invariant to a rule to slightly change the logic +// if the fromBlock is the same as before, then the number of checkpoints stays the same +// however if the fromBlock is new than the number of checkpoints increases +rule unique_checkpoints_rule(method f) { + env e; calldataarg args; + + require e.block.number > 0; // we don't care about this exception + + address account; + + require unsafeNumCheckpoints(account) < 4294967295; // 2^32 // we don't want to deal with the checkpoint overflow error here + + uint32 num_ckpts_ = numCheckpoints(account); + uint32 fromBlock_ = num_ckpts_ == 0 ? 0 : ckptFromBlock(account, num_ckpts_ - 1); + + f(e, args); + + uint32 _num_ckpts = numCheckpoints(account); + uint32 _fromBlock = _num_ckpts == 0 ? 0 : ckptFromBlock(account, _num_ckpts - 1); + + + // assert fromBlock_ == _fromBlock => num_ckpts_ == _num_ckpts, "same fromBlock, new checkpoint"; + assert doubleFromBlock(account) => num_ckpts_ == _num_ckpts, "same fromBlock, new checkpoint"; + // this assert fails consistently + // assert !doubleFromBlock(account) => ckpts_ != _ckpts, "new fromBlock but total checkpoints not being increased"; +} + // assumes neither account has delegated +// currently fails due to this scenario. A has maxint number of checkpoints +// an additional checkpoint is added which overflows and sets A's votes to 0 rule transfer_safe() { env e; uint256 amount; address a; address b; require a != b; + require delegates(a) != delegates(b); // confirmed if they both delegate to the same person then transfer keeps the votes the sameå + // requireInvariant fromBlock_constrains_numBlocks(a); + // requireInvariant fromBlock_constrains_numBlocks(b); // requireInvariant totalVotes_gte_accounts(a, b); - address delegateA = delegates(a); - address delegateB = delegates(b); + uint256 votesA_pre = getVotes(delegates(a)); + uint256 votesB_pre = getVotes(delegates(b)); - uint256 votesA_pre = getVotes(delegateA); - uint256 votesB_pre = getVotes(delegateB); + // for debugging + uint256 balA_ = balanceOf(e, a); + uint256 balB_ = balanceOf(e, b); mathint totalVotes_pre = totalVotes(); erc20votes.transferFrom(e, a, b, amount); + + + require lastIndex(delegates(a)) < 1000000; + require lastIndex(delegates(b)) < 1000000; mathint totalVotes_post = totalVotes(); - uint256 votesA_post = getVotes(delegateA); - uint256 votesB_post = getVotes(delegateB); + uint256 votesA_post = getVotes(delegates(a)); + uint256 votesB_post = getVotes(delegates(b)); + + // for debugging + uint256 _balA = balanceOf(e, a); + uint256 _balB = balanceOf(e, b); // if an account that has not delegated transfers balance to an account that has, it will increase the total supply of votes assert totalVotes_pre == totalVotes_post, "transfer changed total supply"; - assert delegateA == delegates(a) && delegateB == delegates(b), "delegates changed by transfer"; - assert delegateA != 0 => votesA_pre - votesA_post == amount, "a lost the proper amount of votes"; - assert delegateB != 0 => votesB_post - votesB_pre == amount, "b lost the proper amount of votes"; + assert delegates(a) != 0 => votesA_pre - votesA_post == amount, "A lost the wrong amount of votes"; + assert delegates(b) != 0 => votesB_post - votesB_pre == amount, "B lost the wrong amount of votes"; +} + +// for any given function f, if the delegate is changed the function must be delegate or delegateBySig +// passes +rule delegates_safe(method f) filtered {f -> (f.selector != delegate(address).selector && + f.selector != _delegate(address, address).selector && + f.selector != delegateBySig(address, uint256, uint256, uint8, bytes32, bytes32).selector) } +{ + env e; calldataarg args; + address account; + address pre = delegates(account); + + f(e, args); + + address post = delegates(account); + + assert pre == post, "invalid delegate change"; } @@ -158,18 +254,22 @@ rule delegator_votes_removed() { assert post == pre - balance, "delegator retained votes"; } +// delegates increases the delegatee's votes by the proper amount +// passes + rule sanity rule delegatee_receives_votes() { env e; address delegator; address delegatee; - require delegator != delegatee; require delegates(delegator) != delegatee; + require delegatee != 0x0; uint256 delegator_bal = balanceOf(e, delegator); uint256 votes_= getVotes(delegatee); _delegate(e, delegator, delegatee); + require lastIndex(delegatee) < 1000000; + uint256 _votes = getVotes(delegatee); assert _votes == votes_ + delegator_bal, "delegatee did not receive votes"; @@ -246,6 +346,8 @@ rule delegate_no_frontrunning(method f) { assert other_ == _other, "delegate not contained"; } + + // passes rule mint_increases_totalSupply() { From a982bee2350a3414877db3324bb6883928085ec8 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Thu, 31 Mar 2022 21:07:01 +0100 Subject: [PATCH 154/254] TC cleaning --- certora/munged/governance/TimelockController.sol | 7 ------- 1 file changed, 7 deletions(-) diff --git a/certora/munged/governance/TimelockController.sol b/certora/munged/governance/TimelockController.sol index c6d30c665..da38df8f3 100644 --- a/certora/munged/governance/TimelockController.sol +++ b/certora/munged/governance/TimelockController.sol @@ -353,11 +353,4 @@ contract TimelockController is AccessControl { emit MinDelayChange(_minDelay, newDelay); _minDelay = newDelay; } - - - - function scheduleCheck1(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) { - bool tmp = false; - require(tmp); - } } From 53b6ed80bbc707f24b30b56e7d2c1ab5c6de3408 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Thu, 31 Mar 2022 21:07:28 +0100 Subject: [PATCH 155/254] ERC1155 verification (not finished) --- certora/munged/token/ERC1155/ERC1155.sol | 12 +- certora/scripts/verifyERC1155.sh | 11 + certora/specs/ERC1155.spec | 563 +++++++++++++++++++++++ 3 files changed, 580 insertions(+), 6 deletions(-) create mode 100644 certora/scripts/verifyERC1155.sh create mode 100644 certora/specs/ERC1155.spec diff --git a/certora/munged/token/ERC1155/ERC1155.sol b/certora/munged/token/ERC1155/ERC1155.sol index a9a619842..b828b44ee 100644 --- a/certora/munged/token/ERC1155/ERC1155.sol +++ b/certora/munged/token/ERC1155/ERC1155.sol @@ -268,7 +268,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { uint256 id, uint256 amount, bytes memory data - ) internal virtual { + ) public virtual { // HARNESS: internal -> public require(to != address(0), "ERC1155: mint to the zero address"); address operator = _msgSender(); @@ -299,7 +299,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { uint256[] memory ids, uint256[] memory amounts, bytes memory data - ) internal virtual { + ) public virtual { // HARNESS: internal -> public require(to != address(0), "ERC1155: mint to the zero address"); require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); @@ -330,7 +330,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { address from, uint256 id, uint256 amount - ) internal virtual { + ) public virtual { // HARNESS: internal -> public require(from != address(0), "ERC1155: burn from the zero address"); address operator = _msgSender(); @@ -361,7 +361,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { address from, uint256[] memory ids, uint256[] memory amounts - ) internal virtual { + ) public virtual { // HARNESS: internal -> public require(from != address(0), "ERC1155: burn from the zero address"); require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); @@ -465,7 +465,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { uint256 id, uint256 amount, bytes memory data - ) private { + ) public { // HARNESS: private -> public if (to.isContract()) { try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { if (response != IERC1155Receiver.onERC1155Received.selector) { @@ -486,7 +486,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { uint256[] memory ids, uint256[] memory amounts, bytes memory data - ) private { + ) public { // HARNESS: private -> public if (to.isContract()) { try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response diff --git a/certora/scripts/verifyERC1155.sh b/certora/scripts/verifyERC1155.sh new file mode 100644 index 000000000..169df616d --- /dev/null +++ b/certora/scripts/verifyERC1155.sh @@ -0,0 +1,11 @@ +certoraRun \ + certora/munged/token/ERC1155/ERC1155.sol \ + --verify ERC1155:certora/specs/ERC1155.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --staging \ + --rule_sanity \ + --rule "$1" \ + --msg "$1 check" + \ No newline at end of file diff --git a/certora/specs/ERC1155.spec b/certora/specs/ERC1155.spec new file mode 100644 index 000000000..e811bd552 --- /dev/null +++ b/certora/specs/ERC1155.spec @@ -0,0 +1,563 @@ +methods { + isApprovedForAll(address, address) returns(bool) envfree + balanceOf(address, uint256) returns(uint256) envfree + balanceOfBatch(address[], uint256[]) returns(uint256[]) envfree + _doSafeBatchTransferAcceptanceCheck(address, address, address, uint256[], uint256[], bytes) envfree + _doSafeTransferAcceptanceCheck(address, address, address, uint256, uint256, bytes) envfree + + setApprovalForAll(address, bool) + safeTransferFrom(address, address, uint256, uint256, bytes) + safeBatchTransferFrom(address, address, uint256[], uint256[], bytes) + _mint(address, uint256, uint256, bytes) + _mintBatch(address, uint256[], uint256[], bytes) + _burn(address, uint256, uint256) + _burnBatch(address, uint256[], uint256[]) +} + + + +///////////////////////////////////////////////// +// Approval +///////////////////////////////////////////////// + + +// STATUS - verified +// Function $f, which is not setApprovalForAll, should not change approval +rule unexpectedAllowanceChange(method f, env e) filtered { f -> f.selector != setApprovalForAll(address, bool).selector } { + address account; address operator; + bool approveBefore = isApprovedForAll(account, operator); + + calldataarg args; + f(e, args); + + bool approveAfter = isApprovedForAll(account, operator); + + assert approveBefore == approveAfter, "You couldn't get king's approval this way!"; +} + + +// STATUS - verified +// approval can be changed only by owner +rule onlyOwnerCanApprove(env e){ + address owner; address operator; bool approved; + + bool aprovalBefore = isApprovedForAll(owner, operator); + + setApprovalForAll(e, operator, approved); + + bool aprovalAfter = isApprovedForAll(owner, operator); + + assert aprovalBefore != aprovalAfter => owner == e.msg.sender, "There should be only one owner"; +} + + +// STATUS - verified +// chech in which scenarios (if any) isApprovedForAll() revertes +rule approvalRevertCases(env e){ + address account; address operator; + isApprovedForAll@withrevert(account, operator); + assert !lastReverted, "Houston, we have a problem"; +} + + +// STATUS - verified +// Set approval changes only one approval +rule onlyOneAllowanceChange(method f, env e) { + address owner; address operator; address user; + bool approved; + + bool userApproveBefore = isApprovedForAll(owner, user); + + setApprovalForAll(e, operator, approved); + + bool userApproveAfter = isApprovedForAll(owner, user); + + assert userApproveBefore != userApproveAfter => (e.msg.sender == owner && operator == user), "Imposter!"; +} + + + +///////////////////////////////////////////////// +// Balance +///////////////////////////////////////////////// + + +// STATUS - verified +// Function $f, which is not one of transfers, mints and burns, should not change balanceOf of a user +rule unexpectedBalanceChange(method f, env e) + filtered { f -> f.selector != safeTransferFrom(address, address, uint256, uint256, bytes).selector + && f.selector != safeBatchTransferFrom(address, address, uint256[], uint256[], bytes).selector + && f.selector != _mint(address, uint256, uint256, bytes).selector + && f.selector != _mintBatch(address, uint256[], uint256[], bytes).selector + && f.selector != _burn(address, uint256, uint256).selector + && f.selector != _burnBatch(address, uint256[], uint256[]).selector } { + address from; uint256 id; + uint256 balanceBefore = balanceOf(from, id); + + calldataarg args; + f(e, args); + + uint256 balanceAfter = balanceOf(from, id); + + assert balanceBefore == balanceAfter, "How you dare to take my money?"; +} + + +// STATUS - verified +// chech in which scenarios balanceOf() revertes +rule balanceOfRevertCases(env e){ + address account; uint256 id; + balanceOf@withrevert(account, id); + assert lastReverted => account == 0, "Houston, we have a problem"; +} + + +// STATUS - verified +// chech in which scenarios balanceOfBatch() revertes +rule balanceOfBatchRevertCases(env e){ + address[] accounts; uint256[] ids; + address account1; address account2; address account3; + uint256 id1; uint256 id2; uint256 id3; + + require accounts.length == 3; + require ids.length == 3; + + require accounts[0] == account1; require accounts[1] == account2; require accounts[2] == account3; + + balanceOfBatch@withrevert(accounts, ids); + assert lastReverted => (account1 == 0 || account2 == 0 || account3 == 0), "Houston, we have a problem"; +} + + + +///////////////////////////////////////////////// +// Transfer +///////////////////////////////////////////////// + + +// STATUS - +// cannot transfer more than `from` balance (safeTransferFrom version) +rule cannotTransferMoreSingle(env e){ + address from; address to; uint256 id; uint256 amount; bytes data; + uint256 balanceBefore = balanceOf(from, id); + + safeTransferFrom@withrevert(e, from, to, id, amount, data); + + assert amount > balanceBefore => lastReverted, "Achtung! Scammer!"; + assert to == 0 => lastReverted, "Achtung! Scammer!"; +} + + +// STATUS - +// cannot transfer more than allowed (safeBatchTransferFrom version) +rule cannotTransferMoreBatch(env e){ + address from; address to; uint256[] ids; uint256[] amounts; bytes data; + uint256 idToCheck1; uint256 amountToCheck1; + uint256 idToCheck2; uint256 amountToCheck2; + uint256 idToCheck3; uint256 amountToCheck3; + + uint256 balanceBefore1 = balanceOf(from, idToCheck1); + uint256 balanceBefore2 = balanceOf(from, idToCheck2); + uint256 balanceBefore3 = balanceOf(from, idToCheck3); + + require ids.length == 3; + require amounts.length == 3; + require ids[0] == idToCheck1; require amounts[0] == amountToCheck1; + require ids[1] == idToCheck2; require amounts[1] == amountToCheck2; + require ids[2] == idToCheck3; require amounts[2] == amountToCheck3; + + safeBatchTransferFrom@withrevert(e, from, to, ids, amounts, data); + + assert (amountToCheck1 > balanceBefore1 || amountToCheck2 > balanceBefore2 || amountToCheck2 > balanceBefore2) => lastReverted, "Achtung! Scammer!"; +} + + +// STATUS - (added debug vars) +// safeBatchTransferFrom should revert if `to` is 0 address or if arrays length is different +rule revertOfTransferBatch(env e){ + address from; address to; uint256[] ids; uint256[] amounts; bytes data; + + uint256 idTMP = ids.length; + uint256 amountsTMP = amounts.length; + + safeBatchTransferFrom@withrevert(e, from, to, ids, amounts, data); + + assert ids.length != amounts.length => lastReverted, "Achtung! Scammer!"; + assert to == 0 => lastReverted, "Achtung! Scammer!"; +} + + +// STATUS - verified +// Sender calling safeTransferFrom should only reduce 'from' balance and not other's if sending amount is greater than 0 +rule transferBalanceReduceEffect(env e){ + address from; address to; address other; + uint256 id; uint256 amount; + bytes data; + + require other != to; + require amount > 0; + + uint256 otherBalanceBefore = balanceOf(other, id); + + safeTransferFrom(e, from, to, id, amount, data); + + uint256 otherBalanceAfter = balanceOf(other, id); + + assert from != other => otherBalanceBefore == otherBalanceAfter, "Don't touch my money!"; +} + + +// STATUS - +// Sender calling safeTransferFrom should only reduce 'to' balance and not other's if sending amount is greater than 0 +rule transferBalanceIncreaseEffect(env e){ + address from; address to; address other; + uint256 id; uint256 amount; + bytes data; + + require from != other; + + uint256 otherBalanceBefore = balanceOf(other, id); + + safeTransferFrom(e, from, to, id, amount, data); + + uint256 otherBalanceAfter = balanceOf(other, id); + + assert other != to => otherBalanceBefore == otherBalanceAfter, "Don't touch my money!"; +} + + +// STATUS - +// Sender calling safeTransferFrom should only reduce 'from' balance and not other's if sending amount is greater than 0 +rule transferBatchBalanceFromEffect(env e){ + address from; address to; address other; + uint256[] ids; uint256[] amounts; + uint256 id1; uint256 amount1; uint256 id2; uint256 amount2; uint256 id3; uint256 amount3; + bytes data; + + require other != to; + + // require ids.length == 3; + // require amounts.length == 3; + + // require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; + // require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; + // require amount1 > 0; require amount2 > 0; require amount3 > 0; + + uint256 otherBalanceBefore1 = balanceOf(other, id1); + uint256 otherBalanceBefore2 = balanceOf(other, id2); + uint256 otherBalanceBefore3 = balanceOf(other, id3); + + safeBatchTransferFrom(e, from, to, ids, amounts, data); + + uint256 otherBalanceAfter1 = balanceOf(other, id1); + uint256 otherBalanceAfter2 = balanceOf(other, id2); + uint256 otherBalanceAfter3 = balanceOf(other, id3); + + assert from != other => (otherBalanceBefore1 == otherBalanceAfter1 + && otherBalanceBefore2 == otherBalanceAfter2 + && otherBalanceBefore3 == otherBalanceAfter3), "Don't touch my money!"; +} + + +// STATUS - +// Sender calling safeBatchTransferFrom should only reduce 'to' balance and not other's if sending amount is greater than 0 +rule transferBatchBalanceToEffect(env e){ + address from; address to; address other; + uint256[] ids; uint256[] amounts; + uint256 id1; uint256 amount1; uint256 id2; uint256 amount2; uint256 id3; uint256 amount3; + bytes data; + + require other != from; + + // require ids.length == 3; + // require amounts.length == 3; + + // require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; + // require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; + // require amount1 > 0; require amount2 > 0; require amount3 > 0; + + uint256 otherBalanceBefore1 = balanceOf(other, id1); + uint256 otherBalanceBefore2 = balanceOf(other, id2); + uint256 otherBalanceBefore3 = balanceOf(other, id3); + + safeBatchTransferFrom(e, from, to, ids, amounts, data); + + uint256 otherBalanceAfter1 = balanceOf(other, id1); + uint256 otherBalanceAfter2 = balanceOf(other, id2); + uint256 otherBalanceAfter3 = balanceOf(other, id3); + + assert other != to => (otherBalanceBefore1 == otherBalanceAfter1 + && otherBalanceBefore2 == otherBalanceAfter2 + && otherBalanceBefore3 == otherBalanceAfter3), "Don't touch my money!"; +} + + +// STATUS - verified +// cannot transfer without approval (safeTransferFrom version) +rule noTransferForNotApproved(env e) { + address from; address operator; + address to; uint256 id; uint256 amount; bytes data; + + require from != e.msg.sender; + + bool approve = isApprovedForAll(from, e.msg.sender); + + safeTransferFrom@withrevert(e, from, to, id, amount, data); + + assert !approve => lastReverted, "You don't have king's approval!"; +} + + +// STATUS - +// cannot transfer without approval (safeBatchTransferFrom version) +rule noTransferBatchForNotApproved(env e) { + address from; address operator; address to; + bytes data; + uint256[] ids; uint256[] amounts; + + require from != e.msg.sender; + + bool approve = isApprovedForAll(from, e.msg.sender); + + safeBatchTransferFrom@withrevert(e, from, to, ids, amounts, data); + + assert !approve => lastReverted, "You don't have king's approval!"; +} + + +// STATUS - +// safeTransferFrom doesn't affect any approval +rule noTransferEffectOnApproval(env e){ + address from; address to; + address owner; address operator; + uint256 id; uint256 amount; + bytes data; + + bool approveBefore = isApprovedForAll(owner, operator); + + safeTransferFrom(e, from, to, id, amount, data); + + bool approveAfter = isApprovedForAll(owner, operator); + + assert approveBefore == approveAfter, "Something was effected"; +} + + +// STATUS - +// safeTransferFrom doesn't affect any approval +rule noTransferBatchEffectOnApproval(env e){ + address from; address to; + address owner; address operator; + uint256[] ids; uint256[] amounts; + bytes data; + + bool approveBefore = isApprovedForAll(owner, operator); + + safeBatchTransferFrom(e, from, to, ids, amounts, data); + + bool approveAfter = isApprovedForAll(owner, operator); + + assert approveBefore == approveAfter, "Something was effected"; +} + + +///////////////////////////////////////////////// +// Mint +///////////////////////////////////////////////// + + +// STATUS - verified +// the user cannot mint more than max_uint256 +rule cantMintMoreSingle(env e){ + address to; uint256 id; uint256 amount; bytes data; + + require to_mathint(balanceOf(to, id) + amount) > max_uint256; + + _mint@withrevert(e, to, id, amount, data); + + assert lastReverted, "Don't be too greedy!"; +} + + +// STATUS - verified +// the user cannot mint more than max_uint256 (batch version) +rule cantMintMoreBatch(env e){ + address to; bytes data; + uint256 id1; uint256 id2; uint256 id3; + uint256 amount1; uint256 amount2; uint256 amount3; + uint256[] ids; uint256[] amounts; + + require ids.length == 3; + require amounts.length == 3; + + require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; + require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; + + require to_mathint(balanceOf(to, id1) + amount1) > max_uint256 + || to_mathint(balanceOf(to, id2) + amount2) > max_uint256 + || to_mathint(balanceOf(to, id3) + amount3) > max_uint256; + + _mintBatch@withrevert(e, to, ids, amounts, data); + + assert lastReverted, "Don't be too greedy!"; +} + + +// rule mintRevert(env e){ +// address operator; +// address from; +// address to; +// uint256 id; +// uint256 amount; +// bytes data; +// +// require operator == e.msg.sender; +// require from == 0; +// +// _doSafeTransferAcceptanceCheck@withrevert(operator, from, to, id, amount, data); +// +// bool acceptanceCheck = lastReverted; +// +// _mint@withrevert(e, to, id, amount, data); +// +// bool mintRevert = lastReverted; +// +// assert acceptanceCheck => mintRevert, "reverts are wrong"; +// } + + + +///////////////////////////////////////////////// +// Burn +///////////////////////////////////////////////// + + +// STATUS - +// check that burn updates `from` balance correctly +rule burnCorrectWork(env e){ + address from; uint256 id; uint256 amount; + + uint256 otherBalanceBefore = balanceOf(from, id); + + _burn(e, from, id, amount); + + uint256 otherBalanceAfter = balanceOf(from, id); + + assert otherBalanceBefore == otherBalanceAfter + amount, "Something is wrong"; +} + + +// STATUS - +// check that burnBatch updates `from` balance correctly +rule burnBatchCorrectWork(env e){ + address from; + uint256 id1; uint256 id2; uint256 id3; + uint256 amount1; uint256 amount2; uint256 amount3; + uint256[] ids; uint256[] amounts; + + require ids.length == 3; + require amounts.length == 3; + + require id1 != id2 && id2 != id3 && id3 != id1; + require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; + require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; + + uint256 otherBalanceBefore1 = balanceOf(from, id1); + uint256 otherBalanceBefore2 = balanceOf(from, id2); + uint256 otherBalanceBefore3 = balanceOf(from, id3); + + _burnBatch(e, from, ids, amounts); + + uint256 otherBalanceAfter1 = balanceOf(from, id1); + uint256 otherBalanceAfter2 = balanceOf(from, id2); + uint256 otherBalanceAfter3 = balanceOf(from, id3); + + assert otherBalanceBefore1 == otherBalanceAfter1 + amount1 + && otherBalanceBefore2 == otherBalanceAfter2 + amount2 + && otherBalanceBefore3 == otherBalanceAfter3 + amount3 + , "Something is wrong"; +} + + +// STATUS - verified +// the user cannot mint more than max_uint256 +rule cantBurnMoreSingle(env e){ + address from; uint256 id; uint256 amount; + + require to_mathint(balanceOf(from, id) - amount) < 0; + + _burn@withrevert(e, from, id, amount); + + assert lastReverted, "Don't be too greedy!"; +} + + +// STATUS - +// burn changes only `from` balance +rule cantBurnOtherBalances(env e){ + address from; uint256 id; uint256 amount; + address other; + + uint256 otherBalanceBefore = balanceOf(other, id); + + _burn(e, from, id, amount); + + uint256 otherBalanceAfter = balanceOf(other, id); + + assert other != from => otherBalanceBefore == otherBalanceAfter, "I like to see your money disappearing"; +} + + +// STATUS - verified +// the user cannot mint more than max_uint256 (batch version) +rule cantBurnMoreBatch(env e){ + address from; + uint256 id1; uint256 id2; uint256 id3; + uint256 amount1; uint256 amount2; uint256 amount3; + uint256[] ids; uint256[] amounts; + + require ids.length == 3; + + require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; + require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; + + require to_mathint(balanceOf(from, id1) - amount1) < 0 + || to_mathint(balanceOf(from, id2) - amount2) < 0 + || to_mathint(balanceOf(from, id3) - amount3) < 0 ; + + _burnBatch@withrevert(e, from, ids, amounts); + + assert lastReverted, "Don't be too greedy!"; +} + + +// STATUS - +// burnBatch changes only `from` balance +rule cantBurnbatchOtherBalances(env e){ + address from; + uint256 id1; uint256 id2; uint256 id3; + uint256 amount1; uint256 amount2; uint256 amount3; + uint256[] ids; uint256[] amounts; + address other; + + require ids.length == 3; + require amounts.length == 3; + + require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; + require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; + + uint256 otherBalanceBefore1 = balanceOf(other, id1); + uint256 otherBalanceBefore2 = balanceOf(other, id2); + uint256 otherBalanceBefore3 = balanceOf(other, id3); + + _burnBatch(e, from, ids, amounts); + + uint256 otherBalanceAfter1 = balanceOf(other, id1); + uint256 otherBalanceAfter2 = balanceOf(other, id2); + uint256 otherBalanceAfter3 = balanceOf(other, id3); + + assert other != from => (otherBalanceBefore1 == otherBalanceAfter1 + || otherBalanceBefore2 == otherBalanceAfter2 + || otherBalanceBefore3 == otherBalanceAfter3) + , "I like to see your money disappearing"; +} From 50cf82823e719e1316cba0247fabc083d5962b97 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Thu, 31 Mar 2022 21:08:00 +0100 Subject: [PATCH 156/254] one more TC cleaning --- certora/scripts/verifyTimelock.sh | 7 +-- certora/specs/TimelockController.spec | 68 ++++++++++++++++----------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/certora/scripts/verifyTimelock.sh b/certora/scripts/verifyTimelock.sh index 718c13c60..93bb92f75 100644 --- a/certora/scripts/verifyTimelock.sh +++ b/certora/scripts/verifyTimelock.sh @@ -3,8 +3,9 @@ certoraRun \ --verify TimelockControllerHarness:certora/specs/TimelockController.spec \ --solc solc8.2 \ --optimistic_loop \ - --staging alex/unify-hash-functions \ + --loop_iter 3 \ + --staging alex/new-dt-hashing-alpha \ --rule_sanity \ --rule "$1" \ - --msg "$1 false check with hash" - \ No newline at end of file + --msg "$1" + \ No newline at end of file diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec index 8efcbb508..65aedeba8 100644 --- a/certora/specs/TimelockController.spec +++ b/certora/specs/TimelockController.spec @@ -5,16 +5,13 @@ methods { _DONE_TIMESTAMP() returns(uint256) envfree _minDelay() returns(uint256) envfree getMinDelay() returns(uint256) envfree + hashOperation(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt) returns(bytes32) envfree cancel(bytes32) - schedule(address, uint256, bytes, bytes32, bytes32, uint256) + schedule(address, uint256, bytes32, bytes32, bytes32, uint256) execute(address, uint256, bytes, bytes32, bytes32) - - hashOperation(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt) returns(bytes32) envfree // => uniqueHashGhost(target, value, data, predecessor, salt) } - - //////////////////////////////////////////////////////////////////////////// // Definitions // //////////////////////////////////////////////////////////////////////////// @@ -45,6 +42,19 @@ function hashIdCorrelation(bytes32 id, address target, uint256 value, bytes data } +function executionsCall(method f, env e, address target, uint256 value, bytes data, + bytes32 predecessor, bytes32 salt, uint256 delay, + address[] targets, uint256[] values, bytes[] datas) { + if (f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector) { + execute(e, target, value, data, predecessor, salt); + } else if (f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector) { + executeBatch(e, targets, values, datas, predecessor, salt); + } else { + calldataarg args; + f(e, args); + } +} + //////////////////////////////////////////////////////////////////////////// // Ghosts // //////////////////////////////////////////////////////////////////////////// @@ -63,22 +73,24 @@ function hashIdCorrelation(bytes32 id, address target, uint256 value, bytes data rule keccakCheck(method f, env e){ - address target; - uint256 value; - bytes data; - bytes32 predecessor; - bytes32 salt; + address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; + address targetRand; uint256 valueRand; bytes dataRand; bytes32 predecessorRand; bytes32 saltRand; - require data.length < 3; + require data.length < 4; + // uint256 freshIndex; + // require freshIndex <= data.length + + // require target != targetRand || value != valueRand || data[freshIndex] != dataRand[freshIndex] || predecessor != predecessorRand || salt != saltRand; bytes32 a = hashOperation(target, value, data, predecessor, salt); bytes32 b = hashOperation(target, value, data, predecessor, salt); + // bytes32 c = hashOperation(targetRand, valueRand, dataRand, predecessorRand, saltRand); assert a == b, "hashes are different"; + // assert a != c, "hashes are the same"; } - ///////////////////////////////////////////////////////////// // STATE TRANSITIONS ///////////////////////////////////////////////////////////// @@ -99,7 +111,7 @@ rule unsetPendingTransitionGeneral(method f, env e){ } -// STATUS - verified +// STATUS - // unset() -> pending() via schedule() and scheduleBatch() only rule unsetPendingTransitionMethods(method f, env e){ bytes32 id; @@ -109,8 +121,10 @@ rule unsetPendingTransitionMethods(method f, env e){ calldataarg args; f(e, args); - assert pending(id) => f.selector == schedule(address, uint256, bytes, bytes32, bytes32, uint256).selector - || f.selector == scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256).selector , "Why do we need to follow the schedule?"; + bool tmp = pending(id); + + assert pending(id) => (f.selector == schedule(address, uint256, bytes, bytes32, bytes32, uint256).selector + || f.selector == scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256).selector), "Why do we need to follow the schedule?"; } @@ -181,17 +195,19 @@ rule minDealyOnlyChange(method f, env e){ // execute() is the only way to set timestamp to 1 rule getTimestampOnlyChange(method f, env e){ bytes32 id; - address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; + address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; uint256 delay; + address[] targets; uint256[] values; bytes[] datas; + + require (targets[0] == target && values[0] == value && datas[0] == data) + || (targets[1] == target && values[1] == value && datas[1] == data) + || (targets[2] == target && values[2] == value && datas[2] == data); - require getTimestamp(id) != 1; hashIdCorrelation(id, target, value, data, predecessor, salt); - calldataarg args; - // write helper function with values from hashOperation() call; - f(e, args); + executionsCall(f, e, target, value, data, predecessor, salt, delay, targets, values, datas); assert getTimestamp(id) == 1 => f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector - || f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector , "Did you find a way to break the system?"; + || f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector, "Did you find a way to break the system?"; } @@ -233,20 +249,16 @@ rule executeRevertFromUnset(method f, env e, env e2){ address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; bytes32 id; - // hashIdCorrelation(id, target, value, data, predecessor, salt); - require data.length < 4; - // require hashOperation(target, value, data, predecessor, salt) == id; + hashIdCorrelation(id, target, value, data, predecessor, salt); require unset(id); - scheduleCheck1@withrevert(e, id); - - // execute@withrevert(e, target, value, data, predecessor, salt); + execute@withrevert(e, target, value, data, predecessor, salt); assert lastReverted, "you go against execution nature"; } -// STATUS - verified +// STATUS - // Execute reverts => state returns to pending rule executeRevertEffectCheck(method f, env e){ address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; From ec4e77397f0b4e2be145cc6238ceb5bd3486f94c Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Thu, 31 Mar 2022 21:08:14 +0100 Subject: [PATCH 157/254] AccessControl verification --- certora/scripts/verifyAccessControl.sh | 9 +++ certora/specs/AccessControl.spec | 85 ++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 certora/scripts/verifyAccessControl.sh create mode 100644 certora/specs/AccessControl.spec diff --git a/certora/scripts/verifyAccessControl.sh b/certora/scripts/verifyAccessControl.sh new file mode 100644 index 000000000..6b1a7ba05 --- /dev/null +++ b/certora/scripts/verifyAccessControl.sh @@ -0,0 +1,9 @@ +certoraRun \ + certora/harnesses/AccessControlHarness.sol \ + --verify AccessControlHarness:certora/specs/AccessControl.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --staging \ + --rule_sanity \ + --msg "modifier check" + \ No newline at end of file diff --git a/certora/specs/AccessControl.spec b/certora/specs/AccessControl.spec new file mode 100644 index 000000000..83f925d86 --- /dev/null +++ b/certora/specs/AccessControl.spec @@ -0,0 +1,85 @@ +methods { + grantRole(bytes32, address) + revokeRole(bytes32, address) + _checkRole(bytes32) + safeTransferFrom(address, address, uint256, uint256, bytes) + safeBatchTransferFrom(address, address, uint256[], uint256[], bytes) + + getRoleAdmin(bytes32) returns(bytes32) envfree + hasRole(bytes32, address) returns(bool) envfree +} + + +// STATUS - verified +// check onlyRole modifier for grantRole() +rule onlyRoleModifierCheckGrant(env e){ + bytes32 role; address account; + + _checkRole@withrevert(e, getRoleAdmin(role)); + bool checkRevert = lastReverted; + + grantRole@withrevert(e, role, account); + bool grantRevert = lastReverted; + + assert checkRevert => grantRevert, "modifier goes bananas"; +} + + +// STATUS - verified +// check onlyRole modifier for revokeRole() +rule onlyRoleModifierCheckRevoke(env e){ + bytes32 role; address account; + + _checkRole@withrevert(e, getRoleAdmin(role)); + bool checkRevert = lastReverted; + + revokeRole@withrevert(e, role, account); + bool revokeRevert = lastReverted; + + assert checkRevert => revokeRevert, "modifier goes bananas"; +} + + +// STATUS - verified +// grantRole() does not affect another accounts +rule grantRoleEffect(env e){ + bytes32 role; address account; + bytes32 anotherRole; address nonEffectedAcc; + require account != nonEffectedAcc; + + bool hasRoleBefore = hasRole(anotherRole, nonEffectedAcc); + + grantRole(e, role, account); + + bool hasRoleAfter = hasRole(anotherRole, nonEffectedAcc); + + assert hasRoleBefore == hasRoleAfter, "modifier goes bananas"; +} + + +// STATUS - verified +// grantRole() does not affect another accounts +rule revokeRoleEffect(env e){ + bytes32 role; address account; + bytes32 anotherRole; address nonEffectedAcc; + require account != nonEffectedAcc; + + bool hasRoleBefore = hasRole(anotherRole, nonEffectedAcc); + + revokeRole(e, role, account); + + bool hasRoleAfter = hasRole(anotherRole, nonEffectedAcc); + + assert hasRoleBefore == hasRoleAfter, "modifier goes bananas"; +} + + + + + + + + + + + From 22827223c0016df14df1e2a5fe87f64e735acb9d Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Thu, 31 Mar 2022 14:40:20 -0700 Subject: [PATCH 158/254] more passing --- certora/scripts/ERC20VotesRule.sh | 4 +- certora/specs/ERC20Votes.spec | 88 ++++++++++++++----------------- 2 files changed, 43 insertions(+), 49 deletions(-) diff --git a/certora/scripts/ERC20VotesRule.sh b/certora/scripts/ERC20VotesRule.sh index 64ff6a27d..0921d1df2 100644 --- a/certora/scripts/ERC20VotesRule.sh +++ b/certora/scripts/ERC20VotesRule.sh @@ -19,5 +19,5 @@ certoraRun \ --optimistic_loop \ --rule ${rule} \ --msg "${msg}" \ - --staging "Eyal/SanityWithoutCallTrace" \ - # --rule_sanity \ \ No newline at end of file + --staging "alex/new-dt-hashing-alpha" \ + --rule_sanity \ \ No newline at end of file diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index 33bef7384..5cccdeb9f 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -153,14 +153,18 @@ invariant no_delegate_no_checkpoints(address account) // converted from an invariant to a rule to slightly change the logic // if the fromBlock is the same as before, then the number of checkpoints stays the same // however if the fromBlock is new than the number of checkpoints increases +// passes, fails rule sanity because tautology check seems to be bugged rule unique_checkpoints_rule(method f) { env e; calldataarg args; - require e.block.number > 0; // we don't care about this exception + // require e.block.number > 0; // we don't care about this exception + address account; + // address delegates_pre = delegates(account); - require unsafeNumCheckpoints(account) < 4294967295; // 2^32 // we don't want to deal with the checkpoint overflow error here + + // require unsafeNumCheckpoints(account) < 1000000; // 2^32 // we don't want to deal with the checkpoint overflow error here uint32 num_ckpts_ = numCheckpoints(account); uint32 fromBlock_ = num_ckpts_ == 0 ? 0 : ckptFromBlock(account, num_ckpts_ - 1); @@ -171,8 +175,8 @@ rule unique_checkpoints_rule(method f) { uint32 _fromBlock = _num_ckpts == 0 ? 0 : ckptFromBlock(account, _num_ckpts - 1); - // assert fromBlock_ == _fromBlock => num_ckpts_ == _num_ckpts, "same fromBlock, new checkpoint"; - assert doubleFromBlock(account) => num_ckpts_ == _num_ckpts, "same fromBlock, new checkpoint"; + assert fromBlock_ == _fromBlock => num_ckpts_ == _num_ckpts || _num_ckpts == 1, "same fromBlock, new checkpoint"; + // assert doubleFromBlock(account) => num_ckpts_ == _num_ckpts, "same fromBlock, new checkpoint"; // this assert fails consistently // assert !doubleFromBlock(account) => ckpts_ != _ckpts, "new fromBlock but total checkpoints not being increased"; } @@ -180,16 +184,22 @@ rule unique_checkpoints_rule(method f) { // assumes neither account has delegated // currently fails due to this scenario. A has maxint number of checkpoints // an additional checkpoint is added which overflows and sets A's votes to 0 +// passes + rule sanity (- a bad tautology check) rule transfer_safe() { env e; uint256 amount; address a; address b; - require a != b; - require delegates(a) != delegates(b); // confirmed if they both delegate to the same person then transfer keeps the votes the sameå + // require a != b; + require delegates(a) != delegates(b); // confirmed if they both delegate to the same person then transfer keeps the votes the same // requireInvariant fromBlock_constrains_numBlocks(a); // requireInvariant fromBlock_constrains_numBlocks(b); // requireInvariant totalVotes_gte_accounts(a, b); + // require lastIndex(delegates(a)) < 1000000; + // require lastIndex(delegates(b)) < 1000000; + require numCheckpoints(delegates(a)) < 1000000; + require numCheckpoints(delegates(b)) < 1000000; + uint256 votesA_pre = getVotes(delegates(a)); uint256 votesB_pre = getVotes(delegates(b)); @@ -201,9 +211,9 @@ rule transfer_safe() { erc20votes.transferFrom(e, a, b, amount); - - require lastIndex(delegates(a)) < 1000000; - require lastIndex(delegates(b)) < 1000000; + + // require lastIndex(delegates(a)) < 1000000; + // require lastIndex(delegates(b)) < 1000000; mathint totalVotes_post = totalVotes(); uint256 votesA_post = getVotes(delegates(a)); @@ -236,24 +246,6 @@ rule delegates_safe(method f) filtered {f -> (f.selector != delegate(address).se assert pre == post, "invalid delegate change"; } - -rule delegator_votes_removed() { - env e; - address delegator; address delegatee; - - require delegator != delegatee; - require delegates(delegator) == 0; // has not delegated - - uint256 pre = getVotes(delegator); - - _delegate(e, delegator, delegatee); - - uint256 balance = balanceOf(e, delegator); - - uint256 post = getVotes(delegator); - assert post == pre - balance, "delegator retained votes"; -} - // delegates increases the delegatee's votes by the proper amount // passes + rule sanity rule delegatee_receives_votes() { @@ -263,6 +255,7 @@ rule delegatee_receives_votes() { require delegates(delegator) != delegatee; require delegatee != 0x0; + uint256 delegator_bal = balanceOf(e, delegator); uint256 votes_= getVotes(delegatee); @@ -275,14 +268,14 @@ rule delegatee_receives_votes() { assert _votes == votes_ + delegator_bal, "delegatee did not receive votes"; } -rule previous_delegatee_zeroed() { +// passes + rule sanity +rule previous_delegatee_votes_removed() { env e; address delegator; address delegatee; address third; require third != delegatee; - require third != delegator; require delegates(delegator) == third; - // for third to have been delegated to, it must have a checkpoint + require numCheckpoints(third) < 1000000; uint256 delegator_bal = balanceOf(e, delegator); uint256 votes_ = getVotes(third); @@ -291,7 +284,7 @@ rule previous_delegatee_zeroed() { uint256 _votes = getVotes(third); - assert _votes == votes_ - delegator_bal, "votes not removed from the previous delegatee"; + assert third != 0x0 => _votes == votes_ - delegator_bal, "votes not removed from the previous delegatee"; } // passes with rule sanity @@ -311,39 +304,40 @@ rule delegate_contained() { assert votes_ == _votes, "votes not contained"; } -// checks all of the above rules with front running +// checks all of the above delegate rules with front running rule delegate_no_frontrunning(method f) { env e; calldataarg args; address delegator; address delegatee; address third; address other; - require delegator != delegatee; require delegates(delegator) == third; + require third != delegatee; require other != third; + require other != delegatee; + require delegatee != 0x0; - uint256 delegator_bal = erc20votes.balanceOf(e, delegator); + require numCheckpoints(delegatee) < 1000000; + require numCheckpoints(third) < 1000000; - uint256 dr_ = getVotes(delegator); - uint256 de_ = getVotes(delegatee); - uint256 third_ = getVotes(third); - uint256 other_ = getVotes(other); + uint256 delegatee_votes_ = getVotes(delegatee); + uint256 third_votes_ = getVotes(third); + uint256 other_votes_ = getVotes(other); // require third is address for previous delegator f(e, args); + uint256 delegator_bal = erc20votes.balanceOf(e, delegator); _delegate(e, delegator, delegatee); - uint256 _dr = getVotes(delegator); - uint256 _de = getVotes(delegatee); - uint256 _third = getVotes(third); - uint256 _other = getVotes(other); + uint256 _delegatee_votes = getVotes(delegatee); + uint256 _third_votes = getVotes(third); + uint256 _other_votes = getVotes(other); - // delegator loses all of their votes + // previous delegatee loses all of their votes // delegatee gains that many votes // third loses any votes delegated to them - assert _dr == 0, "delegator retained votes"; - assert _de == de_ + delegator_bal, "delegatee not received votes"; - assert _third != 0 => _third == third_ - delegator_bal, "votes not removed from third"; - assert other_ == _other, "delegate not contained"; + assert _delegatee_votes == delegatee_votes_ + delegator_bal, "delegatee did not receive votes"; + assert third != 0 => _third_votes == third_votes_ - delegator_bal, "votes not removed from third"; + assert other_votes_ == _other_votes, "delegate not contained"; } From 44fba3e2eb196d8461f02e92a7980c9a551065ed Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Fri, 1 Apr 2022 17:58:41 +0100 Subject: [PATCH 159/254] ERC1155 finished --- certora/specs/ERC1155.spec | 364 ++++++++++++++++++++++++++++++++----- 1 file changed, 319 insertions(+), 45 deletions(-) diff --git a/certora/specs/ERC1155.spec b/certora/specs/ERC1155.spec index e811bd552..26d77a099 100644 --- a/certora/specs/ERC1155.spec +++ b/certora/specs/ERC1155.spec @@ -17,7 +17,7 @@ methods { ///////////////////////////////////////////////// -// Approval +// Approval (4/4) ///////////////////////////////////////////////// @@ -60,8 +60,8 @@ rule approvalRevertCases(env e){ } -// STATUS - verified -// Set approval changes only one approval +// STATUS - verified +// setApproval changes only one approval rule onlyOneAllowanceChange(method f, env e) { address owner; address operator; address user; bool approved; @@ -78,7 +78,7 @@ rule onlyOneAllowanceChange(method f, env e) { ///////////////////////////////////////////////// -// Balance +// Balance (3/3) ///////////////////////////////////////////////// @@ -131,11 +131,97 @@ rule balanceOfBatchRevertCases(env e){ ///////////////////////////////////////////////// -// Transfer +// Transfer (14/14) ///////////////////////////////////////////////// -// STATUS - +// STATUS - verified +// transfer additivity +rule transferAdditivity(env e){ + address from; address to; uint256 id; bytes data; + uint256 amount; uint256 amount1; uint256 amount2; + require amount == amount1 + amount2; + + storage initialStorage = lastStorage; + + safeTransferFrom(e, from, to, id, amount, data); + + uint256 balanceAfterSingleTransaction = balanceOf(to, id); + + safeTransferFrom(e, from, to, id, amount1, data) at initialStorage; + safeTransferFrom(e, from, to, id, amount2, data); + + uint256 balanceAfterDoubleTransaction = balanceOf(to, id); + + assert balanceAfterSingleTransaction == balanceAfterDoubleTransaction, "Not additive"; +} + + +// STATUS - verified +// safeTransferFrom updates `from` and `to` balances +rule transferCorrectness(env e){ + address from; address to; uint256 id; uint256 amount; bytes data; + + require to != from; + + uint256 fromBalanceBefore = balanceOf(from, id); + uint256 toBalanceBefore = balanceOf(to, id); + + safeTransferFrom(e, from, to, id, amount, data); + + uint256 fromBalanceAfter = balanceOf(from, id); + uint256 toBalanceAfter = balanceOf(to, id); + + assert fromBalanceBefore == fromBalanceAfter + amount, "Something wet wrong"; + assert toBalanceBefore == toBalanceAfter - amount, "Something wet wrong"; +} + + +// STATUS - verified +// cannot transfer more than allowed (safeBatchTransferFrom version) +rule transferBatchCorrectness(env e){ + address from; address to; uint256[] ids; uint256[] amounts; bytes data; + uint256 idToCheck1; uint256 amountToCheck1; + uint256 idToCheck2; uint256 amountToCheck2; + uint256 idToCheck3; uint256 amountToCheck3; + + require to != from; + require idToCheck1 != idToCheck2 && idToCheck3 != idToCheck2 && idToCheck1 != idToCheck3; + + require ids.length == 3; + require amounts.length == 3; + require ids[0] == idToCheck1; require amounts[0] == amountToCheck1; + require ids[1] == idToCheck2; require amounts[1] == amountToCheck2; + require ids[2] == idToCheck3; require amounts[2] == amountToCheck3; + + uint256 fromBalanceBefore1 = balanceOf(from, idToCheck1); + uint256 fromBalanceBefore2 = balanceOf(from, idToCheck2); + uint256 fromBalanceBefore3 = balanceOf(from, idToCheck3); + + uint256 toBalanceBefore1 = balanceOf(to, idToCheck1); + uint256 toBalanceBefore2 = balanceOf(to, idToCheck2); + uint256 toBalanceBefore3 = balanceOf(to, idToCheck3); + + safeBatchTransferFrom(e, from, to, ids, amounts, data); + + uint256 fromBalanceAfter1 = balanceOf(from, idToCheck1); + uint256 fromBalanceAfter2 = balanceOf(from, idToCheck2); + uint256 fromBalanceAfter3 = balanceOf(from, idToCheck3); + + uint256 toBalanceAfter1 = balanceOf(to, idToCheck1); + uint256 toBalanceAfter2 = balanceOf(to, idToCheck2); + uint256 toBalanceAfter3 = balanceOf(to, idToCheck3); + + assert (fromBalanceBefore1 == fromBalanceAfter1 + amountToCheck1) + && (fromBalanceBefore2 == fromBalanceAfter2 + amountToCheck2) + && (fromBalanceBefore3 == fromBalanceAfter3 + amountToCheck3), "Something wet wrong"; + assert (toBalanceBefore1 == toBalanceAfter1 - amountToCheck1) + && (toBalanceBefore2 == toBalanceAfter2 - amountToCheck2) + && (toBalanceBefore3 == toBalanceAfter3 - amountToCheck3), "Something wet wrong"; +} + + +// STATUS - verified // cannot transfer more than `from` balance (safeTransferFrom version) rule cannotTransferMoreSingle(env e){ address from; address to; uint256 id; uint256 amount; bytes data; @@ -148,7 +234,7 @@ rule cannotTransferMoreSingle(env e){ } -// STATUS - +// STATUS - verified // cannot transfer more than allowed (safeBatchTransferFrom version) rule cannotTransferMoreBatch(env e){ address from; address to; uint256[] ids; uint256[] amounts; bytes data; @@ -164,7 +250,7 @@ rule cannotTransferMoreBatch(env e){ require amounts.length == 3; require ids[0] == idToCheck1; require amounts[0] == amountToCheck1; require ids[1] == idToCheck2; require amounts[1] == amountToCheck2; - require ids[2] == idToCheck3; require amounts[2] == amountToCheck3; + // require ids[2] == idToCheck3; require amounts[2] == amountToCheck3; safeBatchTransferFrom@withrevert(e, from, to, ids, amounts, data); @@ -172,11 +258,14 @@ rule cannotTransferMoreBatch(env e){ } -// STATUS - (added debug vars) +// STATUS - verified // safeBatchTransferFrom should revert if `to` is 0 address or if arrays length is different rule revertOfTransferBatch(env e){ address from; address to; uint256[] ids; uint256[] amounts; bytes data; + require ids.length < 100000000; + require amounts.length < 100000000; + uint256 idTMP = ids.length; uint256 amountsTMP = amounts.length; @@ -195,7 +284,6 @@ rule transferBalanceReduceEffect(env e){ bytes data; require other != to; - require amount > 0; uint256 otherBalanceBefore = balanceOf(other, id); @@ -207,7 +295,7 @@ rule transferBalanceReduceEffect(env e){ } -// STATUS - +// STATUS - verified // Sender calling safeTransferFrom should only reduce 'to' balance and not other's if sending amount is greater than 0 rule transferBalanceIncreaseEffect(env e){ address from; address to; address other; @@ -226,7 +314,7 @@ rule transferBalanceIncreaseEffect(env e){ } -// STATUS - +// STATUS - verified // Sender calling safeTransferFrom should only reduce 'from' balance and not other's if sending amount is greater than 0 rule transferBatchBalanceFromEffect(env e){ address from; address to; address other; @@ -259,7 +347,7 @@ rule transferBatchBalanceFromEffect(env e){ } -// STATUS - +// STATUS - verified // Sender calling safeBatchTransferFrom should only reduce 'to' balance and not other's if sending amount is greater than 0 rule transferBatchBalanceToEffect(env e){ address from; address to; address other; @@ -308,7 +396,7 @@ rule noTransferForNotApproved(env e) { } -// STATUS - +// STATUS - verified // cannot transfer without approval (safeBatchTransferFrom version) rule noTransferBatchForNotApproved(env e) { address from; address operator; address to; @@ -325,7 +413,7 @@ rule noTransferBatchForNotApproved(env e) { } -// STATUS - +// STATUS - verified // safeTransferFrom doesn't affect any approval rule noTransferEffectOnApproval(env e){ address from; address to; @@ -343,7 +431,7 @@ rule noTransferEffectOnApproval(env e){ } -// STATUS - +// STATUS - verified // safeTransferFrom doesn't affect any approval rule noTransferBatchEffectOnApproval(env e){ address from; address to; @@ -361,11 +449,107 @@ rule noTransferBatchEffectOnApproval(env e){ } + ///////////////////////////////////////////////// -// Mint +// Mint (9/9) ///////////////////////////////////////////////// +// STATUS - verified +// mint additivity +rule mintAdditivity(env e){ + address to; uint256 id; uint256 amount; uint256 amount1; uint256 amount2; bytes data; + require amount == amount1 + amount2; + + storage initialStorage = lastStorage; + + _mint(e, to, id, amount, data); + + uint256 balanceAfterSingleTransaction = balanceOf(to, id); + + _mint(e, to, id, amount1, data) at initialStorage; + _mint(e, to, id, amount2, data); + + uint256 balanceAfterDoubleTransaction = balanceOf(to, id); + + assert balanceAfterSingleTransaction == balanceAfterDoubleTransaction, "Not additive"; +} + + +// STATUS - verified +// mint should revert if `from` is 0 +rule mintRevertCases(env e){ + address to; uint256 id; uint256 amount; bytes data; + + _mint@withrevert(e, to, id, amount, data); + + assert to == 0 => lastReverted, "Should revert"; +} + + +// STATUS - verified +// mintBatch should revert if `from` is 0 or arrays have different length +rule mintBatchRevertCases(env e){ + address to; uint256[] ids; uint256[] amounts; bytes data; + + require ids.length < 100000000; + require amounts.length < 100000000; + + _mintBatch@withrevert(e, to, ids, amounts, data); + + assert to == 0 => lastReverted, "Should revert"; + assert ids.length != amounts.length => lastReverted, "Should revert"; +} + + +// STATUS - verified +// check that mint updates `to` balance correctly +rule mintCorrectWork(env e){ + address to; uint256 id; uint256 amount; bytes data; + + uint256 otherBalanceBefore = balanceOf(to, id); + + _mint(e, to, id, amount, data); + + uint256 otherBalanceAfter = balanceOf(to, id); + + assert otherBalanceBefore == otherBalanceAfter - amount, "Something is wrong"; +} + + +// STATUS - verified +// check that mintBatch updates `bootcamp participantsfrom` balance correctly +rule mintBatchCorrectWork(env e){ + address to; + uint256 id1; uint256 id2; uint256 id3; + uint256 amount1; uint256 amount2; uint256 amount3; + uint256[] ids; uint256[] amounts; + bytes data; + + require ids.length == 3; + require amounts.length == 3; + + require id1 != id2 && id2 != id3 && id3 != id1; + require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; + require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; + + uint256 otherBalanceBefore1 = balanceOf(to, id1); + uint256 otherBalanceBefore2 = balanceOf(to, id2); + uint256 otherBalanceBefore3 = balanceOf(to, id3); + + _mintBatch(e, to, ids, amounts, data); + + uint256 otherBalanceAfter1 = balanceOf(to, id1); + uint256 otherBalanceAfter2 = balanceOf(to, id2); + uint256 otherBalanceAfter3 = balanceOf(to, id3); + + assert otherBalanceBefore1 == otherBalanceAfter1 - amount1 + && otherBalanceBefore2 == otherBalanceAfter2 - amount2 + && otherBalanceBefore3 == otherBalanceAfter3 - amount3 + , "Something is wrong"; +} + + // STATUS - verified // the user cannot mint more than max_uint256 rule cantMintMoreSingle(env e){ @@ -403,6 +587,51 @@ rule cantMintMoreBatch(env e){ } +// STATUS - verified +// mint changes only `to` balance +rule cantMintOtherBalances(env e){ + address to; uint256 id; uint256 amount; bytes data; + address other; + + uint256 otherBalanceBefore = balanceOf(other, id); + + _mint(e, to, id, amount, data); + + uint256 otherBalanceAfter = balanceOf(other, id); + + assert other != to => otherBalanceBefore == otherBalanceAfter, "I like to see your money disappearing"; +} + + +// STATUS - verified +// mintBatch changes only `to` balance +rule cantMintBatchOtherBalances(env e){ + address to; + uint256 id1; uint256 id2; uint256 id3; + uint256[] ids; uint256[] amounts; + address other; + bytes data; + + uint256 otherBalanceBefore1 = balanceOf(other, id1); + uint256 otherBalanceBefore2 = balanceOf(other, id2); + uint256 otherBalanceBefore3 = balanceOf(other, id3); + + _mintBatch(e, to, ids, amounts, data); + + uint256 otherBalanceAfter1 = balanceOf(other, id1); + uint256 otherBalanceAfter2 = balanceOf(other, id2); + uint256 otherBalanceAfter3 = balanceOf(other, id3); + + assert other != to => (otherBalanceBefore1 == otherBalanceAfter1 + || otherBalanceBefore2 == otherBalanceAfter2 + || otherBalanceBefore3 == otherBalanceAfter3) + , "I like to see your money disappearing"; +} + + + + + // rule mintRevert(env e){ // address operator; // address from; @@ -428,11 +657,57 @@ rule cantMintMoreBatch(env e){ ///////////////////////////////////////////////// -// Burn +// Burn (9/9) ///////////////////////////////////////////////// -// STATUS - +// STATUS - verified +// burn additivity +rule burnAdditivity(env e){ + address from; uint256 id; uint256 amount; uint256 amount1; uint256 amount2; + require amount == amount1 + amount2; + + storage initialStorage = lastStorage; + + _burn(e, from, id, amount); + + uint256 balanceAfterSingleTransaction = balanceOf(from, id); + + _burn(e, from, id, amount1) at initialStorage; + _burn(e, from, id, amount2); + + uint256 balanceAfterDoubleTransaction = balanceOf(from, id); + + assert balanceAfterSingleTransaction == balanceAfterDoubleTransaction, "Not additive"; +} + + +// STATUS - verified +// burn should revert if `from` is 0 +rule burnRevertCases(env e){ + address from; uint256 id; uint256 amount; + + _burn@withrevert(e, from, id, amount); + + assert from == 0 => lastReverted, "Should revert"; +} + + +// STATUS - verified +// burnBatch should revert if `from` is 0 or arrays have different length +rule burnBatchRevertCases(env e){ + address from; uint256[] ids; uint256[] amounts; + + require ids.length < 100000000; + + _burnBatch@withrevert(e, from, ids, amounts); + + assert from == 0 => lastReverted, "Should revert"; + assert ids.length != amounts.length => lastReverted, "Should revert"; +} + + +// STATUS - verified // check that burn updates `from` balance correctly rule burnCorrectWork(env e){ address from; uint256 id; uint256 amount; @@ -447,7 +722,7 @@ rule burnCorrectWork(env e){ } -// STATUS - +// STATUS - verified // check that burnBatch updates `from` balance correctly rule burnBatchCorrectWork(env e){ address from; @@ -456,7 +731,6 @@ rule burnBatchCorrectWork(env e){ uint256[] ids; uint256[] amounts; require ids.length == 3; - require amounts.length == 3; require id1 != id2 && id2 != id3 && id3 != id1; require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; @@ -480,7 +754,7 @@ rule burnBatchCorrectWork(env e){ // STATUS - verified -// the user cannot mint more than max_uint256 +// the user cannot burn more than they have rule cantBurnMoreSingle(env e){ address from; uint256 id; uint256 amount; @@ -492,24 +766,8 @@ rule cantBurnMoreSingle(env e){ } -// STATUS - -// burn changes only `from` balance -rule cantBurnOtherBalances(env e){ - address from; uint256 id; uint256 amount; - address other; - - uint256 otherBalanceBefore = balanceOf(other, id); - - _burn(e, from, id, amount); - - uint256 otherBalanceAfter = balanceOf(other, id); - - assert other != from => otherBalanceBefore == otherBalanceAfter, "I like to see your money disappearing"; -} - - // STATUS - verified -// the user cannot mint more than max_uint256 (batch version) +// the user cannot burn more than they have (batch version) rule cantBurnMoreBatch(env e){ address from; uint256 id1; uint256 id2; uint256 id3; @@ -531,20 +789,36 @@ rule cantBurnMoreBatch(env e){ } -// STATUS - +// STATUS - verified +// burn changes only `from` balance +rule cantBurnOtherBalances(env e){ + address from; uint256 id; uint256 amount; + address other; + + uint256 otherBalanceBefore = balanceOf(other, id); + + _burn(e, from, id, amount); + + uint256 otherBalanceAfter = balanceOf(other, id); + + assert other != from => otherBalanceBefore == otherBalanceAfter, "I like to see your money disappearing"; +} + + +// STATUS - verified // burnBatch changes only `from` balance -rule cantBurnbatchOtherBalances(env e){ +rule cantBurnBatchOtherBalances(env e){ address from; uint256 id1; uint256 id2; uint256 id3; uint256 amount1; uint256 amount2; uint256 amount3; uint256[] ids; uint256[] amounts; address other; - require ids.length == 3; - require amounts.length == 3; + // require ids.length == 3; + // require amounts.length == 3; - require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; - require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; + // require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; + // require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; uint256 otherBalanceBefore1 = balanceOf(other, id1); uint256 otherBalanceBefore2 = balanceOf(other, id2); From a0b58c30710910cc805d10d0cf20ebf2c99ff7f6 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 4 Apr 2022 21:16:15 +0100 Subject: [PATCH 160/254] flashMint finished --- certora/specs/ERC20FlashMint.spec | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/certora/specs/ERC20FlashMint.spec b/certora/specs/ERC20FlashMint.spec index 511eaf84f..eb605893b 100644 --- a/certora/specs/ERC20FlashMint.spec +++ b/certora/specs/ERC20FlashMint.spec @@ -1,9 +1,8 @@ import "erc20.spec" methods { - onFlashLoan(address, address, uint256, uint256, bytes) => HAVOC_ALL - - _burn(address account, uint256 amount) returns(bool) => specBurn(account, amount); + maxFlashLoan(address) returns(uint256) envfree + _burn(address account, uint256 amount) returns(bool) => specBurn(account, amount) } ghost mapping(address => uint256) trackedBurnAmount; @@ -26,4 +25,4 @@ rule letsWatchItBurns(env e){ uint256 burned = trackedBurnAmount[receiver]; assert to_mathint(amount + feeBefore) == burned, "cheater"; -} \ No newline at end of file +} From 8c86b250bc2b4bc88cb0a1d20c15a212ab4869e2 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 4 Apr 2022 21:19:54 +0100 Subject: [PATCH 161/254] fixed rule description --- certora/specs/ERC20FlashMint.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/ERC20FlashMint.spec b/certora/specs/ERC20FlashMint.spec index eb605893b..3142f508f 100644 --- a/certora/specs/ERC20FlashMint.spec +++ b/certora/specs/ERC20FlashMint.spec @@ -14,7 +14,7 @@ function specBurn(address account, uint256 amount) returns bool { // retuns ne // STATUS - verified -// fee + flashLoan amount is burned +// Check that if flashLoan() call is successful, then proper amount of tokens was burnt(fee + flashLoan amount) rule letsWatchItBurns(env e){ address receiver; address token; uint256 amount; bytes data; From 479118fcd1ce4cdce646231fd3c350e577ce13f0 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 4 Apr 2022 21:33:31 +0100 Subject: [PATCH 162/254] push to report issues --- certora/specs/TimelockController.spec | 68 ++++++++++++++++++--------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec index 65aedeba8..9e08fcf98 100644 --- a/certora/specs/TimelockController.spec +++ b/certora/specs/TimelockController.spec @@ -1,8 +1,7 @@ -using AccessControlHarness as AC - methods { getTimestamp(bytes32) returns(uint256) envfree _DONE_TIMESTAMP() returns(uint256) envfree + PROPOSER_ROLE() returns(bytes32) envfree _minDelay() returns(uint256) envfree getMinDelay() returns(uint256) envfree hashOperation(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt) returns(bytes32) envfree @@ -10,6 +9,8 @@ methods { cancel(bytes32) schedule(address, uint256, bytes32, bytes32, bytes32, uint256) execute(address, uint256, bytes, bytes32, bytes32) + executeBatch(address[], uint256[], bytes[], bytes32, bytes32) + _checkRole(bytes32) => DISPATCHER(true) } //////////////////////////////////////////////////////////////////////////// @@ -76,7 +77,7 @@ rule keccakCheck(method f, env e){ address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; address targetRand; uint256 valueRand; bytes dataRand; bytes32 predecessorRand; bytes32 saltRand; - require data.length < 4; + require data.length < 7; // uint256 freshIndex; // require freshIndex <= data.length @@ -111,7 +112,7 @@ rule unsetPendingTransitionGeneral(method f, env e){ } -// STATUS - +// STATUS - verified // unset() -> pending() via schedule() and scheduleBatch() only rule unsetPendingTransitionMethods(method f, env e){ bytes32 id; @@ -178,8 +179,8 @@ rule doneToNothingTransition(method f, env e){ // STATUS - verified -// only TimelockController contract can change minDealy -rule minDealyOnlyChange(method f, env e){ +// only TimelockController contract can change minDelay +rule minDelayOnlyChange(method f, env e){ uint256 delayBefore = _minDelay(); calldataarg args; @@ -191,7 +192,7 @@ rule minDealyOnlyChange(method f, env e){ } -// STATUS - in progress (need working hash) +// STATUS - in progress // execute() is the only way to set timestamp to 1 rule getTimestampOnlyChange(method f, env e){ bytes32 id; @@ -211,7 +212,7 @@ rule getTimestampOnlyChange(method f, env e){ } -// STATUS - in progress (need working hash) +// STATUS - verified // scheduled operation timestamp == block.timestamp + delay (kind of unit test) rule scheduleCheck(method f, env e){ bytes32 id; @@ -219,16 +220,15 @@ rule scheduleCheck(method f, env e){ address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; require getTimestamp(id) < e.block.timestamp; - // require getMinDelay() > 0; hashIdCorrelation(id, target, value, data, predecessor, salt); schedule(e, target, value, data, predecessor, salt, delay); - assert getTimestamp(id) == to_uint256(e.block.timestamp + getMinDelay()), "Time doesn't obey to mortal souls"; + assert getTimestamp(id) == to_uint256(e.block.timestamp + delay), "Time doesn't obey to mortal souls"; } -// STATUS - in progress (need working hash) +// STATUS - verified // Cannot call execute on a pending (not ready) operation rule cannotCallExecute(method f, env e){ address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; @@ -243,9 +243,9 @@ rule cannotCallExecute(method f, env e){ } -// STATUS - in progress +// STATUS - verified // in unset() execute() reverts -rule executeRevertFromUnset(method f, env e, env e2){ +rule executeRevertsFromUnset(method f, env e, env e2){ address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; bytes32 id; @@ -258,9 +258,9 @@ rule executeRevertFromUnset(method f, env e, env e2){ } -// STATUS - +// STATUS - verified // Execute reverts => state returns to pending -rule executeRevertEffectCheck(method f, env e){ +rule executeRevertsEffectCheck(method f, env e){ address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; bytes32 id; @@ -289,21 +289,47 @@ rule cancelledNotExecuted(method f, env e){ } -// STATUS - in progress (add schedule batch) +// STATUS - broken // Only proposers can schedule an operation -rule onlyProposer(method f, env e){ +rule onlyProposerCertorafallbackFail(method f, env e) filtered { f -> f.selector == schedule(address, uint256, bytes32, bytes32, bytes32, uint256).selector + || f.selector == scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256).selector } { bytes32 id; bytes32 role; address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; - require unset(id); - hashIdCorrelation(id, target, value, data, predecessor, salt); + // hashIdCorrelation(id, target, value, data, predecessor, salt); - AC._checkRole@withrevert(e, role); + _checkRole@withrevert(e, PROPOSER_ROLE()); bool isCheckRoleReverted = lastReverted; - schedule@withrevert(e, target, value, data, predecessor, salt, delay); + // schedule@withrevert(e, target, value, data, predecessor, salt, delay); + + calldataarg args; + f@withrevert(e, args); + + bool isScheduleReverted = lastReverted; + + assert isCheckRoleReverted => isScheduleReverted, "Enemy was detected"; +} + + +// STATUS - verified +// Only proposers can schedule an operation +rule onlyProposer1(method f, env e){ + bytes32 id; + bytes32 role; + // address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; + address[] targets; uint256[] values; bytes[] datas; bytes32 predecessor; bytes32 salt; uint256 delay; + + // hashIdCorrelation(id, target, value, data, predecessor, salt); + + _checkRole@withrevert(e, PROPOSER_ROLE()); + + bool isCheckRoleReverted = lastReverted; + + // schedule@withrevert(e, target, value, data, predecessor, salt, delay); + scheduleBatch@withrevert(e, targets, values, datas, predecessor, salt, delay); bool isScheduleReverted = lastReverted; From da674eced1defb824d75933b7db716a54500f5cb Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Mon, 4 Apr 2022 22:34:51 +0100 Subject: [PATCH 163/254] typos and cleaning --- certora/specs/ERC1155.spec | 62 ++++---------------------------------- 1 file changed, 6 insertions(+), 56 deletions(-) diff --git a/certora/specs/ERC1155.spec b/certora/specs/ERC1155.spec index 26d77a099..3ecca54ca 100644 --- a/certora/specs/ERC1155.spec +++ b/certora/specs/ERC1155.spec @@ -250,11 +250,11 @@ rule cannotTransferMoreBatch(env e){ require amounts.length == 3; require ids[0] == idToCheck1; require amounts[0] == amountToCheck1; require ids[1] == idToCheck2; require amounts[1] == amountToCheck2; - // require ids[2] == idToCheck3; require amounts[2] == amountToCheck3; + require ids[2] == idToCheck3; require amounts[2] == amountToCheck3; safeBatchTransferFrom@withrevert(e, from, to, ids, amounts, data); - assert (amountToCheck1 > balanceBefore1 || amountToCheck2 > balanceBefore2 || amountToCheck2 > balanceBefore2) => lastReverted, "Achtung! Scammer!"; + assert (amountToCheck1 > balanceBefore1 || amountToCheck2 > balanceBefore2 || amountToCheck3 > balanceBefore3) => lastReverted, "Achtung! Scammer!"; } @@ -266,9 +266,6 @@ rule revertOfTransferBatch(env e){ require ids.length < 100000000; require amounts.length < 100000000; - uint256 idTMP = ids.length; - uint256 amountsTMP = amounts.length; - safeBatchTransferFrom@withrevert(e, from, to, ids, amounts, data); assert ids.length != amounts.length => lastReverted, "Achtung! Scammer!"; @@ -324,13 +321,6 @@ rule transferBatchBalanceFromEffect(env e){ require other != to; - // require ids.length == 3; - // require amounts.length == 3; - - // require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; - // require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; - // require amount1 > 0; require amount2 > 0; require amount3 > 0; - uint256 otherBalanceBefore1 = balanceOf(other, id1); uint256 otherBalanceBefore2 = balanceOf(other, id2); uint256 otherBalanceBefore3 = balanceOf(other, id3); @@ -357,13 +347,6 @@ rule transferBatchBalanceToEffect(env e){ require other != from; - // require ids.length == 3; - // require amounts.length == 3; - - // require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; - // require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; - // require amount1 > 0; require amount2 > 0; require amount3 > 0; - uint256 otherBalanceBefore1 = balanceOf(other, id1); uint256 otherBalanceBefore2 = balanceOf(other, id2); uint256 otherBalanceBefore3 = balanceOf(other, id3); @@ -623,39 +606,12 @@ rule cantMintBatchOtherBalances(env e){ uint256 otherBalanceAfter3 = balanceOf(other, id3); assert other != to => (otherBalanceBefore1 == otherBalanceAfter1 - || otherBalanceBefore2 == otherBalanceAfter2 - || otherBalanceBefore3 == otherBalanceAfter3) + && otherBalanceBefore2 == otherBalanceAfter2 + && otherBalanceBefore3 == otherBalanceAfter3) , "I like to see your money disappearing"; } - - - -// rule mintRevert(env e){ -// address operator; -// address from; -// address to; -// uint256 id; -// uint256 amount; -// bytes data; -// -// require operator == e.msg.sender; -// require from == 0; -// -// _doSafeTransferAcceptanceCheck@withrevert(operator, from, to, id, amount, data); -// -// bool acceptanceCheck = lastReverted; -// -// _mint@withrevert(e, to, id, amount, data); -// -// bool mintRevert = lastReverted; -// -// assert acceptanceCheck => mintRevert, "reverts are wrong"; -// } - - - ///////////////////////////////////////////////// // Burn (9/9) ///////////////////////////////////////////////// @@ -814,12 +770,6 @@ rule cantBurnBatchOtherBalances(env e){ uint256[] ids; uint256[] amounts; address other; - // require ids.length == 3; - // require amounts.length == 3; - - // require ids[0] == id1; require ids[1] == id2; require ids[2] == id3; - // require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3; - uint256 otherBalanceBefore1 = balanceOf(other, id1); uint256 otherBalanceBefore2 = balanceOf(other, id2); uint256 otherBalanceBefore3 = balanceOf(other, id3); @@ -831,7 +781,7 @@ rule cantBurnBatchOtherBalances(env e){ uint256 otherBalanceAfter3 = balanceOf(other, id3); assert other != from => (otherBalanceBefore1 == otherBalanceAfter1 - || otherBalanceBefore2 == otherBalanceAfter2 - || otherBalanceBefore3 == otherBalanceAfter3) + && otherBalanceBefore2 == otherBalanceAfter2 + && otherBalanceBefore3 == otherBalanceAfter3) , "I like to see your money disappearing"; } From 140df5b7cebbf6cdcd96fb5c3a7f63803c946d37 Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Thu, 7 Apr 2022 15:49:37 -0700 Subject: [PATCH 164/254] everything except Alex fix for totalVotes_sums_accounts --- certora/scripts/ERC20VotesRule.sh | 2 +- certora/scripts/verifyERC20Votes.sh | 5 +- certora/specs/ERC20Votes.spec | 82 ++++++++--------------------- 3 files changed, 26 insertions(+), 63 deletions(-) diff --git a/certora/scripts/ERC20VotesRule.sh b/certora/scripts/ERC20VotesRule.sh index 0921d1df2..28517b730 100644 --- a/certora/scripts/ERC20VotesRule.sh +++ b/certora/scripts/ERC20VotesRule.sh @@ -20,4 +20,4 @@ certoraRun \ --rule ${rule} \ --msg "${msg}" \ --staging "alex/new-dt-hashing-alpha" \ - --rule_sanity \ \ No newline at end of file + # --rule_sanity \ \ No newline at end of file diff --git a/certora/scripts/verifyERC20Votes.sh b/certora/scripts/verifyERC20Votes.sh index b3b4e2035..22766e175 100644 --- a/certora/scripts/verifyERC20Votes.sh +++ b/certora/scripts/verifyERC20Votes.sh @@ -19,5 +19,6 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --loop_iter 4 \ - --staging "Eyal/SanityWithoutCallTrace" \ - --msg "${msg}" + --staging "alex/new-dt-hashing-alpha" \ + --msg "${msg}" \ + --rule_sanity diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index 5cccdeb9f..9f453d8b2 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -41,11 +41,6 @@ ghost lastIndex(address) returns uint32; // helper -// blocked by tool error -invariant totalVotes_gte_accounts() - forall address a. forall address b. a != b => totalVotes() >= getVotes(a) + getVotes(b) - - hook Sstore _checkpoints[KEY address account][INDEX uint32 index].votes uint224 newVotes (uint224 oldVotes) STORAGE { havoc userVotes assuming userVotes@new(account) == newVotes; @@ -90,14 +85,18 @@ invariant sanity_invariant() // blocked by tool error invariant votes_solvency() to_mathint(totalSupply()) >= totalVotes() -{ preserved { - require forall address account. unsafeNumCheckpoints(account) < 4294967295; - requireInvariant totalVotes_gte_accounts(); -}} +{ preserved with(env e) { + require forall address account. numCheckpoints(account) < 1000000; + requireInvariant totalVotes_sums_accounts(); +} } + +invariant totalVotes_sums_accounts() + forall address a. forall address b. (a != b && a != 0x0 && b != 0x0) => totalVotes() >= getVotes(delegates(a)) + getVotes(delegates(b)) + + // for some checkpoint, the fromBlock is less than the current block number -// passes but fails rule sanity from hash on delegate by sig -invariant timestamp_constrains_fromBlock(address account, uint32 index, env e) +invariant blockNum_constrains_fromBlock(address account, uint32 index, env e) ckptFromBlock(account, index) < e.block.number { preserved { @@ -120,10 +119,6 @@ invariant timestamp_constrains_fromBlock(address account, uint32 index, env e) invariant fromBlock_constrains_numBlocks(address account) numCheckpoints(account) <= ckptFromBlock(account, numCheckpoints(account) - 1) { preserved with(env e) { - uint32 pos; - uint32 pos2; - requireInvariant fromBlock_greaterThanEq_pos(account, pos); - requireInvariant fromBlock_increasing(account, pos, pos2); require e.block.number >= ckptFromBlock(account, numCheckpoints(account) - 1); // this should be true from the invariant above!! }} @@ -142,30 +137,13 @@ invariant fromBlock_increasing(address account, uint32 pos, uint32 pos2) pos > pos2 => ckptFromBlock(account, pos) > ckptFromBlock(account, pos2) -invariant no_delegate_no_checkpoints(address account) - delegates(account) == 0x0 => numCheckpoints(account) == 0 -{ preserved delegate(address delegatee) with(env e) { - require delegatee != 0; -} preserved _delegate(address delegator, address delegatee) with(env e) { - require delegatee != 0; -}} - // converted from an invariant to a rule to slightly change the logic // if the fromBlock is the same as before, then the number of checkpoints stays the same // however if the fromBlock is new than the number of checkpoints increases // passes, fails rule sanity because tautology check seems to be bugged rule unique_checkpoints_rule(method f) { env e; calldataarg args; - - // require e.block.number > 0; // we don't care about this exception - - address account; - // address delegates_pre = delegates(account); - - - // require unsafeNumCheckpoints(account) < 1000000; // 2^32 // we don't want to deal with the checkpoint overflow error here - uint32 num_ckpts_ = numCheckpoints(account); uint32 fromBlock_ = num_ckpts_ == 0 ? 0 : ckptFromBlock(account, num_ckpts_ - 1); @@ -176,9 +154,6 @@ rule unique_checkpoints_rule(method f) { assert fromBlock_ == _fromBlock => num_ckpts_ == _num_ckpts || _num_ckpts == 1, "same fromBlock, new checkpoint"; - // assert doubleFromBlock(account) => num_ckpts_ == _num_ckpts, "same fromBlock, new checkpoint"; - // this assert fails consistently - // assert !doubleFromBlock(account) => ckpts_ != _ckpts, "new fromBlock but total checkpoints not being increased"; } // assumes neither account has delegated @@ -203,26 +178,14 @@ rule transfer_safe() { uint256 votesA_pre = getVotes(delegates(a)); uint256 votesB_pre = getVotes(delegates(b)); - // for debugging - uint256 balA_ = balanceOf(e, a); - uint256 balB_ = balanceOf(e, b); - mathint totalVotes_pre = totalVotes(); erc20votes.transferFrom(e, a, b, amount); - - - // require lastIndex(delegates(a)) < 1000000; - // require lastIndex(delegates(b)) < 1000000; mathint totalVotes_post = totalVotes(); uint256 votesA_post = getVotes(delegates(a)); uint256 votesB_post = getVotes(delegates(b)); - // for debugging - uint256 _balA = balanceOf(e, a); - uint256 _balB = balanceOf(e, b); - // if an account that has not delegated transfers balance to an account that has, it will increase the total supply of votes assert totalVotes_pre == totalVotes_post, "transfer changed total supply"; assert delegates(a) != 0 => votesA_pre - votesA_post == amount, "A lost the wrong amount of votes"; @@ -304,27 +267,28 @@ rule delegate_contained() { assert votes_ == _votes, "votes not contained"; } -// checks all of the above delegate rules with front running rule delegate_no_frontrunning(method f) { env e; calldataarg args; address delegator; address delegatee; address third; address other; + + + require numCheckpoints(delegatee) < 1000000; + require numCheckpoints(third) < 1000000; + + + f(e, args); + + uint256 delegator_bal = balanceOf(e, delegator); + uint256 delegatee_votes_ = getVotes(delegatee); + uint256 third_votes_ = getVotes(third); + uint256 other_votes_ = getVotes(other); require delegates(delegator) == third; require third != delegatee; require other != third; require other != delegatee; require delegatee != 0x0; - require numCheckpoints(delegatee) < 1000000; - require numCheckpoints(third) < 1000000; - - uint256 delegatee_votes_ = getVotes(delegatee); - uint256 third_votes_ = getVotes(third); - uint256 other_votes_ = getVotes(other); - - // require third is address for previous delegator - f(e, args); - uint256 delegator_bal = erc20votes.balanceOf(e, delegator); _delegate(e, delegator, delegatee); uint256 _delegatee_votes = getVotes(delegatee); @@ -367,12 +331,10 @@ rule burn_decreases_totalSupply() { uint256 fromBlock = e.block.number; uint256 totalSupply_ = totalSupply(); - require totalSupply_ > balanceOf(e, account); burn(e, account, amount); uint256 _totalSupply = totalSupply(); - require _totalSupply < _maxSupply(); assert _totalSupply == totalSupply_ - amount, "totalSupply not decreased properly"; assert getPastTotalSupply(e, fromBlock) == totalSupply_ , "previous total supply not saved properly"; From 75417fbf9f6660c32fe42e20444db51f8d8cc3c1 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Fri, 8 Apr 2022 15:38:26 +0100 Subject: [PATCH 165/254] finilized rules --- certora/scripts/verifyERC20Wrapper.sh | 2 +- certora/scripts/verifyTimelock.sh | 1 + certora/specs/AccessControl.spec | 4 +- certora/specs/ERC1155.spec | 67 ++--- certora/specs/ERC20Wrapper.spec | 16 +- certora/specs/TimelockController.spec | 411 +++++++++++++------------- 6 files changed, 246 insertions(+), 255 deletions(-) diff --git a/certora/scripts/verifyERC20Wrapper.sh b/certora/scripts/verifyERC20Wrapper.sh index c4aaef9ff..15654d84e 100644 --- a/certora/scripts/verifyERC20Wrapper.sh +++ b/certora/scripts/verifyERC20Wrapper.sh @@ -6,5 +6,5 @@ certoraRun \ --optimistic_loop \ --staging \ --rule_sanity \ - --msg "wrapper spec sanity check fixes" + --msg "ERC20wrapper spec" \ No newline at end of file diff --git a/certora/scripts/verifyTimelock.sh b/certora/scripts/verifyTimelock.sh index 93bb92f75..2876d5f92 100644 --- a/certora/scripts/verifyTimelock.sh +++ b/certora/scripts/verifyTimelock.sh @@ -6,6 +6,7 @@ certoraRun \ --loop_iter 3 \ --staging alex/new-dt-hashing-alpha \ --rule_sanity \ + --settings -byteMapHashingPrecision=32 \ --rule "$1" \ --msg "$1" \ No newline at end of file diff --git a/certora/specs/AccessControl.spec b/certora/specs/AccessControl.spec index 83f925d86..b57ce3113 100644 --- a/certora/specs/AccessControl.spec +++ b/certora/specs/AccessControl.spec @@ -11,7 +11,7 @@ methods { // STATUS - verified -// check onlyRole modifier for grantRole() +// If `onlyRole` modifier reverts then `grantRole()` reverts rule onlyRoleModifierCheckGrant(env e){ bytes32 role; address account; @@ -58,7 +58,7 @@ rule grantRoleEffect(env e){ // STATUS - verified -// grantRole() does not affect another accounts +// revokeRole() does not affect another accounts rule revokeRoleEffect(env e){ bytes32 role; address account; bytes32 anotherRole; address nonEffectedAcc; diff --git a/certora/specs/ERC1155.spec b/certora/specs/ERC1155.spec index 3ecca54ca..9b5839031 100644 --- a/certora/specs/ERC1155.spec +++ b/certora/specs/ERC1155.spec @@ -52,7 +52,7 @@ rule onlyOwnerCanApprove(env e){ // STATUS - verified -// chech in which scenarios (if any) isApprovedForAll() revertes +// Chech that isApprovedForAll() revertes in planned scenarios and no more. rule approvalRevertCases(env e){ address account; address operator; isApprovedForAll@withrevert(account, operator); @@ -104,7 +104,7 @@ rule unexpectedBalanceChange(method f, env e) // STATUS - verified -// chech in which scenarios balanceOf() revertes +// Chech that `balanceOf()` revertes in planned scenarios and no more (only if `account` is 0) rule balanceOfRevertCases(env e){ address account; uint256 id; balanceOf@withrevert(account, id); @@ -113,7 +113,7 @@ rule balanceOfRevertCases(env e){ // STATUS - verified -// chech in which scenarios balanceOfBatch() revertes +// Chech that `balanceOfBatch()` revertes in planned scenarios and no more (only if at least one of `account`s is 0) rule balanceOfBatchRevertCases(env e){ address[] accounts; uint256[] ids; address account1; address account2; address account3; @@ -131,7 +131,7 @@ rule balanceOfBatchRevertCases(env e){ ///////////////////////////////////////////////// -// Transfer (14/14) +// Transfer (13/13) ///////////////////////////////////////////////// @@ -178,7 +178,7 @@ rule transferCorrectness(env e){ // STATUS - verified -// cannot transfer more than allowed (safeBatchTransferFrom version) +// safeBatchTransferFrom updates `from` and `to` balances) rule transferBatchCorrectness(env e){ address from; address to; uint256[] ids; uint256[] amounts; bytes data; uint256 idToCheck1; uint256 amountToCheck1; @@ -230,7 +230,6 @@ rule cannotTransferMoreSingle(env e){ safeTransferFrom@withrevert(e, from, to, id, amount, data); assert amount > balanceBefore => lastReverted, "Achtung! Scammer!"; - assert to == 0 => lastReverted, "Achtung! Scammer!"; } @@ -258,21 +257,6 @@ rule cannotTransferMoreBatch(env e){ } -// STATUS - verified -// safeBatchTransferFrom should revert if `to` is 0 address or if arrays length is different -rule revertOfTransferBatch(env e){ - address from; address to; uint256[] ids; uint256[] amounts; bytes data; - - require ids.length < 100000000; - require amounts.length < 100000000; - - safeBatchTransferFrom@withrevert(e, from, to, ids, amounts, data); - - assert ids.length != amounts.length => lastReverted, "Achtung! Scammer!"; - assert to == 0 => lastReverted, "Achtung! Scammer!"; -} - - // STATUS - verified // Sender calling safeTransferFrom should only reduce 'from' balance and not other's if sending amount is greater than 0 rule transferBalanceReduceEffect(env e){ @@ -293,7 +277,7 @@ rule transferBalanceReduceEffect(env e){ // STATUS - verified -// Sender calling safeTransferFrom should only reduce 'to' balance and not other's if sending amount is greater than 0 +// Sender calling safeTransferFrom should only increase 'to' balance and not other's if sending amount is greater than 0 rule transferBalanceIncreaseEffect(env e){ address from; address to; address other; uint256 id; uint256 amount; @@ -338,7 +322,7 @@ rule transferBatchBalanceFromEffect(env e){ // STATUS - verified -// Sender calling safeBatchTransferFrom should only reduce 'to' balance and not other's if sending amount is greater than 0 +// Sender calling safeBatchTransferFrom should only increase 'to' balance and not other's if sending amount is greater than 0 rule transferBatchBalanceToEffect(env e){ address from; address to; address other; uint256[] ids; uint256[] amounts; @@ -434,12 +418,12 @@ rule noTransferBatchEffectOnApproval(env e){ ///////////////////////////////////////////////// -// Mint (9/9) +// Mint (7/9) ///////////////////////////////////////////////// // STATUS - verified -// mint additivity +// Additivity of _mint: _mint(a); _mint(b) has same effect as _mint(a+b) rule mintAdditivity(env e){ address to; uint256 id; uint256 amount; uint256 amount1; uint256 amount2; bytes data; require amount == amount1 + amount2; @@ -459,8 +443,8 @@ rule mintAdditivity(env e){ } -// STATUS - verified -// mint should revert if `from` is 0 +// STATUS - verified +// Chech that `_mint()` revertes in planned scenario(s) (only if `to` is 0) rule mintRevertCases(env e){ address to; uint256 id; uint256 amount; bytes data; @@ -471,28 +455,27 @@ rule mintRevertCases(env e){ // STATUS - verified -// mintBatch should revert if `from` is 0 or arrays have different length +// Chech that `_mintBatch()` revertes in planned scenario(s) (only if `to` is 0 or arrays have different length) rule mintBatchRevertCases(env e){ address to; uint256[] ids; uint256[] amounts; bytes data; - require ids.length < 100000000; - require amounts.length < 100000000; + require ids.length < 1000000000; + require amounts.length < 1000000000; _mintBatch@withrevert(e, to, ids, amounts, data); - assert to == 0 => lastReverted, "Should revert"; - assert ids.length != amounts.length => lastReverted, "Should revert"; + assert (ids.length != amounts.length || to == 0) => lastReverted, "Should revert"; } // STATUS - verified -// check that mint updates `to` balance correctly +// Check that mint updates `to` balance correctly rule mintCorrectWork(env e){ address to; uint256 id; uint256 amount; bytes data; uint256 otherBalanceBefore = balanceOf(to, id); - _mint(e, to, id, amount, data); + _mint(e, to, id, amount, data); uint256 otherBalanceAfter = balanceOf(to, id); @@ -534,7 +517,7 @@ rule mintBatchCorrectWork(env e){ // STATUS - verified -// the user cannot mint more than max_uint256 +// The user cannot mint more than max_uint256 rule cantMintMoreSingle(env e){ address to; uint256 id; uint256 amount; bytes data; @@ -571,7 +554,7 @@ rule cantMintMoreBatch(env e){ // STATUS - verified -// mint changes only `to` balance +// `_mint()` changes only `to` balance rule cantMintOtherBalances(env e){ address to; uint256 id; uint256 amount; bytes data; address other; @@ -618,7 +601,7 @@ rule cantMintBatchOtherBalances(env e){ // STATUS - verified -// burn additivity +// Additivity of _burn: _burn(a); _burn(b) has same effect as _burn(a+b) rule burnAdditivity(env e){ address from; uint256 id; uint256 amount; uint256 amount1; uint256 amount2; require amount == amount1 + amount2; @@ -639,7 +622,7 @@ rule burnAdditivity(env e){ // STATUS - verified -// burn should revert if `from` is 0 +// Chech that `_burn()` revertes in planned scenario(s) (if `from` is 0) rule burnRevertCases(env e){ address from; uint256 id; uint256 amount; @@ -650,16 +633,16 @@ rule burnRevertCases(env e){ // STATUS - verified -// burnBatch should revert if `from` is 0 or arrays have different length +// Chech that `balanceOf()` revertes in planned scenario(s) (if `from` is 0 or arrays have different length) rule burnBatchRevertCases(env e){ address from; uint256[] ids; uint256[] amounts; - require ids.length < 100000000; + require ids.length < 1000000000; + require amounts.length < 1000000000; _burnBatch@withrevert(e, from, ids, amounts); - assert from == 0 => lastReverted, "Should revert"; - assert ids.length != amounts.length => lastReverted, "Should revert"; + assert (from == 0 || ids.length != amounts.length) => lastReverted, "Should revert"; } diff --git a/certora/specs/ERC20Wrapper.spec b/certora/specs/ERC20Wrapper.spec index 3ea288545..01c1b4295 100644 --- a/certora/specs/ERC20Wrapper.spec +++ b/certora/specs/ERC20Wrapper.spec @@ -40,7 +40,7 @@ invariant underTotalAndContractBalanceOfCorrelation(env e) // STATUS - verified -// check correct values update by depositFor() +// Check that values are updated correctly by `depositFor()` rule depositForSpecBasic(env e){ address account; uint256 amount; @@ -64,11 +64,10 @@ rule depositForSpecBasic(env e){ // STATUS - verified -// check correct values update by depositFor() +// Check that values are updated correctly by `depositFor()` rule depositForSpecWrapper(env e){ address account; uint256 amount; - // require e.msg.sender != currentContract; require underlying() != currentContract; uint256 wrapperUserBalanceBefore = balanceOf(e, account); @@ -88,7 +87,7 @@ rule depositForSpecWrapper(env e){ // STATUS - verified -// check correct values update by depositFor() +// Check that values are updated correctly by `depositFor()` rule depositForSpecUnderlying(env e){ address account; uint256 amount; @@ -116,7 +115,7 @@ rule depositForSpecUnderlying(env e){ // STATUS - verified -// check correct values update by withdrawTo() +// Check that values are updated correctly by `withdrawTo()` rule withdrawToSpecBasic(env e){ address account; uint256 amount; @@ -136,7 +135,7 @@ rule withdrawToSpecBasic(env e){ // STATUS - verified -// check correct values update by withdrawTo() +// Check that values are updated correctly by `withdrawTo()` rule withdrawToSpecWrapper(env e){ address account; uint256 amount; @@ -159,7 +158,7 @@ rule withdrawToSpecWrapper(env e){ // STATUS - verified -// check correct values update by withdrawTo() +// cCheck that values are updated correctly by `withdrawTo()` rule withdrawToSpecUnderlying(env e){ address account; uint256 amount; @@ -189,14 +188,13 @@ rule withdrawToSpecUnderlying(env e){ // STATUS - verified -// check correct values update by _recover() +// Check that values are updated correctly by `_recover()` rule recoverSpec(env e){ address account; uint256 amount; uint256 wrapperTotalBefore = totalSupply(e); uint256 wrapperUserBalanceBefore = balanceOf(e, account); uint256 wrapperSenderBalanceBefore = balanceOf(e, e.msg.sender); - uint256 underlyingThisBalanceBefore = underlyingBalanceOf(currentContract); mathint value = underlyingThisBalanceBefore - wrapperTotalBefore; diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec index 9e08fcf98..fbce17bfd 100644 --- a/certora/specs/TimelockController.spec +++ b/certora/specs/TimelockController.spec @@ -5,7 +5,11 @@ methods { _minDelay() returns(uint256) envfree getMinDelay() returns(uint256) envfree hashOperation(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt) returns(bytes32) envfree - + isOperation(bytes32) returns(bool) envfree + isOperationPending(bytes32) returns(bool) envfree + isOperationDone(bytes32) returns(bool) envfree + + isOperationReady(bytes32) returns(bool) cancel(bytes32) schedule(address, uint256, bytes32, bytes32, bytes32, uint256) execute(address, uint256, bytes, bytes32, bytes32) @@ -13,23 +17,6 @@ methods { _checkRole(bytes32) => DISPATCHER(true) } -//////////////////////////////////////////////////////////////////////////// -// Definitions // -//////////////////////////////////////////////////////////////////////////// - - -definition unset(bytes32 id) returns bool = - getTimestamp(id) == 0; - -definition pending(bytes32 id) returns bool = - getTimestamp(id) > _DONE_TIMESTAMP(); - -definition ready(bytes32 id, env e) returns bool = - getTimestamp(id) > _DONE_TIMESTAMP() && getTimestamp(id) <= e.block.timestamp; - -definition done(bytes32 id) returns bool = - getTimestamp(id) == _DONE_TIMESTAMP(); - //////////////////////////////////////////////////////////////////////////// @@ -38,7 +25,7 @@ definition done(bytes32 id) returns bool = function hashIdCorrelation(bytes32 id, address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt){ - require data.length < 7; + require data.length < 32; require hashOperation(target, value, data, predecessor, salt) == id; } @@ -56,15 +43,35 @@ function executionsCall(method f, env e, address target, uint256 value, bytes da } } + + //////////////////////////////////////////////////////////////////////////// -// Ghosts // +// Invariants // //////////////////////////////////////////////////////////////////////////// +// STATUS - verified +// `isOperation()` correctness check +invariant operationCheck(bytes32 id) + getTimestamp(id) > 0 <=> isOperation(id) -//////////////////////////////////////////////////////////////////////////// -// Invariants // -//////////////////////////////////////////////////////////////////////////// + +// STATUS - verified +// `isOperationPending()` correctness check +invariant pendingCheck(bytes32 id) + getTimestamp(id) > _DONE_TIMESTAMP() <=> isOperationPending(id) + + +// STATUS - verified +// `isOperationReady()` correctness check +invariant readyCheck(env e, bytes32 id) + (e.block.timestamp >= getTimestamp(id) && getTimestamp(id) > 1) <=> isOperationReady(e, id) + + +// STATUS - verified +// `isOperationDone()` correctness check +invariant doneCheck(bytes32 id) + getTimestamp(id) == _DONE_TIMESTAMP() <=> isOperationDone(id) @@ -73,102 +80,81 @@ function executionsCall(method f, env e, address target, uint256 value, bytes da //////////////////////////////////////////////////////////////////////////// -rule keccakCheck(method f, env e){ - address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; - address targetRand; uint256 valueRand; bytes dataRand; bytes32 predecessorRand; bytes32 saltRand; - - require data.length < 7; - // uint256 freshIndex; - // require freshIndex <= data.length - - // require target != targetRand || value != valueRand || data[freshIndex] != dataRand[freshIndex] || predecessor != predecessorRand || salt != saltRand; - - bytes32 a = hashOperation(target, value, data, predecessor, salt); - bytes32 b = hashOperation(target, value, data, predecessor, salt); - // bytes32 c = hashOperation(targetRand, valueRand, dataRand, predecessorRand, saltRand); - - assert a == b, "hashes are different"; - // assert a != c, "hashes are the same"; -} - - ///////////////////////////////////////////////////////////// // STATE TRANSITIONS ///////////////////////////////////////////////////////////// // STATUS - verified -// unset() -> unset() || pending() only +// Possible transitions: form `!isOperation()` to `!isOperation()` or `isOperationPending()` only rule unsetPendingTransitionGeneral(method f, env e){ bytes32 id; - require unset(id); + require !isOperation(id); require e.block.timestamp > 1; calldataarg args; f(e, args); - assert pending(id) || unset(id); + assert isOperationPending(id) || !isOperation(id); } // STATUS - verified -// unset() -> pending() via schedule() and scheduleBatch() only +// Possible transitions: form `!isOperation()` to `isOperationPending()` via `schedule()` and `scheduleBatch()` only rule unsetPendingTransitionMethods(method f, env e){ bytes32 id; - require unset(id); + require !isOperation(id); calldataarg args; f(e, args); - bool tmp = pending(id); - - assert pending(id) => (f.selector == schedule(address, uint256, bytes, bytes32, bytes32, uint256).selector + assert isOperationPending(id) => (f.selector == schedule(address, uint256, bytes, bytes32, bytes32, uint256).selector || f.selector == scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256).selector), "Why do we need to follow the schedule?"; } // STATUS - verified -// ready() -> done() via execute() and executeBatch() only +// Possible transitions: form `ready()` to `isOperationDone()` via `execute()` and `executeBatch()` only rule readyDoneTransition(method f, env e){ bytes32 id; - require ready(id, e); + require isOperationReady(e, id); calldataarg args; f(e, args); - assert done(id) => f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector - || f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector , "It's not done yet!"; + assert isOperationDone(id) => f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector + || f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector , "It's not isOperationDone yet!"; } // STATUS - verified -// pending() -> cancelled() via cancel() only +// isOperationPending() -> cancelled() via cancel() only rule pendingCancelledTransition(method f, env e){ bytes32 id; - require pending(id); + require isOperationPending(id); calldataarg args; f(e, args); - assert unset(id) => f.selector == cancel(bytes32).selector, "How you dare to cancel me?"; + assert !isOperation(id) => f.selector == cancel(bytes32).selector, "How you dare to cancel me?"; } // STATUS - verified -// done() -> nowhere +// isOperationDone() -> nowhere rule doneToNothingTransition(method f, env e){ bytes32 id; - require done(id); + require isOperationDone(id); calldataarg args; f(e, args); - assert done(id), "Did you find a way to escape? There is no way! HA-HA-HA"; + assert isOperationDone(id), "Did you find a way to escape? There is no way! HA-HA-HA"; } @@ -192,7 +178,170 @@ rule minDelayOnlyChange(method f, env e){ } -// STATUS - in progress +// STATUS - verified +// scheduled operation timestamp == block.timestamp + delay (kind of unit test) +rule scheduleCheck(method f, env e){ + bytes32 id; + + address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; + + hashIdCorrelation(id, target, value, data, predecessor, salt); + + schedule(e, target, value, data, predecessor, salt, delay); + + assert getTimestamp(id) == to_uint256(e.block.timestamp + delay), "Time doesn't obey to mortal souls"; +} + + +// STATUS - verified +// Cannot call `execute()` on a isOperationPending (not ready) operation +rule cannotCallExecute(method f, env e){ + address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; + bytes32 id; + + hashIdCorrelation(id, target, value, data, predecessor, salt); + require isOperationPending(id) && !isOperationReady(e, id); + + execute@withrevert(e, target, value, data, predecessor, salt); + + assert lastReverted, "you go against execution nature"; +} + + +// STATUS - verified +// Cannot call `execute()` on a !isOperation operation +rule executeRevertsFromUnset(method f, env e, env e2){ + address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; + bytes32 id; + + hashIdCorrelation(id, target, value, data, predecessor, salt); + require !isOperation(id); + + execute@withrevert(e, target, value, data, predecessor, salt); + + assert lastReverted, "you go against execution nature"; +} + + +// STATUS - verified +// Execute reverts => state returns to isOperationPending +rule executeRevertsEffectCheck(method f, env e){ + address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; + bytes32 id; + + hashIdCorrelation(id, target, value, data, predecessor, salt); + require isOperationPending(id) && !isOperationReady(e, id); + + execute@withrevert(e, target, value, data, predecessor, salt); + bool reverted = lastReverted; + + assert lastReverted => isOperationPending(id) && !isOperationReady(e, id), "you go against execution nature"; +} + + +// STATUS - verified +// Canceled operations cannot be executed → can’t move from canceled to isOperationDone +rule cancelledNotExecuted(method f, env e){ + bytes32 id; + + require !isOperation(id); + require e.block.timestamp > 1; + + calldataarg args; + f(e, args); + + assert !isOperationDone(id), "The ship is not a creature of the air"; +} + + +// STATUS - verified +// Only proposers can schedule +rule onlyProposer(method f, env e) filtered { f -> f.selector == schedule(address, uint256, bytes, bytes32, bytes32, uint256).selector + || f.selector == scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256).selector } { + bytes32 id; + bytes32 role; + address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; + + _checkRole@withrevert(e, PROPOSER_ROLE()); + + bool isCheckRoleReverted = lastReverted; + + calldataarg args; + f@withrevert(e, args); + + bool isScheduleReverted = lastReverted; + + assert isCheckRoleReverted => isScheduleReverted, "Enemy was detected"; +} + + +// STATUS - verified +// if `ready` then has waited minimum period after isOperationPending() +rule cooldown(method f, env e, env e2){ + bytes32 id; + address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; + uint256 minDelay = getMinDelay(); + + hashIdCorrelation(id, target, value, data, predecessor, salt); + + schedule(e, target, value, data, predecessor, salt, delay); + + calldataarg args; + f(e, args); + + assert isOperationReady(e2, id) => (e2.block.timestamp - e.block.timestamp >= minDelay), "No rush! When I'm ready, I'm ready"; +} + + +// STATUS - verified +// `schedule()` should change only one id's timestamp +rule scheduleChange(env e){ + bytes32 id; bytes32 otherId; + address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; + + uint256 otherIdTimestampBefore = getTimestamp(otherId); + + hashIdCorrelation(id, target, value, data, predecessor, salt); + + schedule(e, target, value, data, predecessor, salt, delay); + + assert id != otherId => otherIdTimestampBefore == getTimestamp(otherId), "Master of puppets, I'm pulling your strings"; +} + + +// STATUS - verified +// `execute()` should change only one id's timestamp +rule executeChange(env e){ + bytes32 id; bytes32 otherId; + address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; + uint256 otherIdTimestampBefore = getTimestamp(otherId); + + hashIdCorrelation(id, target, value, data, predecessor, salt); + + execute(e, target, value, data, predecessor, salt); + + assert id != otherId => otherIdTimestampBefore == getTimestamp(otherId), "Master of puppets, I'm pulling your strings"; +} + + +// STATUS - verified +// `cancel()` should change only one id's timestamp +rule cancelChange(env e){ + bytes32 id; bytes32 otherId; + + uint256 otherIdTimestampBefore = getTimestamp(otherId); + + cancel(e, id); + + assert id != otherId => otherIdTimestampBefore == getTimestamp(otherId), "Master of puppets, I'm pulling your strings"; +} + + + + + + +// STATUS - in progress // execute() is the only way to set timestamp to 1 rule getTimestampOnlyChange(method f, env e){ bytes32 id; @@ -210,143 +359,3 @@ rule getTimestampOnlyChange(method f, env e){ assert getTimestamp(id) == 1 => f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector || f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector, "Did you find a way to break the system?"; } - - -// STATUS - verified -// scheduled operation timestamp == block.timestamp + delay (kind of unit test) -rule scheduleCheck(method f, env e){ - bytes32 id; - - address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; - - require getTimestamp(id) < e.block.timestamp; - hashIdCorrelation(id, target, value, data, predecessor, salt); - - schedule(e, target, value, data, predecessor, salt, delay); - - assert getTimestamp(id) == to_uint256(e.block.timestamp + delay), "Time doesn't obey to mortal souls"; -} - - -// STATUS - verified -// Cannot call execute on a pending (not ready) operation -rule cannotCallExecute(method f, env e){ - address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; - bytes32 id; - - hashIdCorrelation(id, target, value, data, predecessor, salt); - require pending(id) && !ready(id, e); - - execute@withrevert(e, target, value, data, predecessor, salt); - - assert lastReverted, "you go against execution nature"; -} - - -// STATUS - verified -// in unset() execute() reverts -rule executeRevertsFromUnset(method f, env e, env e2){ - address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; - bytes32 id; - - hashIdCorrelation(id, target, value, data, predecessor, salt); - require unset(id); - - execute@withrevert(e, target, value, data, predecessor, salt); - - assert lastReverted, "you go against execution nature"; -} - - -// STATUS - verified -// Execute reverts => state returns to pending -rule executeRevertsEffectCheck(method f, env e){ - address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; - bytes32 id; - - hashIdCorrelation(id, target, value, data, predecessor, salt); - require pending(id) && !ready(id, e); - - execute@withrevert(e, target, value, data, predecessor, salt); - bool reverted = lastReverted; - - assert lastReverted => pending(id) && !ready(id, e), "you go against execution nature"; -} - - -// STATUS - verified -// Canceled operations cannot be executed → can’t move from canceled to ready -rule cancelledNotExecuted(method f, env e){ - bytes32 id; - - require unset(id); - require e.block.timestamp > 1; - - calldataarg args; - f(e, args); - - assert !done(id), "The ship is not a creature of the air"; -} - - -// STATUS - broken -// Only proposers can schedule an operation -rule onlyProposerCertorafallbackFail(method f, env e) filtered { f -> f.selector == schedule(address, uint256, bytes32, bytes32, bytes32, uint256).selector - || f.selector == scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256).selector } { - bytes32 id; - bytes32 role; - address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; - - // hashIdCorrelation(id, target, value, data, predecessor, salt); - - _checkRole@withrevert(e, PROPOSER_ROLE()); - - bool isCheckRoleReverted = lastReverted; - - // schedule@withrevert(e, target, value, data, predecessor, salt, delay); - - calldataarg args; - f@withrevert(e, args); - - bool isScheduleReverted = lastReverted; - - assert isCheckRoleReverted => isScheduleReverted, "Enemy was detected"; -} - - -// STATUS - verified -// Only proposers can schedule an operation -rule onlyProposer1(method f, env e){ - bytes32 id; - bytes32 role; - // address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; - address[] targets; uint256[] values; bytes[] datas; bytes32 predecessor; bytes32 salt; uint256 delay; - - // hashIdCorrelation(id, target, value, data, predecessor, salt); - - _checkRole@withrevert(e, PROPOSER_ROLE()); - - bool isCheckRoleReverted = lastReverted; - - // schedule@withrevert(e, target, value, data, predecessor, salt, delay); - scheduleBatch@withrevert(e, targets, values, datas, predecessor, salt, delay); - - bool isScheduleReverted = lastReverted; - - assert isCheckRoleReverted => isScheduleReverted, "Enemy was detected"; -} - - -// STATUS - in progress -// Ready = has waited minimum period after pending -rule cooldown(method f, env e, env e2){ - bytes32 id; - - require unset(id); - - calldataarg args; - f(e, args); - - // e.block.timestamp - delay > time scheduled => ready() - assert e.block.timestamp >= getTimestamp(id) => ready(id, e), "No rush! When I'm ready, I'm ready"; -} \ No newline at end of file From 66c72f2b5d0199ed6059a6632ad900725849100b Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Fri, 8 Apr 2022 20:52:38 +0100 Subject: [PATCH 166/254] CI preparations --- certora/scripts/verifyAccessControl.sh | 5 ++--- certora/scripts/verifyAllSasha.sh | 5 +++++ certora/scripts/verifyERC1155.sh | 6 ++---- certora/scripts/verifyERC20FlashMint.sh | 5 ++--- certora/scripts/verifyERC20Wrapper.sh | 5 ++--- certora/scripts/verifyTimelock.sh | 4 +--- certora/specs/ERC20Wrapper.spec | 1 + certora/specs/RulesInProgress.spec | 26 ++++++++++++++++++++++++- certora/specs/TimelockController.spec | 24 ----------------------- 9 files changed, 40 insertions(+), 41 deletions(-) create mode 100644 certora/scripts/verifyAllSasha.sh diff --git a/certora/scripts/verifyAccessControl.sh b/certora/scripts/verifyAccessControl.sh index 6b1a7ba05..eb706d063 100644 --- a/certora/scripts/verifyAccessControl.sh +++ b/certora/scripts/verifyAccessControl.sh @@ -3,7 +3,6 @@ certoraRun \ --verify AccessControlHarness:certora/specs/AccessControl.spec \ --solc solc8.2 \ --optimistic_loop \ - --staging \ - --rule_sanity \ - --msg "modifier check" + --cloud \ + --msg "AccessControl verification" \ No newline at end of file diff --git a/certora/scripts/verifyAllSasha.sh b/certora/scripts/verifyAllSasha.sh new file mode 100644 index 000000000..e87893bf9 --- /dev/null +++ b/certora/scripts/verifyAllSasha.sh @@ -0,0 +1,5 @@ +sh certora/scripts/verifyTimelock.sh +sh certora/scripts/verifyERC1155.sh +sh certora/scripts/verifyERC20FlashMint.sh +sh certora/scripts/verifyERC20Wrapper.sh +sh certora/scripts/verifyAccessControl.sh diff --git a/certora/scripts/verifyERC1155.sh b/certora/scripts/verifyERC1155.sh index 169df616d..84c59fc47 100644 --- a/certora/scripts/verifyERC1155.sh +++ b/certora/scripts/verifyERC1155.sh @@ -4,8 +4,6 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --loop_iter 3 \ - --staging \ - --rule_sanity \ - --rule "$1" \ - --msg "$1 check" + --cloud \ + --msg "ERC1155 verification" \ No newline at end of file diff --git a/certora/scripts/verifyERC20FlashMint.sh b/certora/scripts/verifyERC20FlashMint.sh index cd29f5b1a..0af81e006 100644 --- a/certora/scripts/verifyERC20FlashMint.sh +++ b/certora/scripts/verifyERC20FlashMint.sh @@ -4,7 +4,6 @@ certoraRun \ --verify ERC20FlashMintHarness:certora/specs/ERC20FlashMint.spec \ --solc solc8.2 \ --optimistic_loop \ - --staging \ - --rule_sanity \ - --msg "flashMint" + --cloud \ + --msg "ERC20FlashMint verification" \ No newline at end of file diff --git a/certora/scripts/verifyERC20Wrapper.sh b/certora/scripts/verifyERC20Wrapper.sh index 15654d84e..e1ef85ef0 100644 --- a/certora/scripts/verifyERC20Wrapper.sh +++ b/certora/scripts/verifyERC20Wrapper.sh @@ -4,7 +4,6 @@ certoraRun \ --verify ERC20WrapperHarness:certora/specs/ERC20Wrapper.spec \ --solc solc8.2 \ --optimistic_loop \ - --staging \ - --rule_sanity \ - --msg "ERC20wrapper spec" + --cloud \ + --msg "ERC20Wrapper verification" \ No newline at end of file diff --git a/certora/scripts/verifyTimelock.sh b/certora/scripts/verifyTimelock.sh index 2876d5f92..5afc11911 100644 --- a/certora/scripts/verifyTimelock.sh +++ b/certora/scripts/verifyTimelock.sh @@ -5,8 +5,6 @@ certoraRun \ --optimistic_loop \ --loop_iter 3 \ --staging alex/new-dt-hashing-alpha \ - --rule_sanity \ --settings -byteMapHashingPrecision=32 \ - --rule "$1" \ - --msg "$1" + --msg "TimelockController verification" \ No newline at end of file diff --git a/certora/specs/ERC20Wrapper.spec b/certora/specs/ERC20Wrapper.spec index 01c1b4295..bc95288e9 100644 --- a/certora/specs/ERC20Wrapper.spec +++ b/certora/specs/ERC20Wrapper.spec @@ -195,6 +195,7 @@ rule recoverSpec(env e){ uint256 wrapperTotalBefore = totalSupply(e); uint256 wrapperUserBalanceBefore = balanceOf(e, account); uint256 wrapperSenderBalanceBefore = balanceOf(e, e.msg.sender); + uint256 underlyingThisBalanceBefore = underlyingBalanceOf(currentContract); mathint value = underlyingThisBalanceBefore - wrapperTotalBefore; diff --git a/certora/specs/RulesInProgress.spec b/certora/specs/RulesInProgress.spec index cbad3336e..d0e2a6b74 100644 --- a/certora/specs/RulesInProgress.spec +++ b/certora/specs/RulesInProgress.spec @@ -136,4 +136,28 @@ rule possibleTotalVotes(uint256 pId, uint8 sup, env e, method f) { uint256 ps = proposalSnapshot(pId); assert tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)), "bla bla bla"; -} \ No newline at end of file +} + + + +/////////////////// 2nd iteration with OZ ////////////////////////// + +// STATUS - in progress +// execute() is the only way to set timestamp to 1 +rule getTimestampOnlyChange(method f, env e){ + bytes32 id; + address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; uint256 delay; + address[] targets; uint256[] values; bytes[] datas; + + require (targets[0] == target && values[0] == value && datas[0] == data) + || (targets[1] == target && values[1] == value && datas[1] == data) + || (targets[2] == target && values[2] == value && datas[2] == data); + + hashIdCorrelation(id, target, value, data, predecessor, salt); + + executionsCall(f, e, target, value, data, predecessor, salt, delay, targets, values, datas); + + assert getTimestamp(id) == 1 => f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector + || f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector, "Did you find a way to break the system?"; +} + diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec index fbce17bfd..29aeaee69 100644 --- a/certora/specs/TimelockController.spec +++ b/certora/specs/TimelockController.spec @@ -335,27 +335,3 @@ rule cancelChange(env e){ assert id != otherId => otherIdTimestampBefore == getTimestamp(otherId), "Master of puppets, I'm pulling your strings"; } - - - - - - -// STATUS - in progress -// execute() is the only way to set timestamp to 1 -rule getTimestampOnlyChange(method f, env e){ - bytes32 id; - address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; uint256 delay; - address[] targets; uint256[] values; bytes[] datas; - - require (targets[0] == target && values[0] == value && datas[0] == data) - || (targets[1] == target && values[1] == value && datas[1] == data) - || (targets[2] == target && values[2] == value && datas[2] == data); - - hashIdCorrelation(id, target, value, data, predecessor, salt); - - executionsCall(f, e, target, value, data, predecessor, salt, delay, targets, values, datas); - - assert getTimestamp(id) == 1 => f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector - || f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector, "Did you find a way to break the system?"; -} From 741e9a8b6d1c210dd055661971b2092d1b341959 Mon Sep 17 00:00:00 2001 From: Aleksander Kryukov Date: Fri, 8 Apr 2022 20:57:53 +0100 Subject: [PATCH 167/254] timelock function moved --- certora/specs/RulesInProgress.spec | 13 +++++++++++++ certora/specs/TimelockController.spec | 14 -------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/certora/specs/RulesInProgress.spec b/certora/specs/RulesInProgress.spec index d0e2a6b74..18a79d619 100644 --- a/certora/specs/RulesInProgress.spec +++ b/certora/specs/RulesInProgress.spec @@ -142,6 +142,19 @@ rule possibleTotalVotes(uint256 pId, uint8 sup, env e, method f) { /////////////////// 2nd iteration with OZ ////////////////////////// +function executionsCall(method f, env e, address target, uint256 value, bytes data, + bytes32 predecessor, bytes32 salt, uint256 delay, + address[] targets, uint256[] values, bytes[] datas) { + if (f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector) { + execute(e, target, value, data, predecessor, salt); + } else if (f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector) { + executeBatch(e, targets, values, datas, predecessor, salt); + } else { + calldataarg args; + f(e, args); + } +} + // STATUS - in progress // execute() is the only way to set timestamp to 1 rule getTimestampOnlyChange(method f, env e){ diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec index 29aeaee69..2277fecb1 100644 --- a/certora/specs/TimelockController.spec +++ b/certora/specs/TimelockController.spec @@ -30,20 +30,6 @@ function hashIdCorrelation(bytes32 id, address target, uint256 value, bytes data } -function executionsCall(method f, env e, address target, uint256 value, bytes data, - bytes32 predecessor, bytes32 salt, uint256 delay, - address[] targets, uint256[] values, bytes[] datas) { - if (f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector) { - execute(e, target, value, data, predecessor, salt); - } else if (f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector) { - executeBatch(e, targets, values, datas, predecessor, salt); - } else { - calldataarg args; - f(e, args); - } -} - - //////////////////////////////////////////////////////////////////////////// // Invariants // From cab9b09b7b3e61ea163c4107fb029725f769e0b1 Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Sun, 10 Apr 2022 19:55:23 -0700 Subject: [PATCH 168/254] rough contracts all finished --- certora/applyHarness.patch | 168 +++++++-- certora/harnesses/ERC721VotesHarness.sol | 26 ++ certora/harnesses/VotesHarness.sol | 14 - certora/munged/governance/utils/Votes.sol | 32 +- .../ERC721/extensions/draft-ERC721Votes.sol | 2 +- certora/scripts/verifyERC721Votes.sh | 25 ++ certora/specs/ERC20Votes.spec | 10 +- certora/specs/ERC721Votes.spec | 351 ++++++++++++++++++ 8 files changed, 573 insertions(+), 55 deletions(-) create mode 100644 certora/harnesses/ERC721VotesHarness.sol delete mode 100644 certora/harnesses/VotesHarness.sol create mode 100644 certora/scripts/verifyERC721Votes.sh create mode 100644 certora/specs/ERC721Votes.spec diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 7af5f82c8..3957b54f3 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,6 +1,6 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol --- access/AccessControl.sol 2022-03-02 09:14:55.000000000 -0800 -+++ access/AccessControl.sol 2022-03-24 18:08:46.000000000 -0700 ++++ access/AccessControl.sol 2022-04-08 17:31:22.000000000 -0700 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -12,7 +12,7 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol diff -ruN governance/TimelockController.sol governance/TimelockController.sol --- governance/TimelockController.sol 2022-03-02 09:14:55.000000000 -0800 -+++ governance/TimelockController.sol 2022-03-24 18:08:46.000000000 -0700 ++++ governance/TimelockController.sol 2022-04-08 17:31:22.000000000 -0700 @@ -24,10 +24,10 @@ bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); @@ -26,32 +26,147 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol /** * @dev Emitted when a call is scheduled as part of operation `id`. -@@ -353,4 +353,11 @@ +@@ -353,4 +353,4 @@ emit MinDelayChange(_minDelay, newDelay); _minDelay = newDelay; } -} -+ -+ -+ -+ function scheduleCheck1(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) { -+ bool tmp = false; -+ require(tmp); -+ } +} diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol --- governance/utils/Votes.sol 2022-03-02 09:14:55.000000000 -0800 -+++ governance/utils/Votes.sol 2022-03-24 18:08:46.000000000 -0700 -@@ -207,5 +207,5 @@ ++++ governance/utils/Votes.sol 2022-04-08 17:44:19.000000000 -0700 +@@ -35,7 +35,25 @@ + bytes32 private constant _DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + +- mapping(address => address) private _delegation; ++ // HARNESS : Hooks cannot access any information from Checkpoints yet, so I am also updating votes and fromBlock in this struct ++ struct Ckpt { ++ uint32 fromBlock; ++ uint224 votes; ++ } ++ mapping(address => Ckpt) public _checkpoints; ++ ++ // HARNESSED getters ++ function numCheckpoints(address account) public view returns (uint32) { ++ return SafeCast.toUint32(_delegateCheckpoints[account]._checkpoints.length); ++ } ++ function ckptFromBlock(address account, uint32 pos) public view returns (uint32) { ++ return _delegateCheckpoints[account]._checkpoints[pos]._blockNumber; ++ } ++ function ckptVotes(address account, uint32 pos) public view returns (uint224) { ++ return _delegateCheckpoints[account]._checkpoints[pos]._value; ++ } ++ ++ mapping(address => address) public _delegation; + mapping(address => Checkpoints.History) private _delegateCheckpoints; + Checkpoints.History private _totalCheckpoints; + +@@ -124,7 +142,7 @@ + * + * Emits events {DelegateChanged} and {DelegateVotesChanged}. + */ +- function _delegate(address account, address delegatee) internal virtual { ++ function _delegate(address account, address delegatee) public virtual { + address oldDelegate = delegates(account); + _delegation[account] = delegatee; + +@@ -142,10 +160,10 @@ + uint256 amount + ) internal virtual { + if (from == address(0)) { +- _totalCheckpoints.push(_add, amount); ++ _totalCheckpoints.push(_totalCheckpoints.latest() + amount); // Harnessed to remove function pointers + } + if (to == address(0)) { +- _totalCheckpoints.push(_subtract, amount); ++ _totalCheckpoints.push(_totalCheckpoints.latest() - amount); // Harnessed to remove function pointers + } + _moveDelegateVotes(delegates(from), delegates(to), amount); + } +@@ -160,11 +178,13 @@ + ) private { + if (from != to && amount > 0) { + if (from != address(0)) { +- (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_subtract, amount); ++ (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_delegateCheckpoints[from].latest() - amount); // HARNESSED TO REMOVE FUNCTION POINTERS ++ _checkpoints[from] = Ckpt({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newValue)}); // HARNESS + emit DelegateVotesChanged(from, oldValue, newValue); + } + if (to != address(0)) { +- (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_add, amount); ++ (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_delegateCheckpoints[to].latest() + amount); // HARNESSED TO REMOVE FUNCTION POINTERS ++ _checkpoints[to] = Ckpt({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newValue)}); // HARNESS + emit DelegateVotesChanged(to, oldValue, newValue); + } + } +@@ -207,5 +227,5 @@ /** * @dev Must return the voting units held by an account. */ - function _getVotingUnits(address) internal virtual returns (uint256); + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public } +diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol +--- token/ERC1155/ERC1155.sol 2022-03-02 09:14:55.000000000 -0800 ++++ token/ERC1155/ERC1155.sol 2022-04-08 17:31:22.000000000 -0700 +@@ -268,7 +268,7 @@ + uint256 id, + uint256 amount, + bytes memory data +- ) internal virtual { ++ ) public virtual { // HARNESS: internal -> public + require(to != address(0), "ERC1155: mint to the zero address"); + + address operator = _msgSender(); +@@ -299,7 +299,7 @@ + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data +- ) internal virtual { ++ ) public virtual { // HARNESS: internal -> public + require(to != address(0), "ERC1155: mint to the zero address"); + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + +@@ -330,7 +330,7 @@ + address from, + uint256 id, + uint256 amount +- ) internal virtual { ++ ) public virtual { // HARNESS: internal -> public + require(from != address(0), "ERC1155: burn from the zero address"); + + address operator = _msgSender(); +@@ -361,7 +361,7 @@ + address from, + uint256[] memory ids, + uint256[] memory amounts +- ) internal virtual { ++ ) public virtual { // HARNESS: internal -> public + require(from != address(0), "ERC1155: burn from the zero address"); + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + +@@ -465,7 +465,7 @@ + uint256 id, + uint256 amount, + bytes memory data +- ) private { ++ ) public { // HARNESS: private -> public + if (to.isContract()) { + try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { + if (response != IERC1155Receiver.onERC1155Received.selector) { +@@ -486,7 +486,7 @@ + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data +- ) private { ++ ) public { // HARNESS: private -> public + if (to.isContract()) { + try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( + bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol --- token/ERC20/ERC20.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/ERC20.sol 2022-03-24 18:08:46.000000000 -0700 ++++ token/ERC20/ERC20.sol 2022-04-08 17:31:22.000000000 -0700 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -72,7 +187,7 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol /** diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol --- token/ERC20/extensions/ERC20FlashMint.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-03-24 18:08:46.000000000 -0700 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-04-08 17:31:22.000000000 -0700 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -86,18 +201,9 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 /** * @dev Performs a flash loan. New tokens are minted and sent to the * `receiver`, who is required to implement the {IERC3156FlashBorrower} -@@ -70,7 +72,7 @@ - uint256 fee = flashFee(token, amount); - _mint(address(receiver), amount); - require( -- receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, -+ receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, // HAVOC_ALL - "ERC20FlashMint: invalid return value" - ); - uint256 currentAllowance = allowance(address(receiver), address(this)); diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol --- token/ERC20/extensions/ERC20Votes.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-03-25 13:13:49.000000000 -0700 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-04-08 17:31:22.000000000 -0700 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -230,7 +336,7 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote } diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol --- token/ERC20/extensions/ERC20Wrapper.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-03-24 18:08:46.000000000 -0700 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-04-08 17:31:22.000000000 -0700 @@ -44,7 +44,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -240,3 +346,15 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr uint256 value = underlying.balanceOf(address(this)) - totalSupply(); _mint(account, value); return value; +diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol +--- token/ERC721/extensions/draft-ERC721Votes.sol 2022-03-02 09:14:55.000000000 -0800 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-04-08 17:31:22.000000000 -0700 +@@ -34,7 +34,7 @@ + /** + * @dev Returns the balance of `account`. + */ +- function _getVotingUnits(address account) internal virtual override returns (uint256) { ++ function _getVotingUnits(address account) public virtual override returns (uint256) { + return balanceOf(account); + } + } diff --git a/certora/harnesses/ERC721VotesHarness.sol b/certora/harnesses/ERC721VotesHarness.sol new file mode 100644 index 000000000..9ff8911cd --- /dev/null +++ b/certora/harnesses/ERC721VotesHarness.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.8.0; + +import "../munged/token/ERC721/extensions/draft-ERC721Votes.sol"; + +contract ERC721VotesHarness is ERC721Votes { + constructor(string memory name, string memory symbol) ERC721(name, symbol) EIP712(name, symbol){} + + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + assert(true); + } + + function mint(address account, uint256 tokenID) public { + _mint(account, tokenID); + } + + function burn(uint256 tokenID) public { + _burn(tokenID); + } +} \ No newline at end of file diff --git a/certora/harnesses/VotesHarness.sol b/certora/harnesses/VotesHarness.sol deleted file mode 100644 index e57405d20..000000000 --- a/certora/harnesses/VotesHarness.sol +++ /dev/null @@ -1,14 +0,0 @@ -pragma solidity ^0.8.0; - -import "../munged/governance/utils/Votes.sol"; - - contract VotesHarness is Votes { - - constructor(string memory name, string memory version) EIP712(name, version) { - - } - - function _getVotingUnits(address) public override returns (uint256) { - return 0; - } -} \ No newline at end of file diff --git a/certora/munged/governance/utils/Votes.sol b/certora/munged/governance/utils/Votes.sol index 6afdc7cfe..df58c2561 100644 --- a/certora/munged/governance/utils/Votes.sol +++ b/certora/munged/governance/utils/Votes.sol @@ -35,7 +35,25 @@ abstract contract Votes is IVotes, Context, EIP712 { bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - mapping(address => address) private _delegation; + // HARNESS : Hooks cannot access any information from Checkpoints yet, so I am also updating votes and fromBlock in this struct + struct Ckpt { + uint32 fromBlock; + uint224 votes; + } + mapping(address => Ckpt) public _checkpoints; + + // HARNESSED getters + function numCheckpoints(address account) public view returns (uint32) { + return SafeCast.toUint32(_delegateCheckpoints[account]._checkpoints.length); + } + function ckptFromBlock(address account, uint32 pos) public view returns (uint32) { + return _delegateCheckpoints[account]._checkpoints[pos]._blockNumber; + } + function ckptVotes(address account, uint32 pos) public view returns (uint224) { + return _delegateCheckpoints[account]._checkpoints[pos]._value; + } + + mapping(address => address) public _delegation; mapping(address => Checkpoints.History) private _delegateCheckpoints; Checkpoints.History private _totalCheckpoints; @@ -124,7 +142,7 @@ abstract contract Votes is IVotes, Context, EIP712 { * * Emits events {DelegateChanged} and {DelegateVotesChanged}. */ - function _delegate(address account, address delegatee) internal virtual { + function _delegate(address account, address delegatee) public virtual { address oldDelegate = delegates(account); _delegation[account] = delegatee; @@ -142,10 +160,10 @@ abstract contract Votes is IVotes, Context, EIP712 { uint256 amount ) internal virtual { if (from == address(0)) { - _totalCheckpoints.push(_add, amount); + _totalCheckpoints.push(_totalCheckpoints.latest() + amount); // Harnessed to remove function pointers } if (to == address(0)) { - _totalCheckpoints.push(_subtract, amount); + _totalCheckpoints.push(_totalCheckpoints.latest() - amount); // Harnessed to remove function pointers } _moveDelegateVotes(delegates(from), delegates(to), amount); } @@ -160,11 +178,13 @@ abstract contract Votes is IVotes, Context, EIP712 { ) private { if (from != to && amount > 0) { if (from != address(0)) { - (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_subtract, amount); + (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_delegateCheckpoints[from].latest() - amount); // HARNESSED TO REMOVE FUNCTION POINTERS + _checkpoints[from] = Ckpt({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newValue)}); // HARNESS emit DelegateVotesChanged(from, oldValue, newValue); } if (to != address(0)) { - (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_add, amount); + (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_delegateCheckpoints[to].latest() + amount); // HARNESSED TO REMOVE FUNCTION POINTERS + _checkpoints[to] = Ckpt({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newValue)}); // HARNESS emit DelegateVotesChanged(to, oldValue, newValue); } } diff --git a/certora/munged/token/ERC721/extensions/draft-ERC721Votes.sol b/certora/munged/token/ERC721/extensions/draft-ERC721Votes.sol index 7d23c4921..67c584fc6 100644 --- a/certora/munged/token/ERC721/extensions/draft-ERC721Votes.sol +++ b/certora/munged/token/ERC721/extensions/draft-ERC721Votes.sol @@ -34,7 +34,7 @@ abstract contract ERC721Votes is ERC721, Votes { /** * @dev Returns the balance of `account`. */ - function _getVotingUnits(address account) internal virtual override returns (uint256) { + function _getVotingUnits(address account) public virtual override returns (uint256) { return balanceOf(account); } } diff --git a/certora/scripts/verifyERC721Votes.sh b/certora/scripts/verifyERC721Votes.sh new file mode 100644 index 000000000..cb23c2c64 --- /dev/null +++ b/certora/scripts/verifyERC721Votes.sh @@ -0,0 +1,25 @@ +make -C certora munged + +if [ -z "$1" ] + then + echo "Incorrect number of arguments" + echo "" + echo "Usage: (from git root)" + echo " ./certora/scripts/`basename $0` [message describing the run]" + echo "" + exit 1 +fi + +msg=$1 +shift 1 + +certoraRun \ + certora/harnesses/ERC721VotesHarness.sol \ + certora/munged/utils/Checkpoints.sol \ + --verify ERC721VotesHarness:certora/specs/ERC721Votes.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 4 \ + --staging "alex/new-dt-hashing-alpha" \ + --msg "${msg}" \ + # --rule_sanity \ No newline at end of file diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index 9f453d8b2..d24e400b8 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -1,5 +1,3 @@ -using ERC20VotesHarness as erc20votes - methods { // functions checkpoints(address, uint32) envfree @@ -164,14 +162,8 @@ rule transfer_safe() { env e; uint256 amount; address a; address b; - // require a != b; require delegates(a) != delegates(b); // confirmed if they both delegate to the same person then transfer keeps the votes the same - // requireInvariant fromBlock_constrains_numBlocks(a); - // requireInvariant fromBlock_constrains_numBlocks(b); - // requireInvariant totalVotes_gte_accounts(a, b); - // require lastIndex(delegates(a)) < 1000000; - // require lastIndex(delegates(b)) < 1000000; require numCheckpoints(delegates(a)) < 1000000; require numCheckpoints(delegates(b)) < 1000000; @@ -180,7 +172,7 @@ rule transfer_safe() { mathint totalVotes_pre = totalVotes(); - erc20votes.transferFrom(e, a, b, amount); + transferFrom(e, a, b, amount); mathint totalVotes_post = totalVotes(); uint256 votesA_post = getVotes(delegates(a)); diff --git a/certora/specs/ERC721Votes.spec b/certora/specs/ERC721Votes.spec new file mode 100644 index 000000000..e53fdb9e4 --- /dev/null +++ b/certora/specs/ERC721Votes.spec @@ -0,0 +1,351 @@ +using Checkpoints as Checkpoints + +methods { + // functions + checkpoints(address, uint32) envfree + numCheckpoints(address) returns (uint32) envfree + getVotes(address) returns (uint256) envfree + getPastVotes(address, uint256) returns (uint256) + getPastTotalSupply(uint256) returns (uint256) + delegates(address) returns (address) envfree + delegate(address) + _delegate(address, address) + delegateBySig(address, uint256, uint256, uint8, bytes32, bytes32) + nonces(address) returns (uint256) + totalSupply() returns (uint256) envfree + _maxSupply() returns (uint224) envfree + + // harnesss functions + ckptFromBlock(address, uint32) returns (uint32) envfree + ckptVotes(address, uint32) returns (uint224) envfree + mint(address, uint256) + burn(uint256) + unsafeNumCheckpoints(address) returns (uint256) envfree + + // solidity generated getters + _delegation(address) returns (address) envfree + + // external functions + + +} + +// gets the most recent votes for a user +ghost userVotes(address) returns uint224; + +// sums the total votes for all users +ghost totalVotes() returns mathint { + init_state axiom totalVotes() == 0; + axiom totalVotes() >= 0; +} + +hook Sstore _checkpoints[KEY address account].votes uint224 newVotes (uint224 oldVotes) STORAGE { + havoc userVotes assuming + userVotes@new(account) == newVotes; + + havoc totalVotes assuming + totalVotes@new() == totalVotes@old() + to_mathint(newVotes) - to_mathint(userVotes(account)); +} + + +ghost lastFromBlock(address) returns uint32; + +ghost doubleFromBlock(address) returns bool { + init_state axiom forall address a. doubleFromBlock(a) == false; +} + + + + +hook Sstore _checkpoints[KEY address account].fromBlock uint32 newBlock (uint32 oldBlock) STORAGE { + havoc lastFromBlock assuming + lastFromBlock@new(account) == newBlock; + + havoc doubleFromBlock assuming + doubleFromBlock@new(account) == (newBlock == lastFromBlock(account)); +} + +rule sanity(method f) { + env e; + calldataarg arg; + f(e, arg); + assert false; +} + +// something stupid just to see +invariant sanity_invariant() + totalSupply() >= 0 + +// sum of user balances is >= total amount of delegated votes +// blocked by tool error +invariant votes_solvency() + to_mathint(totalSupply()) >= totalVotes() +{ preserved with(env e) { + require forall address account. numCheckpoints(account) < 1000000; + requireInvariant totalVotes_sums_accounts(); +} } + +invariant totalVotes_sums_accounts() + forall address a. forall address b. (a != b && a != 0x0 && b != 0x0) => totalVotes() >= getVotes(delegates(a)) + getVotes(delegates(b)) + + +// for some checkpoint, the fromBlock is less than the current block number +// passes but fails rule sanity from hash on delegate by sig +invariant timestamp_constrains_fromBlock(address account, uint32 index, env e) + ckptFromBlock(account, index) < e.block.number +{ + preserved { + require index < numCheckpoints(account); + } +} + +// TODO add a note about this in the report +// // numCheckpoints are less than maxInt +// // passes because numCheckpoints does a safeCast +// invariant maxInt_constrains_numBlocks(address account) +// numCheckpoints(account) < 4294967295 // 2^32 + +// // fails because there are no checks to stop it +// invariant maxInt_constrains_ckptsLength(address account) +// unsafeNumCheckpoints(account) < 4294967295 // 2^32 + +// can't have more checkpoints for a given account than the last from block +// passes +invariant fromBlock_constrains_numBlocks(address account) + numCheckpoints(account) <= ckptFromBlock(account, numCheckpoints(account) - 1) +{ preserved with(env e) { + require e.block.number >= ckptFromBlock(account, numCheckpoints(account) - 1); // this should be true from the invariant above!! +}} + +// for any given checkpoint, the fromBlock must be greater than the checkpoint +// this proves the above invariant in combination with the below invariant +// if checkpoint has a greater fromBlock than the last, and the FromBlock is always greater than the pos. +// Then the number of positions must be less than the currentFromBlock +// ^note that the tool is assuming it's possible for the starting fromBlock to be 0 or anything, and does not know the current starting block +// passes + rule sanity +invariant fromBlock_greaterThanEq_pos(address account, uint32 pos) + ckptFromBlock(account, pos) >= pos + +// a larger index must have a larger fromBlock +// passes + rule sanity +invariant fromBlock_increasing(address account, uint32 pos, uint32 pos2) + pos > pos2 => ckptFromBlock(account, pos) > ckptFromBlock(account, pos2) + + +// converted from an invariant to a rule to slightly change the logic +// if the fromBlock is the same as before, then the number of checkpoints stays the same +// however if the fromBlock is new than the number of checkpoints increases +// passes, fails rule sanity because tautology check seems to be bugged +rule unique_checkpoints_rule(method f) { + env e; calldataarg args; + address account; + uint32 num_ckpts_ = numCheckpoints(account); + uint32 fromBlock_ = num_ckpts_ == 0 ? 0 : ckptFromBlock(account, num_ckpts_ - 1); + + f(e, args); + + uint32 _num_ckpts = numCheckpoints(account); + uint32 _fromBlock = _num_ckpts == 0 ? 0 : ckptFromBlock(account, _num_ckpts - 1); + + + assert fromBlock_ == _fromBlock => num_ckpts_ == _num_ckpts || _num_ckpts == 1, "same fromBlock, new checkpoint"; + // this assert fails consistently + // assert !doubleFromBlock(account) => ckpts_ != _ckpts, "new fromBlock but total checkpoints not being increased"; +} + +// assumes neither account has delegated +// currently fails due to this scenario. A has maxint number of checkpoints +// an additional checkpoint is added which overflows and sets A's votes to 0 +// passes + rule sanity (- a bad tautology check) +rule transfer_safe() { + env e; + uint256 ID; + address a; address b; + + require delegates(a) != delegates(b); // confirmed if they both delegate to the same person then transfer keeps the votes the same + require numCheckpoints(delegates(a)) < 1000000; + require numCheckpoints(delegates(b)) < 1000000; + + uint256 votesA_pre = getVotes(delegates(a)); + uint256 votesB_pre = getVotes(delegates(b)); + + mathint totalVotes_pre = totalVotes(); + + transferFrom(e, a, b, ID); + + mathint totalVotes_post = totalVotes(); + uint256 votesA_post = getVotes(delegates(a)); + uint256 votesB_post = getVotes(delegates(b)); + + // if an account that has not delegated transfers balance to an account that has, it will increase the total supply of votes + assert totalVotes_pre == totalVotes_post, "transfer changed total supply"; + assert delegates(a) != 0 => votesA_pre - 1 == votesA_post, "A lost the wrong amount of votes"; + assert delegates(b) != 0 => votesB_pre + 1 == votesB_post, "B gained the wrong amount of votes"; +} + +// for any given function f, if the delegate is changed the function must be delegate or delegateBySig +// passes +rule delegates_safe(method f) filtered {f -> (f.selector != delegate(address).selector && + f.selector != _delegate(address, address).selector && + f.selector != delegateBySig(address, uint256, uint256, uint8, bytes32, bytes32).selector) } +{ + env e; calldataarg args; + address account; + address pre = delegates(account); + + f(e, args); + + address post = delegates(account); + + assert pre == post, "invalid delegate change"; +} + +// delegates increases the delegatee's votes by the proper amount +// passes + rule sanity +rule delegatee_receives_votes() { + env e; + address delegator; address delegatee; + + require numCheckpoints(delegatee) < 1000000; + require delegates(delegator) != delegatee; + require delegatee != 0x0; + + + uint256 delegator_bal = balanceOf(e, delegator); + uint256 votes_= getVotes(delegatee); + + _delegate(e, delegator, delegatee); + + uint256 _votes = getVotes(delegatee); + assert _votes == votes_ + delegator_bal, "delegatee did not receive votes"; +} + +// passes + rule sanity +rule previous_delegatee_votes_removed() { + env e; + address delegator; address delegatee; address third; + + require third != delegatee; + require delegates(delegator) == third; + require numCheckpoints(third) < 1000000; + + uint256 delegator_bal = balanceOf(e, delegator); + uint256 votes_ = getVotes(third); + + _delegate(e, delegator, delegatee); + + uint256 _votes = getVotes(third); + + assert third != 0x0 => _votes == votes_ - delegator_bal, "votes not removed from the previous delegatee"; +} + +// passes with rule sanity +rule delegate_contained() { + env e; + address delegator; address delegatee; address other; + + require other != delegatee; + require other != delegates(delegator); + + uint256 votes_ = getVotes(other); + + _delegate(e, delegator, delegatee); + + uint256 _votes = getVotes(other); + + assert votes_ == _votes, "votes not contained"; +} + +rule delegate_no_frontrunning(method f) { + env e; calldataarg args; + address delegator; address delegatee; address third; address other; + + + + require numCheckpoints(delegatee) < 1000000; + require numCheckpoints(third) < 1000000; + + + f(e, args); + + uint256 delegator_bal = balanceOf(e, delegator); + uint256 delegatee_votes_ = getVotes(delegatee); + uint256 third_votes_ = getVotes(third); + uint256 other_votes_ = getVotes(other); + require delegates(delegator) == third; + require third != delegatee; + require other != third; + require other != delegatee; + require delegatee != 0x0; + + _delegate(e, delegator, delegatee); + + uint256 _delegatee_votes = getVotes(delegatee); + uint256 _third_votes = getVotes(third); + uint256 _other_votes = getVotes(other); + + + // previous delegatee loses all of their votes + // delegatee gains that many votes + // third loses any votes delegated to them + assert _delegatee_votes == delegatee_votes_ + delegator_bal, "delegatee did not receive votes"; + assert third != 0 => _third_votes == third_votes_ - delegator_bal, "votes not removed from third"; + assert other_votes_ == _other_votes, "delegate not contained"; +} + + +// mint and burn need to be handled differently for ERC721 + +// rule mint_increases_totalSupply() { + +// env e; +// uint256 amount; address account; +// uint256 fromBlock = e.block.number; +// uint256 totalSupply_ = totalSupply(); + +// mint(e, account, amount); + +// uint256 _totalSupply = totalSupply(); +// require _totalSupply < _maxSupply(); + +// assert _totalSupply == totalSupply_ + amount, "totalSupply not increased properly"; +// assert getPastTotalSupply(e, fromBlock) == totalSupply_ , "previous total supply not saved properly"; +// } + +// rule burn_decreases_totalSupply() { +// env e; +// uint256 amount; address account; + +// uint256 fromBlock = e.block.number; +// uint256 totalSupply_ = totalSupply(); + +// burn(e, account, amount); + +// uint256 _totalSupply = totalSupply(); + +// assert _totalSupply == totalSupply_ - amount, "totalSupply not decreased properly"; +// assert getPastTotalSupply(e, fromBlock) == totalSupply_ , "previous total supply not saved properly"; +// } + + +// rule mint_doesnt_increase_totalVotes() { +// env e; +// uint256 amount; address account; + +// mathint totalVotes_ = totalVotes(); + +// mint(e, account, amount); + +// assert totalVotes() == totalVotes_, "totalVotes increased"; +// } + +// rule burn_doesnt_decrease_totalVotes() { +// env e; +// uint256 amount; address account; + +// mathint totalVotes_ = totalVotes(); + +// burn(e, account, amount); + +// assert totalVotes() == totalVotes_, "totalVotes decreased"; +// } From 6662d0556f446c6849b13f769e40a6a288c8f4b3 Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Mon, 11 Apr 2022 12:46:56 -0700 Subject: [PATCH 169/254] verify all - rules passing --- certora/scripts/verifyAll.sh | 3 +++ certora/scripts/verifyAll2.sh | 7 +++++++ certora/scripts/verifyERC20Votes.sh | 5 +++-- certora/scripts/verifyERC721Votes.sh | 7 ++++--- certora/specs/ERC20Votes.spec | 17 ++++++++++++----- certora/specs/ERC721Votes.spec | 10 ++++++---- 6 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 certora/scripts/verifyAll2.sh diff --git a/certora/scripts/verifyAll.sh b/certora/scripts/verifyAll.sh index 90d76912c..4e04714bb 100644 --- a/certora/scripts/verifyAll.sh +++ b/certora/scripts/verifyAll.sh @@ -2,6 +2,8 @@ make -C certora munged + + for contract in certora/harnesses/Wizard*.sol; do for spec in certora/specs/*.spec; @@ -37,3 +39,4 @@ do fi done done + diff --git a/certora/scripts/verifyAll2.sh b/certora/scripts/verifyAll2.sh new file mode 100644 index 000000000..43c73e477 --- /dev/null +++ b/certora/scripts/verifyAll2.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +make -C certora munged + +sh certora/scripts/verifyAllSasha +sh certora/scripts/verifyERC20Votes.sh "checking ERC20Votes.spec on ERC20Votes.sol" +sh certora/scripts/verifyERC721Votes.sh "checking ERC721Votes.spec on draft-ERC721Votes.sol and Votes.sol" \ No newline at end of file diff --git a/certora/scripts/verifyERC20Votes.sh b/certora/scripts/verifyERC20Votes.sh index 22766e175..8bc5ed9fc 100644 --- a/certora/scripts/verifyERC20Votes.sh +++ b/certora/scripts/verifyERC20Votes.sh @@ -17,8 +17,9 @@ certoraRun \ certora/harnesses/ERC20VotesHarness.sol \ --verify ERC20VotesHarness:certora/specs/ERC20Votes.spec \ --solc solc8.2 \ + --disableLocalTypeChecking \ --optimistic_loop \ - --loop_iter 4 \ + --settings -copyLoopUnroll=4 \ + --send_only \ --staging "alex/new-dt-hashing-alpha" \ --msg "${msg}" \ - --rule_sanity diff --git a/certora/scripts/verifyERC721Votes.sh b/certora/scripts/verifyERC721Votes.sh index cb23c2c64..2f089b1c2 100644 --- a/certora/scripts/verifyERC721Votes.sh +++ b/certora/scripts/verifyERC721Votes.sh @@ -18,8 +18,9 @@ certoraRun \ certora/munged/utils/Checkpoints.sol \ --verify ERC721VotesHarness:certora/specs/ERC721Votes.spec \ --solc solc8.2 \ + --disableLocalTypeChecking \ --optimistic_loop \ - --loop_iter 4 \ + --settings -copyLoopUnroll=4 \ + --send_only \ --staging "alex/new-dt-hashing-alpha" \ - --msg "${msg}" \ - # --rule_sanity \ No newline at end of file + --msg "${msg}" \ \ No newline at end of file diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index d24e400b8..147587d83 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -27,7 +27,9 @@ methods { } // gets the most recent votes for a user -ghost userVotes(address) returns uint224; +ghost userVotes(address) returns uint224 { + init_state axiom forall address a. userVotes(a) == 0; +} // sums the total votes for all users ghost totalVotes() returns mathint { @@ -80,17 +82,22 @@ invariant sanity_invariant() totalSupply() >= 0 // sum of user balances is >= total amount of delegated votes -// blocked by tool error +// fails on burn. This is because burn does not remove votes from the users invariant votes_solvency() to_mathint(totalSupply()) >= totalVotes() { preserved with(env e) { require forall address account. numCheckpoints(account) < 1000000; - requireInvariant totalVotes_sums_accounts(); + // requireInvariant totalVotes_sums_accounts(); } } -invariant totalVotes_sums_accounts() - forall address a. forall address b. (a != b && a != 0x0 && b != 0x0) => totalVotes() >= getVotes(delegates(a)) + getVotes(delegates(b)) +// invariant totalVotes_sums_accounts() +// forall address a. forall address b. (a != b && a != 0x0 && b != 0x0) => totalVotes() >= getVotes(delegates(a)) + getVotes(delegates(b)) +// invariant totalVotes_sums_accounts() +// forall address a. forall address b. (a != b) => totalVotes() >= userVotes(a) + userVotes(b) +// { preserved { +// require forall address account. numCheckpoints(account) < 1000000; +// }} // for some checkpoint, the fromBlock is less than the current block number diff --git a/certora/specs/ERC721Votes.spec b/certora/specs/ERC721Votes.spec index e53fdb9e4..670f1fd59 100644 --- a/certora/specs/ERC721Votes.spec +++ b/certora/specs/ERC721Votes.spec @@ -31,7 +31,9 @@ methods { } // gets the most recent votes for a user -ghost userVotes(address) returns uint224; +ghost userVotes(address) returns uint224{ + init_state axiom forall address a. userVotes(a) == 0; +} // sums the total votes for all users ghost totalVotes() returns mathint { @@ -82,11 +84,11 @@ invariant votes_solvency() to_mathint(totalSupply()) >= totalVotes() { preserved with(env e) { require forall address account. numCheckpoints(account) < 1000000; - requireInvariant totalVotes_sums_accounts(); + // requireInvariant totalVotes_sums_accounts(); } } -invariant totalVotes_sums_accounts() - forall address a. forall address b. (a != b && a != 0x0 && b != 0x0) => totalVotes() >= getVotes(delegates(a)) + getVotes(delegates(b)) +// invariant totalVotes_sums_accounts() +// forall address a. forall address b. (a != b && a != 0x0 && b != 0x0) => totalVotes() >= getVotes(delegates(a)) + getVotes(delegates(b)) // for some checkpoint, the fromBlock is less than the current block number From 135e21f35dcc2b7be8987a51c71da311b959a8df Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Mon, 11 Apr 2022 13:26:46 -0700 Subject: [PATCH 170/254] comment cleanup --- certora/specs/ERC721Votes.spec | 72 ++-------------------------------- resource_errors.json | 3 ++ 2 files changed, 6 insertions(+), 69 deletions(-) create mode 100644 resource_errors.json diff --git a/certora/specs/ERC721Votes.spec b/certora/specs/ERC721Votes.spec index 670f1fd59..0df63d1e2 100644 --- a/certora/specs/ERC721Votes.spec +++ b/certora/specs/ERC721Votes.spec @@ -84,12 +84,8 @@ invariant votes_solvency() to_mathint(totalSupply()) >= totalVotes() { preserved with(env e) { require forall address account. numCheckpoints(account) < 1000000; - // requireInvariant totalVotes_sums_accounts(); } } -// invariant totalVotes_sums_accounts() -// forall address a. forall address b. (a != b && a != 0x0 && b != 0x0) => totalVotes() >= getVotes(delegates(a)) + getVotes(delegates(b)) - // for some checkpoint, the fromBlock is less than the current block number // passes but fails rule sanity from hash on delegate by sig @@ -101,16 +97,11 @@ invariant timestamp_constrains_fromBlock(address account, uint32 index, env e) } } -// TODO add a note about this in the report -// // numCheckpoints are less than maxInt -// // passes because numCheckpoints does a safeCast +// numCheckpoints are less than maxInt +// passes because numCheckpoints does a safeCast // invariant maxInt_constrains_numBlocks(address account) // numCheckpoints(account) < 4294967295 // 2^32 -// // fails because there are no checks to stop it -// invariant maxInt_constrains_ckptsLength(address account) -// unsafeNumCheckpoints(account) < 4294967295 // 2^32 - // can't have more checkpoints for a given account than the last from block // passes invariant fromBlock_constrains_numBlocks(address account) @@ -293,61 +284,4 @@ rule delegate_no_frontrunning(method f) { assert _delegatee_votes == delegatee_votes_ + delegator_bal, "delegatee did not receive votes"; assert third != 0 => _third_votes == third_votes_ - delegator_bal, "votes not removed from third"; assert other_votes_ == _other_votes, "delegate not contained"; -} - - -// mint and burn need to be handled differently for ERC721 - -// rule mint_increases_totalSupply() { - -// env e; -// uint256 amount; address account; -// uint256 fromBlock = e.block.number; -// uint256 totalSupply_ = totalSupply(); - -// mint(e, account, amount); - -// uint256 _totalSupply = totalSupply(); -// require _totalSupply < _maxSupply(); - -// assert _totalSupply == totalSupply_ + amount, "totalSupply not increased properly"; -// assert getPastTotalSupply(e, fromBlock) == totalSupply_ , "previous total supply not saved properly"; -// } - -// rule burn_decreases_totalSupply() { -// env e; -// uint256 amount; address account; - -// uint256 fromBlock = e.block.number; -// uint256 totalSupply_ = totalSupply(); - -// burn(e, account, amount); - -// uint256 _totalSupply = totalSupply(); - -// assert _totalSupply == totalSupply_ - amount, "totalSupply not decreased properly"; -// assert getPastTotalSupply(e, fromBlock) == totalSupply_ , "previous total supply not saved properly"; -// } - - -// rule mint_doesnt_increase_totalVotes() { -// env e; -// uint256 amount; address account; - -// mathint totalVotes_ = totalVotes(); - -// mint(e, account, amount); - -// assert totalVotes() == totalVotes_, "totalVotes increased"; -// } - -// rule burn_doesnt_decrease_totalVotes() { -// env e; -// uint256 amount; address account; - -// mathint totalVotes_ = totalVotes(); - -// burn(e, account, amount); - -// assert totalVotes() == totalVotes_, "totalVotes decreased"; -// } +} \ No newline at end of file diff --git a/resource_errors.json b/resource_errors.json new file mode 100644 index 000000000..d9bd79235 --- /dev/null +++ b/resource_errors.json @@ -0,0 +1,3 @@ +{ + "topics": [] +} \ No newline at end of file From f242abbf939b4bc23fb9fe598f39ab8215b2e5c5 Mon Sep 17 00:00:00 2001 From: Michael George Date: Mon, 11 Apr 2022 16:35:47 -0400 Subject: [PATCH 171/254] starting CI integration --- .github/workflows/verify.yml | 57 +++++++++++++++++++++++++++++++ .gitignore | 1 + certora/scripts/verifyAllSasha.sh | 5 --- 3 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/verify.yml delete mode 100644 certora/scripts/verifyAllSasha.sh diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 000000000..6919609d0 --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,57 @@ +name: Certora + +on: + push: + branches: + - main + - certora/erc20 + +jobs: + verify: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Install python + uses: actions/setup-python@v2 + with: { python-version: 3.6, cache: 'pip' } + + - name: Install java + uses: actions/setup-java@v1 + with: { java-version: "11", java-package: jre } + + - name: Install certora + run: pip install certora-cli + + - name: Install solc + run: | + wget https://github.com/ethereum/solidity/releases/download/v0.8.2/solc-static-linux + chmod +x solc-static-linux + sudo mv solc-static-linux /usr/local/bin/solc8.2 + + - name: Verify rule ${{ matrix.script }} + run: | + touch certora/applyHarness.patch + make -C certora munged + echo "key length" ${#CERTORAKEY} + sh certora/scripts/${{ matrix.script }} + env: + CERTORAKEY: ${{ secrets.CERTORAKEY }} + + strategy: + fail-fast: false + max-parallel: 4 + + matrix: + script: + - verifyTimelock.sh + - verifyERC1155.sh + - verifyERC20FlashMint.sh + - verifyERC20Wrapper.sh + - verifyAccessControl.sh + - verifyERC20Votes.sh "checking ERC20Votes.spec on ERC20Votes.sol" + - verifyERC721Votes.sh "checking ERC721Votes.spec on draft-ERC721Votes.sol and Votes.sol" + + + diff --git a/.gitignore b/.gitignore index c60c5d945..18817cd29 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ artifacts .certora* .last_confs certora_* +resource_errors.json diff --git a/certora/scripts/verifyAllSasha.sh b/certora/scripts/verifyAllSasha.sh deleted file mode 100644 index e87893bf9..000000000 --- a/certora/scripts/verifyAllSasha.sh +++ /dev/null @@ -1,5 +0,0 @@ -sh certora/scripts/verifyTimelock.sh -sh certora/scripts/verifyERC1155.sh -sh certora/scripts/verifyERC20FlashMint.sh -sh certora/scripts/verifyERC20Wrapper.sh -sh certora/scripts/verifyAccessControl.sh From f15308f7633267bb4c8aea6451a09ae559339c76 Mon Sep 17 00:00:00 2001 From: Michael George Date: Mon, 11 Apr 2022 16:37:21 -0400 Subject: [PATCH 172/254] added python requirements file --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..3373e34c4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +certora-cli==3.0.0 From 8fc90f67797aa653cb570dfdc1d1a41a5902fa7d Mon Sep 17 00:00:00 2001 From: Michael George Date: Mon, 11 Apr 2022 16:55:54 -0400 Subject: [PATCH 173/254] fix in script --- certora/scripts/verifyERC721Votes.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/scripts/verifyERC721Votes.sh b/certora/scripts/verifyERC721Votes.sh index 2f089b1c2..e2a7320f9 100644 --- a/certora/scripts/verifyERC721Votes.sh +++ b/certora/scripts/verifyERC721Votes.sh @@ -23,4 +23,4 @@ certoraRun \ --settings -copyLoopUnroll=4 \ --send_only \ --staging "alex/new-dt-hashing-alpha" \ - --msg "${msg}" \ \ No newline at end of file + --msg "${msg}" From 02de598056be747a9e83213eafcdd019b8211cbe Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Mon, 11 Apr 2022 13:56:34 -0700 Subject: [PATCH 174/254] removed sanity --- certora/specs/ERC20Votes.spec | 56 ++-------------------------------- certora/specs/ERC721Votes.spec | 11 ------- 2 files changed, 3 insertions(+), 64 deletions(-) diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index 147587d83..e74509cf2 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -70,35 +70,14 @@ hook Sstore _checkpoints[KEY address account][INDEX uint32 index].fromBlock uint doubleFromBlock@new(account) == (newBlock == lastFromBlock(account)); } -rule sanity(method f) { - env e; - calldataarg arg; - f(e, arg); - assert false; -} - -// something stupid just to see -invariant sanity_invariant() - totalSupply() >= 0 - // sum of user balances is >= total amount of delegated votes // fails on burn. This is because burn does not remove votes from the users invariant votes_solvency() to_mathint(totalSupply()) >= totalVotes() { preserved with(env e) { require forall address account. numCheckpoints(account) < 1000000; - // requireInvariant totalVotes_sums_accounts(); } } -// invariant totalVotes_sums_accounts() -// forall address a. forall address b. (a != b && a != 0x0 && b != 0x0) => totalVotes() >= getVotes(delegates(a)) + getVotes(delegates(b)) - -// invariant totalVotes_sums_accounts() -// forall address a. forall address b. (a != b) => totalVotes() >= userVotes(a) + userVotes(b) -// { preserved { -// require forall address account. numCheckpoints(account) < 1000000; -// }} - // for some checkpoint, the fromBlock is less than the current block number invariant blockNum_constrains_fromBlock(address account, uint32 index, env e) @@ -109,16 +88,11 @@ invariant blockNum_constrains_fromBlock(address account, uint32 index, env e) } } -// TODO add a note about this in the report -// // numCheckpoints are less than maxInt -// // passes because numCheckpoints does a safeCast +// numCheckpoints are less than maxInt +// passes because numCheckpoints does a safeCast // invariant maxInt_constrains_numBlocks(address account) // numCheckpoints(account) < 4294967295 // 2^32 -// // fails because there are no checks to stop it -// invariant maxInt_constrains_ckptsLength(address account) -// unsafeNumCheckpoints(account) < 4294967295 // 2^32 - // can't have more checkpoints for a given account than the last from block // passes invariant fromBlock_constrains_numBlocks(address account) @@ -362,28 +336,4 @@ rule burn_doesnt_decrease_totalVotes() { burn(e, account, amount); assert totalVotes() == totalVotes_, "totalVotes decreased"; -} - -// // fails -// rule mint_increases_totalVotes() { -// env e; -// uint256 amount; address account; - -// mathint totalVotes_ = totalVotes(); - -// mint(e, account, amount); - -// assert totalVotes() == totalVotes_ + to_mathint(amount), "totalVotes not increased"; -// } - -// // fails -// rule burn_decreases_totalVotes() { -// env e; -// uint256 amount; address account; - -// mathint totalVotes_ = totalVotes(); - -// burn(e, account, amount); - -// assert totalVotes() == totalVotes_ - to_mathint(amount), "totalVotes not decreased"; -// } +} \ No newline at end of file diff --git a/certora/specs/ERC721Votes.spec b/certora/specs/ERC721Votes.spec index 0df63d1e2..79bbd563b 100644 --- a/certora/specs/ERC721Votes.spec +++ b/certora/specs/ERC721Votes.spec @@ -67,17 +67,6 @@ hook Sstore _checkpoints[KEY address account].fromBlock uint32 newBlock (uint32 doubleFromBlock@new(account) == (newBlock == lastFromBlock(account)); } -rule sanity(method f) { - env e; - calldataarg arg; - f(e, arg); - assert false; -} - -// something stupid just to see -invariant sanity_invariant() - totalSupply() >= 0 - // sum of user balances is >= total amount of delegated votes // blocked by tool error invariant votes_solvency() From 4a3cddc52934c121b33db60febf7b9511db974c3 Mon Sep 17 00:00:00 2001 From: Michael George Date: Fri, 6 May 2022 13:21:36 -0400 Subject: [PATCH 175/254] temporarily moved old projects into old directory --- certora/scripts/{ => old}/ERC20VotesRule.sh | 0 certora/scripts/{ => old}/Governor.sh | 0 certora/scripts/{ => old}/GovernorCountingSimple-counting.sh | 0 certora/scripts/{ => old}/WizardControlFirstPriority.sh | 0 certora/scripts/{ => old}/WizardFirstTry.sh | 0 certora/scripts/{ => old}/sanity.sh | 0 certora/scripts/{ => old}/sanityGovernor.sh | 0 certora/scripts/{ => old}/sanityTokens.sh | 0 certora/scripts/{ => old}/verifyAccessControl.sh | 0 certora/scripts/{ => old}/verifyAll.sh | 0 certora/scripts/{ => old}/verifyAll2.sh | 0 certora/scripts/{ => old}/verifyERC1155.sh | 0 certora/scripts/{ => old}/verifyERC20FlashMint.sh | 0 certora/scripts/{ => old}/verifyERC20Votes.sh | 0 certora/scripts/{ => old}/verifyERC20Wrapper.sh | 0 certora/scripts/{ => old}/verifyERC721Votes.sh | 0 certora/scripts/{ => old}/verifyGovernor.sh | 0 certora/scripts/{ => old}/verifyTimelock.sh | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename certora/scripts/{ => old}/ERC20VotesRule.sh (100%) rename certora/scripts/{ => old}/Governor.sh (100%) rename certora/scripts/{ => old}/GovernorCountingSimple-counting.sh (100%) rename certora/scripts/{ => old}/WizardControlFirstPriority.sh (100%) rename certora/scripts/{ => old}/WizardFirstTry.sh (100%) rename certora/scripts/{ => old}/sanity.sh (100%) rename certora/scripts/{ => old}/sanityGovernor.sh (100%) rename certora/scripts/{ => old}/sanityTokens.sh (100%) rename certora/scripts/{ => old}/verifyAccessControl.sh (100%) rename certora/scripts/{ => old}/verifyAll.sh (100%) rename certora/scripts/{ => old}/verifyAll2.sh (100%) rename certora/scripts/{ => old}/verifyERC1155.sh (100%) rename certora/scripts/{ => old}/verifyERC20FlashMint.sh (100%) rename certora/scripts/{ => old}/verifyERC20Votes.sh (100%) rename certora/scripts/{ => old}/verifyERC20Wrapper.sh (100%) rename certora/scripts/{ => old}/verifyERC721Votes.sh (100%) rename certora/scripts/{ => old}/verifyGovernor.sh (100%) rename certora/scripts/{ => old}/verifyTimelock.sh (100%) diff --git a/certora/scripts/ERC20VotesRule.sh b/certora/scripts/old/ERC20VotesRule.sh similarity index 100% rename from certora/scripts/ERC20VotesRule.sh rename to certora/scripts/old/ERC20VotesRule.sh diff --git a/certora/scripts/Governor.sh b/certora/scripts/old/Governor.sh similarity index 100% rename from certora/scripts/Governor.sh rename to certora/scripts/old/Governor.sh diff --git a/certora/scripts/GovernorCountingSimple-counting.sh b/certora/scripts/old/GovernorCountingSimple-counting.sh similarity index 100% rename from certora/scripts/GovernorCountingSimple-counting.sh rename to certora/scripts/old/GovernorCountingSimple-counting.sh diff --git a/certora/scripts/WizardControlFirstPriority.sh b/certora/scripts/old/WizardControlFirstPriority.sh similarity index 100% rename from certora/scripts/WizardControlFirstPriority.sh rename to certora/scripts/old/WizardControlFirstPriority.sh diff --git a/certora/scripts/WizardFirstTry.sh b/certora/scripts/old/WizardFirstTry.sh similarity index 100% rename from certora/scripts/WizardFirstTry.sh rename to certora/scripts/old/WizardFirstTry.sh diff --git a/certora/scripts/sanity.sh b/certora/scripts/old/sanity.sh similarity index 100% rename from certora/scripts/sanity.sh rename to certora/scripts/old/sanity.sh diff --git a/certora/scripts/sanityGovernor.sh b/certora/scripts/old/sanityGovernor.sh similarity index 100% rename from certora/scripts/sanityGovernor.sh rename to certora/scripts/old/sanityGovernor.sh diff --git a/certora/scripts/sanityTokens.sh b/certora/scripts/old/sanityTokens.sh similarity index 100% rename from certora/scripts/sanityTokens.sh rename to certora/scripts/old/sanityTokens.sh diff --git a/certora/scripts/verifyAccessControl.sh b/certora/scripts/old/verifyAccessControl.sh similarity index 100% rename from certora/scripts/verifyAccessControl.sh rename to certora/scripts/old/verifyAccessControl.sh diff --git a/certora/scripts/verifyAll.sh b/certora/scripts/old/verifyAll.sh similarity index 100% rename from certora/scripts/verifyAll.sh rename to certora/scripts/old/verifyAll.sh diff --git a/certora/scripts/verifyAll2.sh b/certora/scripts/old/verifyAll2.sh similarity index 100% rename from certora/scripts/verifyAll2.sh rename to certora/scripts/old/verifyAll2.sh diff --git a/certora/scripts/verifyERC1155.sh b/certora/scripts/old/verifyERC1155.sh similarity index 100% rename from certora/scripts/verifyERC1155.sh rename to certora/scripts/old/verifyERC1155.sh diff --git a/certora/scripts/verifyERC20FlashMint.sh b/certora/scripts/old/verifyERC20FlashMint.sh similarity index 100% rename from certora/scripts/verifyERC20FlashMint.sh rename to certora/scripts/old/verifyERC20FlashMint.sh diff --git a/certora/scripts/verifyERC20Votes.sh b/certora/scripts/old/verifyERC20Votes.sh similarity index 100% rename from certora/scripts/verifyERC20Votes.sh rename to certora/scripts/old/verifyERC20Votes.sh diff --git a/certora/scripts/verifyERC20Wrapper.sh b/certora/scripts/old/verifyERC20Wrapper.sh similarity index 100% rename from certora/scripts/verifyERC20Wrapper.sh rename to certora/scripts/old/verifyERC20Wrapper.sh diff --git a/certora/scripts/verifyERC721Votes.sh b/certora/scripts/old/verifyERC721Votes.sh similarity index 100% rename from certora/scripts/verifyERC721Votes.sh rename to certora/scripts/old/verifyERC721Votes.sh diff --git a/certora/scripts/verifyGovernor.sh b/certora/scripts/old/verifyGovernor.sh similarity index 100% rename from certora/scripts/verifyGovernor.sh rename to certora/scripts/old/verifyGovernor.sh diff --git a/certora/scripts/verifyTimelock.sh b/certora/scripts/old/verifyTimelock.sh similarity index 100% rename from certora/scripts/verifyTimelock.sh rename to certora/scripts/old/verifyTimelock.sh From 70cbfffc7446e4391806736ad7b5444ba9514163 Mon Sep 17 00:00:00 2001 From: Michael George Date: Fri, 6 May 2022 13:23:52 -0400 Subject: [PATCH 176/254] created harness and script --- certora/harnesses/ERC1155/ERC1155PausableHarness.sol | 6 ++++++ certora/scripts/verifyERC1155Pausable.sh | 8 ++++++++ 2 files changed, 14 insertions(+) create mode 100644 certora/harnesses/ERC1155/ERC1155PausableHarness.sol create mode 100755 certora/scripts/verifyERC1155Pausable.sh diff --git a/certora/harnesses/ERC1155/ERC1155PausableHarness.sol b/certora/harnesses/ERC1155/ERC1155PausableHarness.sol new file mode 100644 index 000000000..23675091d --- /dev/null +++ b/certora/harnesses/ERC1155/ERC1155PausableHarness.sol @@ -0,0 +1,6 @@ +import "../../munged/token/ERC1155/extensions/ERC1155Pausable.sol" + +contract ERC1155PausableHarness is ERC1155Pausable { + +} + diff --git a/certora/scripts/verifyERC1155Pausable.sh b/certora/scripts/verifyERC1155Pausable.sh new file mode 100755 index 000000000..e1af9c3d0 --- /dev/null +++ b/certora/scripts/verifyERC1155Pausable.sh @@ -0,0 +1,8 @@ +certoraRun \ + certora/harness/ERC1155/ERC1155PausableHarness.sol \ + --verify ERC1155PausableHarness:certora/specs/ERC1155Pausable.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --cloud \ + --msg "ERC1155 Pausable verification" From aafb14461be6d71d02afbe57a06457725595f9af Mon Sep 17 00:00:00 2001 From: Michael George Date: Fri, 6 May 2022 14:12:32 -0400 Subject: [PATCH 177/254] made the spec run --- .../ERC1155/ERC1155PausableHarness.sol | 18 +++++++++++++++++- certora/scripts/verifyERC1155Pausable.sh | 2 +- certora/specs/ERC1155Pausable.spec | 9 +++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 certora/specs/ERC1155Pausable.spec diff --git a/certora/harnesses/ERC1155/ERC1155PausableHarness.sol b/certora/harnesses/ERC1155/ERC1155PausableHarness.sol index 23675091d..134a78748 100644 --- a/certora/harnesses/ERC1155/ERC1155PausableHarness.sol +++ b/certora/harnesses/ERC1155/ERC1155PausableHarness.sol @@ -1,6 +1,22 @@ -import "../../munged/token/ERC1155/extensions/ERC1155Pausable.sol" +import "../../munged/token/ERC1155/extensions/ERC1155Pausable.sol"; contract ERC1155PausableHarness is ERC1155Pausable { + constructor(string memory uri_) + ERC1155(uri_) + {} + function pause() public { + _pause(); + } + + function unpause() public { + _unpause(); + } + + function onlyWhenPausedMethod() public whenPaused { + } + + function onlyWhenNotPausedMethod() public whenNotPaused { + } } diff --git a/certora/scripts/verifyERC1155Pausable.sh b/certora/scripts/verifyERC1155Pausable.sh index e1af9c3d0..fe7d1a024 100755 --- a/certora/scripts/verifyERC1155Pausable.sh +++ b/certora/scripts/verifyERC1155Pausable.sh @@ -1,5 +1,5 @@ certoraRun \ - certora/harness/ERC1155/ERC1155PausableHarness.sol \ + certora/harnesses/ERC1155/ERC1155PausableHarness.sol \ --verify ERC1155PausableHarness:certora/specs/ERC1155Pausable.spec \ --solc solc8.2 \ --optimistic_loop \ diff --git a/certora/specs/ERC1155Pausable.spec b/certora/specs/ERC1155Pausable.spec new file mode 100644 index 000000000..70f738896 --- /dev/null +++ b/certora/specs/ERC1155Pausable.spec @@ -0,0 +1,9 @@ + +rule sanity { + method f; env e; calldataarg args; + + f(e, args); + + assert false; +} + From da0fdc1aa07f85d556381b0425ca2e0af09039c3 Mon Sep 17 00:00:00 2001 From: Michael George Date: Mon, 9 May 2022 17:30:57 -0400 Subject: [PATCH 178/254] harness setup for ERC1155Supply --- certora/harnesses/ERC1155/ERC1155SupplyHarness.sol | 8 ++++++++ certora/scripts/verifyERC1155Supply.sh | 8 ++++++++ certora/specs/ERC1155Supply.spec | 9 +++++++++ 3 files changed, 25 insertions(+) create mode 100644 certora/harnesses/ERC1155/ERC1155SupplyHarness.sol create mode 100755 certora/scripts/verifyERC1155Supply.sh create mode 100644 certora/specs/ERC1155Supply.spec diff --git a/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol b/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol new file mode 100644 index 000000000..53b73dc6c --- /dev/null +++ b/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol @@ -0,0 +1,8 @@ +import "../../munged/token/ERC1155/extensions/ERC1155Supply.sol"; + +contract ERC1155SupplyHarness is ERC1155Supply { + constructor(string memory uri_) + ERC1155(uri_) + {} +} + diff --git a/certora/scripts/verifyERC1155Supply.sh b/certora/scripts/verifyERC1155Supply.sh new file mode 100755 index 000000000..dba264693 --- /dev/null +++ b/certora/scripts/verifyERC1155Supply.sh @@ -0,0 +1,8 @@ +certoraRun \ + certora/harnesses/ERC1155/ERC1155SupplyHarness.sol \ + --verify ERC1155SupplyHarness:certora/specs/ERC1155Supply.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --cloud \ + --msg "ERC1155 Supply verification" diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec new file mode 100644 index 000000000..70f738896 --- /dev/null +++ b/certora/specs/ERC1155Supply.spec @@ -0,0 +1,9 @@ + +rule sanity { + method f; env e; calldataarg args; + + f(e, args); + + assert false; +} + From 2fc3a5d4b8166aa53f357618272c12c98b6a632e Mon Sep 17 00:00:00 2001 From: Michael George Date: Mon, 9 May 2022 17:54:01 -0400 Subject: [PATCH 179/254] implemented independence rule --- certora/specs/ERC1155Supply.spec | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index 70f738896..f73acba10 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -1,4 +1,34 @@ +methods { + totalSupply(uint256) returns uint256 envfree +} + +/// given two different token ids, if totalSupply for one changes, then +/// totalSupply for other should not +rule token_totalSupply_independence(method f) +filtered { + f -> f.selector != _burnBatch(address,uint256[],uint256[]).selector + && f.selector != _mintBatch(address,uint256[],uint256[],bytes).selector + && f.selector != safeBatchTransferFrom(address,address,uint256[],uint256[],bytes).selector +} +{ + uint256 token1; uint256 token2; + require token1 != token2; + + uint256 token1_before = totalSupply(token1); + uint256 token2_before = totalSupply(token2); + + env e; calldataarg args; + f(e, args); + + uint256 token1_after = totalSupply(token1); + uint256 token2_after = totalSupply(token2); + + assert token1_after != token1_before => token2_after == token2_before, + "methods must not change the total supply of more than one token"; +} + + rule sanity { method f; env e; calldataarg args; From 0deaee1217fd6522e194b0fa18615da6e97d4522 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 10 May 2022 17:36:19 -0700 Subject: [PATCH 180/254] Added unfinished invariant regarding user token sums and totalSupply --- certora/specs/ERC1155Supply.spec | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index f73acba10..e6769afc0 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -1,6 +1,7 @@ methods { totalSupply(uint256) returns uint256 envfree + balanceOf(address, uint256) returns uint256 envfree } /// given two different token ids, if totalSupply for one changes, then @@ -28,6 +29,14 @@ filtered { "methods must not change the total supply of more than one token"; } +invariant sum_user_token_balances_vs_totalSupply(uint256 id, address user1, address user2) + balanceOf(user1, id) + balanceOf(user2, id) <= totalSupply(id) +{ preserved { + require user1 != user2; + //for every address not user1 or user2, balance is < user1 and < user2 + require forall address user3. (user3 != user1 && user3 != user2) => balanceOf(user3, id) < balanceOf(user1, id) && balanceOf(user3, id) < balanceOf(user2, id); + } +} rule sanity { method f; env e; calldataarg args; From 6add1e7718ee9c0c6be1b9e69eede481bbed5007 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Wed, 11 May 2022 11:35:24 -0700 Subject: [PATCH 181/254] setup GovLateQuorum and add 3 rules for deadlines --- certora/applyHarness.patch | 62 +++++-- .../GovernorPreventLateQuorumHarness.sol | 159 ++++++++++++++++++ certora/munged/governance/Governor.sol | 2 +- .../extensions/GovernorPreventLateQuorum.sol | 4 +- .../verifyGovernorPreventLateQuorum.sh | 9 + certora/specs/GovernorPreventLateQuorum.spec | 115 +++++++++++++ 6 files changed, 330 insertions(+), 21 deletions(-) create mode 100644 certora/harnesses/GovernorPreventLateQuorumHarness.sol create mode 100644 certora/scripts/verifyGovernorPreventLateQuorum.sh create mode 100644 certora/specs/GovernorPreventLateQuorum.spec diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 3957b54f3..32adf065f 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,6 +1,6 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol ---- access/AccessControl.sol 2022-03-02 09:14:55.000000000 -0800 -+++ access/AccessControl.sol 2022-04-08 17:31:22.000000000 -0700 +--- access/AccessControl.sol 2022-05-06 13:44:28.000000000 -0700 ++++ access/AccessControl.sol 2022-05-09 09:49:26.000000000 -0700 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -10,9 +10,21 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol _checkRole(role, _msgSender()); } +diff -ruN governance/Governor.sol governance/Governor.sol +--- governance/Governor.sol 2022-05-09 09:11:10.000000000 -0700 ++++ governance/Governor.sol 2022-05-09 09:49:26.000000000 -0700 +@@ -42,7 +42,7 @@ + + string private _name; + +- mapping(uint256 => ProposalCore) private _proposals; ++ mapping(uint256 => ProposalCore) internal _proposals; + + // This queue keeps track of the governor operating on itself. Calls to functions protected by the + // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol ---- governance/TimelockController.sol 2022-03-02 09:14:55.000000000 -0800 -+++ governance/TimelockController.sol 2022-04-08 17:31:22.000000000 -0700 +--- governance/TimelockController.sol 2022-05-06 13:44:28.000000000 -0700 ++++ governance/TimelockController.sol 2022-05-09 09:49:26.000000000 -0700 @@ -24,10 +24,10 @@ bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); @@ -32,9 +44,23 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol } -} +} +diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol +--- governance/extensions/GovernorPreventLateQuorum.sol 2022-05-09 09:11:01.000000000 -0700 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-09 09:49:26.000000000 -0700 +@@ -21,8 +21,8 @@ + using SafeCast for uint256; + using Timers for Timers.BlockNumber; + +- uint64 private _voteExtension; +- mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines; ++ uint64 internal _voteExtension; ++ mapping(uint256 => Timers.BlockNumber) internal _extendedDeadlines; + + /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. + event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol ---- governance/utils/Votes.sol 2022-03-02 09:14:55.000000000 -0800 -+++ governance/utils/Votes.sol 2022-04-08 17:44:19.000000000 -0700 +--- governance/utils/Votes.sol 2022-05-06 13:44:28.000000000 -0700 ++++ governance/utils/Votes.sol 2022-05-09 09:49:26.000000000 -0700 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -108,8 +134,8 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public } diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol ---- token/ERC1155/ERC1155.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC1155/ERC1155.sol 2022-04-08 17:31:22.000000000 -0700 +--- token/ERC1155/ERC1155.sol 2022-05-06 13:44:28.000000000 -0700 ++++ token/ERC1155/ERC1155.sol 2022-05-09 09:49:26.000000000 -0700 @@ -268,7 +268,7 @@ uint256 id, uint256 amount, @@ -165,8 +191,8 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol ---- token/ERC20/ERC20.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/ERC20.sol 2022-04-08 17:31:22.000000000 -0700 +--- token/ERC20/ERC20.sol 2022-05-06 13:44:28.000000000 -0700 ++++ token/ERC20/ERC20.sol 2022-05-09 09:49:26.000000000 -0700 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -186,8 +212,8 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol /** diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol ---- token/ERC20/extensions/ERC20FlashMint.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-04-08 17:31:22.000000000 -0700 +--- token/ERC20/extensions/ERC20FlashMint.sol 2022-05-06 13:44:28.000000000 -0700 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-09 09:49:26.000000000 -0700 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -202,8 +228,8 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 * @dev Performs a flash loan. New tokens are minted and sent to the * `receiver`, who is required to implement the {IERC3156FlashBorrower} diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol ---- token/ERC20/extensions/ERC20Votes.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-04-08 17:31:22.000000000 -0700 +--- token/ERC20/extensions/ERC20Votes.sol 2022-05-06 13:43:21.000000000 -0700 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-05-09 09:49:26.000000000 -0700 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -335,8 +361,8 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote return a + b; } diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol ---- token/ERC20/extensions/ERC20Wrapper.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-04-08 17:31:22.000000000 -0700 +--- token/ERC20/extensions/ERC20Wrapper.sol 2022-05-06 13:44:28.000000000 -0700 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-09 09:49:26.000000000 -0700 @@ -44,7 +44,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -347,8 +373,8 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr _mint(account, value); return value; diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol ---- token/ERC721/extensions/draft-ERC721Votes.sol 2022-03-02 09:14:55.000000000 -0800 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-04-08 17:31:22.000000000 -0700 +--- token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-06 13:44:28.000000000 -0700 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-09 09:49:26.000000000 -0700 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. diff --git a/certora/harnesses/GovernorPreventLateQuorumHarness.sol b/certora/harnesses/GovernorPreventLateQuorumHarness.sol new file mode 100644 index 000000000..e13bb62e5 --- /dev/null +++ b/certora/harnesses/GovernorPreventLateQuorumHarness.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../munged/governance/extensions/GovernorPreventLateQuorum.sol"; +import "../munged/governance/Governor.sol"; +import "../munged/governance/extensions/GovernorCountingSimple.sol"; +import "../munged/governance/extensions/GovernorVotes.sol"; +import "../munged/governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../munged/governance/extensions/GovernorTimelockControl.sol"; +import "../munged/token/ERC20/extensions/ERC20Votes.sol"; + +contract GovernorPreventLateQuorumHarness is Governor, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl, GovernorPreventLateQuorum { + using SafeCast for uint256; + using Timers for Timers.BlockNumber; + constructor(IVotes _token, TimelockController _timelock, uint64 initialVoteExtension, uint256 quorumNumeratorValue) + Governor("Harness") + GovernorVotes(_token) + GovernorVotesQuorumFraction(quorumNumeratorValue) + GovernorTimelockControl(_timelock) + GovernorPreventLateQuorum(initialVoteExtension) + {} + + // variable added to check when _castVote is called + uint256 public latestCastVoteCall; + + // Harness from GovernorPreventLateQuorum // + + function getVoteExtension() public view returns(uint64) { + return _voteExtension; + } + + function getExtendedDeadlineIsUnset(uint256 id) public view returns(bool) { + return _extendedDeadlines[id].isUnset(); + } + + function getExtendedDeadline(uint256 id) public view returns(uint64) { + return _extendedDeadlines[id].getDeadline(); + } + + // Harness from GovernorCountingSimple // + + function quorumReached(uint256 proposalId) public view returns(bool) { + return _quorumReached(proposalId); + } + + // Harness from Governor // + + function isExecuted(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].executed; + } + + function isCanceled(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].canceled; + } + + // The following functions are overrides required by Solidity added by Certora. // + + function proposalDeadline(uint256 proposalId) public view virtual override(Governor, GovernorPreventLateQuorum, IGovernor) returns (uint256) { + return super.proposalDeadline(proposalId); + } + + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason, + bytes memory params + ) internal virtual override(Governor, GovernorPreventLateQuorum) returns (uint256) { + latestCastVoteCall = block.number; + return super._castVote(proposalId, account, support, reason, params); + } + + function castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason, + bytes memory params + ) public returns(uint256) { + return _castVote(proposalId, account, support, reason, params); + } + + function lateQuorumVoteExtension() public view virtual override returns (uint64) { + return super.lateQuorumVoteExtension(); + } + + function setLateQuorumVoteExtension(uint64 newVoteExtension) public virtual override onlyGovernance { + super.setLateQuorumVoteExtension(newVoteExtension); + } + + // The following functions are overrides required by Solidity added by OZ Wizard. // + + function votingDelay() public pure override returns (uint256) { + return 1; // 1 block + } + + function votingPeriod() public pure override returns (uint256) { + return 45818; // 1 week + } + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function state(uint256 proposalId) + public + view + override(Governor, GovernorTimelockControl) + returns (ProposalState) + { + return super.state(proposalId); + } + + function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) + public + override(Governor, IGovernor) + returns (uint256) + { + return super.propose(targets, values, calldatas, description); + } + + function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockControl) + { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockControl) + returns (uint256) + { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() + internal + view + override(Governor, GovernorTimelockControl) + returns (address) + { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} \ No newline at end of file diff --git a/certora/munged/governance/Governor.sol b/certora/munged/governance/Governor.sol index 8907d9468..75e634c3d 100644 --- a/certora/munged/governance/Governor.sol +++ b/certora/munged/governance/Governor.sol @@ -42,7 +42,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { string private _name; - mapping(uint256 => ProposalCore) private _proposals; + mapping(uint256 => ProposalCore) internal _proposals; // This queue keeps track of the governor operating on itself. Calls to functions protected by the // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff --git a/certora/munged/governance/extensions/GovernorPreventLateQuorum.sol b/certora/munged/governance/extensions/GovernorPreventLateQuorum.sol index 5b58f6032..453d03b68 100644 --- a/certora/munged/governance/extensions/GovernorPreventLateQuorum.sol +++ b/certora/munged/governance/extensions/GovernorPreventLateQuorum.sol @@ -21,8 +21,8 @@ abstract contract GovernorPreventLateQuorum is Governor { using SafeCast for uint256; using Timers for Timers.BlockNumber; - uint64 private _voteExtension; - mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines; + uint64 internal _voteExtension; + mapping(uint256 => Timers.BlockNumber) internal _extendedDeadlines; /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff --git a/certora/scripts/verifyGovernorPreventLateQuorum.sh b/certora/scripts/verifyGovernorPreventLateQuorum.sh new file mode 100644 index 000000000..ed102853c --- /dev/null +++ b/certora/scripts/verifyGovernorPreventLateQuorum.sh @@ -0,0 +1,9 @@ +certoraRun \ + certora/harnesses/ERC721VotesHarness.sol certora/harnesses/GovernorPreventLateQuorumHarness.sol \ + --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorPreventLateQuorum.spec \ + --solc solc \ + --optimistic_loop \ + --loop_iter 3 \ + --cloud \ + --rule $1 \ + --msg "GovernorPreventLateQuorum $1" \ No newline at end of file diff --git a/certora/specs/GovernorPreventLateQuorum.spec b/certora/specs/GovernorPreventLateQuorum.spec new file mode 100644 index 000000000..a9b13bd45 --- /dev/null +++ b/certora/specs/GovernorPreventLateQuorum.spec @@ -0,0 +1,115 @@ +////////////////////////////////////////////////////////////////////////////// +///////////////////// Governor.sol base definitions ////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +using ERC721VotesHarness as erc20votes + +methods { + proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart + proposalDeadline(uint256) returns uint256 envfree // matches proposalVoteEnd + hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree + isExecuted(uint256) returns bool envfree + isCanceled(uint256) returns bool envfree + execute(address[], uint256[], bytes[], bytes32) returns uint256 + hasVoted(uint256, address) returns bool + castVote(uint256, uint8) returns uint256 + updateQuorumNumerator(uint256) + queue(address[], uint256[], bytes[], bytes32) returns uint256 + quorumNumerator() returns uint256 envfree + quorumDenominator() returns uint256 envfree + votingPeriod() returns uint256 envfree + lateQuorumVoteExtension() returns uint64 envfree + + // harness + getExtendedDeadlineIsUnset(uint256) returns bool envfree + getExtendedDeadline(uint256) returns uint64 envfree + quorumReached(uint256) returns bool envfree + latestCastVoteCall() returns uint256 envfree // more robust check than f.selector == _castVote(...).selector + + // function summarization + proposalThreshold() returns uint256 envfree + + getVotes(address, uint256) returns uint256 => DISPATCHER(true) + + getPastTotalSupply(uint256 t) returns uint256 => PER_CALLEE_CONSTANT + getPastVotes(address a, uint256 t) returns uint256 => PER_CALLEE_CONSTANT +} + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// Definitions ///////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +// create definition for extended +definition deadlineCanBeExtended(uint256 id) returns bool = + getExtendedDeadlineIsUnset(id) && + getExtendedDeadline(id) == 0 && + !quorumReached(id); + +definition deadlineHasBeenExtended(uint256 id) returns bool = + !getExtendedDeadlineIsUnset(id) && + getExtendedDeadline(id) > 0 && + quorumReached(id); + + + +// RULE deadline can only be extended once + // 1. if deadline changes then we have state transition from deadlineCanBeExtended to deadlineHasBeenExtended +rule deadlineChangeEffects(method f) filtered {f -> !f.isView /* bottleneck, restrict for faster testing && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } { + env e; calldataarg args; uint256 id; + + require (latestCastVoteCall() < e.block.number); + require (quorumNumerator() <= quorumDenominator()); + require deadlineCanBeExtended(id); + require (proposalDeadline(id) < e.block.number + && proposalDeadline(id) >= proposalSnapshot(id) + votingPeriod() + && proposalSnapshot(id) > e.block.number); + + uint256 deadlineBefore = proposalDeadline(id); + f(e, args); + uint256 deadlineAfter = proposalDeadline(id); + + assert(deadlineAfter > deadlineBefore => latestCastVoteCall() == e.block.number && deadlineHasBeenExtended(id)); +} + + // 2. cant unextend +rule deadlineCantBeUnextended(method f) filtered {f -> !f.isView /* && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } { + env e; calldataarg args; uint256 id; + require(deadlineHasBeenExtended(id)); + f(e, args); + assert(deadlineHasBeenExtended(id)); +} + + // 3. extended => can't change deadline + //@note if deadline changed, then it wasnt extended and castvote was called +rule canExtendDeadlineOnce(method f) filtered {f -> !f.isView /* && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } { + env e; calldataarg args; + uint256 id; + require(deadlineHasBeenExtended(id)); // stays true + require (proposalDeadline(id) < e.block.number + && proposalDeadline(id) >= proposalSnapshot(id) + votingPeriod() + && proposalSnapshot(id) > e.block.number); + uint256 deadlineBefore = proposalDeadline(id); + f(e, args); + uint256 deadlineAfter = proposalDeadline(id); + assert(deadlineBefore == deadlineAfter, "deadline can not be extended twice"); +} + + +// RULE deadline can only extended if quorum reached w/ <= timeOfExtension left to vote +// 3 rules + // 1. voting increases total votes + // 2. number of votes > quorum => quorum reached + // 3. deadline can only extended if quorum reached w/ <= timeOfExtension left to vote +rule deadlineCanOnlyBeExtenededIfQuorumReached() { + env e; method f; calldataarg args; + uint256 id; + require(getExtendedDeadlineIsUnset(id)); + f(e, args); + assert(false); +} + +// RULE extendedDeadline is used iff quorum is reached w/ <= extensionTime left to vote + +// RULE extendedDeadlineField is set iff quroum is reached + +// RULE if the deadline/extendedDeadline has not been reached, you can still vote (base) \ No newline at end of file From c45f34adc89f58f9ee671f2754ec0db8a13cb929 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Wed, 11 May 2022 12:28:28 -0700 Subject: [PATCH 182/254] fix typos --- certora/specs/GovernorPreventLateQuorum.spec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/certora/specs/GovernorPreventLateQuorum.spec b/certora/specs/GovernorPreventLateQuorum.spec index a9b13bd45..885c665ed 100644 --- a/certora/specs/GovernorPreventLateQuorum.spec +++ b/certora/specs/GovernorPreventLateQuorum.spec @@ -2,7 +2,7 @@ ///////////////////// Governor.sol base definitions ////////////////////////// ////////////////////////////////////////////////////////////////////////////// -using ERC721VotesHarness as erc20votes +using ERC721VotesHarness as erc721votes methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart @@ -60,9 +60,9 @@ rule deadlineChangeEffects(method f) filtered {f -> !f.isView /* bottleneck, res require (latestCastVoteCall() < e.block.number); require (quorumNumerator() <= quorumDenominator()); require deadlineCanBeExtended(id); - require (proposalDeadline(id) < e.block.number + require (proposalDeadline(id) > e.block.number && proposalDeadline(id) >= proposalSnapshot(id) + votingPeriod() - && proposalSnapshot(id) > e.block.number); + && proposalSnapshot(id) < e.block.number); uint256 deadlineBefore = proposalDeadline(id); f(e, args); @@ -85,9 +85,9 @@ rule canExtendDeadlineOnce(method f) filtered {f -> !f.isView /* && f.selector ! env e; calldataarg args; uint256 id; require(deadlineHasBeenExtended(id)); // stays true - require (proposalDeadline(id) < e.block.number + require (proposalDeadline(id) > e.block.number && proposalDeadline(id) >= proposalSnapshot(id) + votingPeriod() - && proposalSnapshot(id) > e.block.number); + && proposalSnapshot(id) < e.block.number); uint256 deadlineBefore = proposalDeadline(id); f(e, args); uint256 deadlineAfter = proposalDeadline(id); From 793b88efd8eedda38dbe6e04d20342a1277ade08 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Mon, 16 May 2022 14:26:31 -0700 Subject: [PATCH 183/254] finalize fist 3 rules; fix old governor spec --- .vscode/settings.json | 3 + certora/applyHarness.patch | 50 +++++-- .../GovernorPreventLateQuorumHarness.sol | 32 ++++- .../munged/governance/TimelockController.sol | 7 +- certora/munged/utils/Address.sol | 1 + .../verifyGovernorPreventLateQuorum.sh | 15 ++- certora/specs/GovernorBase.spec | 23 ++-- certora/specs/GovernorCountingSimple.spec | 3 +- certora/specs/GovernorPreventLateQuorum.spec | 123 +++++++++--------- 9 files changed, 165 insertions(+), 92 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..5120aebf5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "solidity.compileUsingRemoteVersion": "v0.8.2+commit.661d1103" +} \ No newline at end of file diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 32adf065f..1403850fb 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,6 +1,6 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol --- access/AccessControl.sol 2022-05-06 13:44:28.000000000 -0700 -+++ access/AccessControl.sol 2022-05-09 09:49:26.000000000 -0700 ++++ access/AccessControl.sol 2022-05-11 11:17:20.000000000 -0700 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -12,7 +12,7 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol diff -ruN governance/Governor.sol governance/Governor.sol --- governance/Governor.sol 2022-05-09 09:11:10.000000000 -0700 -+++ governance/Governor.sol 2022-05-09 09:49:26.000000000 -0700 ++++ governance/Governor.sol 2022-05-11 11:17:20.000000000 -0700 @@ -42,7 +42,7 @@ string private _name; @@ -24,7 +24,7 @@ diff -ruN governance/Governor.sol governance/Governor.sol // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol --- governance/TimelockController.sol 2022-05-06 13:44:28.000000000 -0700 -+++ governance/TimelockController.sol 2022-05-09 09:49:26.000000000 -0700 ++++ governance/TimelockController.sol 2022-05-12 19:13:19.000000000 -0700 @@ -24,10 +24,10 @@ bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); @@ -38,7 +38,22 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol /** * @dev Emitted when a call is scheduled as part of operation `id`. -@@ -353,4 +353,4 @@ +@@ -332,10 +332,11 @@ + uint256 value, + bytes calldata data + ) private { +- (bool success, ) = target.call{value: value}(data); +- require(success, "TimelockController: underlying transaction reverted"); ++ return; // can't deal with external calls ++ // (bool success, ) = target.call{value: value}(data); ++ // require(success, "TimelockController: underlying transaction reverted"); + +- emit CallExecuted(id, index, target, value, data); ++ // emit CallExecuted(id, index, target, value, data); + } + + /** +@@ -353,4 +354,4 @@ emit MinDelayChange(_minDelay, newDelay); _minDelay = newDelay; } @@ -46,7 +61,7 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol +} diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol --- governance/extensions/GovernorPreventLateQuorum.sol 2022-05-09 09:11:01.000000000 -0700 -+++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-09 09:49:26.000000000 -0700 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-11 11:17:20.000000000 -0700 @@ -21,8 +21,8 @@ using SafeCast for uint256; using Timers for Timers.BlockNumber; @@ -60,7 +75,7 @@ diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensi event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol --- governance/utils/Votes.sol 2022-05-06 13:44:28.000000000 -0700 -+++ governance/utils/Votes.sol 2022-05-09 09:49:26.000000000 -0700 ++++ governance/utils/Votes.sol 2022-05-11 11:17:20.000000000 -0700 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -135,7 +150,7 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol } diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol --- token/ERC1155/ERC1155.sol 2022-05-06 13:44:28.000000000 -0700 -+++ token/ERC1155/ERC1155.sol 2022-05-09 09:49:26.000000000 -0700 ++++ token/ERC1155/ERC1155.sol 2022-05-11 11:17:20.000000000 -0700 @@ -268,7 +268,7 @@ uint256 id, uint256 amount, @@ -192,7 +207,7 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol --- token/ERC20/ERC20.sol 2022-05-06 13:44:28.000000000 -0700 -+++ token/ERC20/ERC20.sol 2022-05-09 09:49:26.000000000 -0700 ++++ token/ERC20/ERC20.sol 2022-05-11 11:17:20.000000000 -0700 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -213,7 +228,7 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol /** diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol --- token/ERC20/extensions/ERC20FlashMint.sol 2022-05-06 13:44:28.000000000 -0700 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-09 09:49:26.000000000 -0700 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-11 11:17:20.000000000 -0700 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -229,7 +244,7 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 * `receiver`, who is required to implement the {IERC3156FlashBorrower} diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol --- token/ERC20/extensions/ERC20Votes.sol 2022-05-06 13:43:21.000000000 -0700 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-05-09 09:49:26.000000000 -0700 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-05-11 11:17:20.000000000 -0700 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -362,7 +377,7 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote } diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol --- token/ERC20/extensions/ERC20Wrapper.sol 2022-05-06 13:44:28.000000000 -0700 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-09 09:49:26.000000000 -0700 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-11 11:17:20.000000000 -0700 @@ -44,7 +44,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -374,7 +389,7 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr return value; diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol --- token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-06 13:44:28.000000000 -0700 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-09 09:49:26.000000000 -0700 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-11 11:17:20.000000000 -0700 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. @@ -384,3 +399,14 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ return balanceOf(account); } } +diff -ruN utils/Address.sol utils/Address.sol +--- utils/Address.sol 2022-05-06 13:43:21.000000000 -0700 ++++ utils/Address.sol 2022-05-15 10:58:38.000000000 -0700 +@@ -131,6 +131,7 @@ + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { ++ return ""; + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + diff --git a/certora/harnesses/GovernorPreventLateQuorumHarness.sol b/certora/harnesses/GovernorPreventLateQuorumHarness.sol index e13bb62e5..cd9133e59 100644 --- a/certora/harnesses/GovernorPreventLateQuorumHarness.sol +++ b/certora/harnesses/GovernorPreventLateQuorumHarness.sol @@ -20,6 +20,8 @@ contract GovernorPreventLateQuorumHarness is Governor, GovernorCountingSimple, G GovernorPreventLateQuorum(initialVoteExtension) {} + mapping(uint256 => uint256) public ghost_sum_vote_power_by_id; + // variable added to check when _castVote is called uint256 public latestCastVoteCall; @@ -39,12 +41,29 @@ contract GovernorPreventLateQuorumHarness is Governor, GovernorCountingSimple, G // Harness from GovernorCountingSimple // - function quorumReached(uint256 proposalId) public view returns(bool) { + function quorumReached(uint256 proposalId) public view returns(bool) { return _quorumReached(proposalId); } + function voteSucceeded(uint256 proposalId) public view returns(bool) { + return _voteSucceeded(proposalId); + } + + function countVote(uint256 proposalId, + address account, + uint8 support, + uint256 weight, + bytes memory // params + ) public view { + return _countVote(proposalId,account,support,weight,""); + } + // Harness from Governor // + function getExecutor() public view returns (address){ + return _executor(); + } + function isExecuted(uint256 proposalId) public view returns (bool) { return _proposals[proposalId].executed; } @@ -66,10 +85,16 @@ contract GovernorPreventLateQuorumHarness is Governor, GovernorCountingSimple, G string memory reason, bytes memory params ) internal virtual override(Governor, GovernorPreventLateQuorum) returns (uint256) { + // flag for when _castVote is called latestCastVoteCall = block.number; - return super._castVote(proposalId, account, support, reason, params); - } + // added to run GovernorCountingSimple.spec + uint256 deltaWeight = super._castVote(proposalId, account, support, reason, params); + ghost_sum_vote_power_by_id[proposalId] += deltaWeight; + + return deltaWeight; + } + /* function castVote( uint256 proposalId, address account, @@ -79,6 +104,7 @@ contract GovernorPreventLateQuorumHarness is Governor, GovernorCountingSimple, G ) public returns(uint256) { return _castVote(proposalId, account, support, reason, params); } + */ function lateQuorumVoteExtension() public view virtual override returns (uint64) { return super.lateQuorumVoteExtension(); diff --git a/certora/munged/governance/TimelockController.sol b/certora/munged/governance/TimelockController.sol index da38df8f3..b8278ff4d 100644 --- a/certora/munged/governance/TimelockController.sol +++ b/certora/munged/governance/TimelockController.sol @@ -332,10 +332,11 @@ contract TimelockController is AccessControl { uint256 value, bytes calldata data ) private { - (bool success, ) = target.call{value: value}(data); - require(success, "TimelockController: underlying transaction reverted"); + return; // haven't dealt with external calls yet + // (bool success, ) = target.call{value: value}(data); + // require(success, "TimelockController: underlying transaction reverted"); - emit CallExecuted(id, index, target, value, data); + // emit CallExecuted(id, index, target, value, data); } /** diff --git a/certora/munged/utils/Address.sol b/certora/munged/utils/Address.sol index daea7f31e..c4da2a463 100644 --- a/certora/munged/utils/Address.sol +++ b/certora/munged/utils/Address.sol @@ -131,6 +131,7 @@ library Address { uint256 value, string memory errorMessage ) internal returns (bytes memory) { + return ""; // external calls havoc require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); diff --git a/certora/scripts/verifyGovernorPreventLateQuorum.sh b/certora/scripts/verifyGovernorPreventLateQuorum.sh index ed102853c..cbea7b40e 100644 --- a/certora/scripts/verifyGovernorPreventLateQuorum.sh +++ b/certora/scripts/verifyGovernorPreventLateQuorum.sh @@ -1,9 +1,14 @@ certoraRun \ - certora/harnesses/ERC721VotesHarness.sol certora/harnesses/GovernorPreventLateQuorumHarness.sol \ - --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorPreventLateQuorum.spec \ + certora/harnesses/ERC721VotesHarness.sol certora/munged/governance/TimelockController.sol certora/harnesses/GovernorPreventLateQuorumHarness.sol \ + --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorCountingSimple.spec \ --solc solc \ --optimistic_loop \ - --loop_iter 3 \ - --cloud \ + --loop_iter 1 \ + --staging \ + --rule_sanity advanced \ + --send_only \ --rule $1 \ - --msg "GovernorPreventLateQuorum $1" \ No newline at end of file + --msg "$1" \ + + + diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 031b2680e..3936e77c4 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -17,16 +17,16 @@ methods { queue(address[], uint256[], bytes[], bytes32) returns uint256 // internal functions made public in harness: - _quorumReached(uint256) returns bool - _voteSucceeded(uint256) returns bool envfree + quorumReached(uint256) returns bool + voteSucceeded(uint256) returns bool envfree // function summarization proposalThreshold() returns uint256 envfree getVotes(address, uint256) returns uint256 => DISPATCHER(true) - getPastTotalSupply(uint256 t) returns uint256 => PER_CALLEE_CONSTANT - getPastVotes(address a, uint256 t) returns uint256 => PER_CALLEE_CONSTANT + getPastTotalSupply(uint256) returns uint256 => DISPATCHER(true) + getPastVotes(address, uint256) returns uint256 => DISPATCHER(true) //scheduleBatch(address[],uint256[],bytes[],bytes32,bytes32,uint256) => DISPATCHER(true) //executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) @@ -47,7 +47,7 @@ definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0 function helperFunctionsWithRevert(uint256 proposalId, method f, env e) { address[] targets; uint256[] values; bytes[] calldatas; string reason; bytes32 descriptionHash; - uint8 support; uint8 v; bytes32 r; bytes32 s; + uint8 support; uint8 v; bytes32 r; bytes32 s; bytes params; if (f.selector == propose(address[], uint256[], bytes[], string).selector) { uint256 result = propose@withrevert(e, targets, values, calldatas, reason); require(result == proposalId); @@ -62,10 +62,15 @@ function helperFunctionsWithRevert(uint256 proposalId, method f, env e) { castVoteBySig@withrevert(e, proposalId, support, v, r, s); } else if (f.selector == queue(address[], uint256[], bytes[], bytes32).selector) { queue@withrevert(e, targets, values, calldatas, descriptionHash); - } else { + } else if (f.selector == castVoteWithReasonAndParamsBySig(uint256,uint8,string,bytes,uint8,bytes32,bytes32).selector) { + castVoteWithReasonAndParamsBySig@withrevert(e, proposalId, support, reason, params, v, r, s); + } else if (f.selector == castVoteWithReasonAndParams(uint256,uint8,string,bytes).selector) { + castVoteWithReasonAndParams@withrevert(e, proposalId, support, reason, params); + } else { calldataarg args; f@withrevert(e, args); } + } /* @@ -152,8 +157,8 @@ invariant noBothExecutedAndCanceled(uint256 pId) */ rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f){ bool isExecutedBefore = isExecuted(pId); - bool quorumReachedBefore = _quorumReached(e, pId); - bool voteSucceededBefore = _voteSucceeded(pId); + bool quorumReachedBefore = quorumReached(e, pId); + bool voteSucceededBefore = voteSucceeded(pId); calldataarg args; f(e, args); @@ -283,6 +288,7 @@ rule allFunctionsRevertIfExecuted(method f) filtered { f -> && f.selector != queue(address[],uint256[],bytes[],bytes32).selector && f.selector != relay(address,uint256,bytes).selector && f.selector != 0xb9a61961 // __acceptAdmin() + && f.selector != setLateQuorumVoteExtension(uint64).selector } { env e; calldataarg args; uint256 pId; @@ -305,6 +311,7 @@ rule allFunctionsRevertIfCanceled(method f) filtered { && f.selector != queue(address[],uint256[],bytes[],bytes32).selector && f.selector != relay(address,uint256,bytes).selector && f.selector != 0xb9a61961 // __acceptAdmin() + && f.selector != setLateQuorumVoteExtension(uint64).selector } { env e; calldataarg args; uint256 pId; diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index c5dcecddf..3660e70b4 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -7,7 +7,6 @@ methods { proposalVotes(uint256) returns (uint256, uint256, uint256) envfree quorumNumerator() returns uint256 - _executor() returns address getExecutor() returns address @@ -184,7 +183,7 @@ rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) { bool hasVotedAfter = hasVoted(e, pId, acc); - assert (!hasVotedBefore && hasVotedAfter) => againstBefore <= againstAfter || forBefore <= forAfter || abstainBefore <= abstainAfter, "no correlation"; + assert (!hasVotedBefore && hasVotedAfter) => againstBefore <= againstAfter && forBefore <= forAfter && abstainBefore <= abstainAfter, "no correlation"; } diff --git a/certora/specs/GovernorPreventLateQuorum.spec b/certora/specs/GovernorPreventLateQuorum.spec index 885c665ed..6ef3745c3 100644 --- a/certora/specs/GovernorPreventLateQuorum.spec +++ b/certora/specs/GovernorPreventLateQuorum.spec @@ -3,6 +3,7 @@ ////////////////////////////////////////////////////////////////////////////// using ERC721VotesHarness as erc721votes +using ERC20VotesHarness as erc20votes methods { proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart @@ -19,97 +20,101 @@ methods { quorumDenominator() returns uint256 envfree votingPeriod() returns uint256 envfree lateQuorumVoteExtension() returns uint64 envfree + propose(address[], uint256[], bytes[], string) // harness getExtendedDeadlineIsUnset(uint256) returns bool envfree getExtendedDeadline(uint256) returns uint64 envfree quorumReached(uint256) returns bool envfree + voteSucceeded(uint256) returns bool envfree + quorum(uint256) returns uint256 latestCastVoteCall() returns uint256 envfree // more robust check than f.selector == _castVote(...).selector // function summarization proposalThreshold() returns uint256 envfree + // erc20votes dispatch getVotes(address, uint256) returns uint256 => DISPATCHER(true) - - getPastTotalSupply(uint256 t) returns uint256 => PER_CALLEE_CONSTANT - getPastVotes(address a, uint256 t) returns uint256 => PER_CALLEE_CONSTANT + // erc721votes/Votes dispatch + getPastTotalSupply(uint256) returns uint256 => DISPATCHER(true) + getPastVotes(address, uint256) returns uint256 => DISPATCHER(true) + // timelock dispatch + getMinDelay() returns uint256 => DISPATCHER(true) + + hashOperationBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) + executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => CONSTANT + scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256) => CONSTANT } ////////////////////////////////////////////////////////////////////////////// //////////////////////////////// Definitions ///////////////////////////////// ////////////////////////////////////////////////////////////////////////////// +// where can invariants help? +// can I replace definitions with invariants? + // create definition for extended -definition deadlineCanBeExtended(uint256 id) returns bool = - getExtendedDeadlineIsUnset(id) && - getExtendedDeadline(id) == 0 && - !quorumReached(id); +definition deadlineCanBeExtended(uint256 pId) returns bool = + getExtendedDeadlineIsUnset(pId) && + !quorumReached(pId); -definition deadlineHasBeenExtended(uint256 id) returns bool = - !getExtendedDeadlineIsUnset(id) && - getExtendedDeadline(id) > 0 && - quorumReached(id); +definition deadlineHasBeenExtended(uint256 pId) returns bool = + !getExtendedDeadlineIsUnset(pId) && + quorumReached(pId); +definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0; +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// Rules ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// -// RULE deadline can only be extended once - // 1. if deadline changes then we have state transition from deadlineCanBeExtended to deadlineHasBeenExtended -rule deadlineChangeEffects(method f) filtered {f -> !f.isView /* bottleneck, restrict for faster testing && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } { - env e; calldataarg args; uint256 id; +// RULE deadline can only be extended only once PASSING but vacuous + // 1. if deadline changes then we have state transition to deadlineHasBeenExtended RULE PASSING; ADV SANITY PASSING +rule deadlineChangeEffects(method f) + filtered { + f -> !f.isView + } { + env e; calldataarg args; uint256 pId; - require (latestCastVoteCall() < e.block.number); - require (quorumNumerator() <= quorumDenominator()); - require deadlineCanBeExtended(id); - require (proposalDeadline(id) > e.block.number - && proposalDeadline(id) >= proposalSnapshot(id) + votingPeriod() - && proposalSnapshot(id) < e.block.number); + require (proposalCreated(pId)); - uint256 deadlineBefore = proposalDeadline(id); + uint256 deadlineBefore = proposalDeadline(pId); f(e, args); - uint256 deadlineAfter = proposalDeadline(id); + uint256 deadlineAfter = proposalDeadline(pId); - assert(deadlineAfter > deadlineBefore => latestCastVoteCall() == e.block.number && deadlineHasBeenExtended(id)); + assert(deadlineAfter > deadlineBefore => latestCastVoteCall() == e.block.number && deadlineHasBeenExtended(pId)); } - // 2. cant unextend -rule deadlineCantBeUnextended(method f) filtered {f -> !f.isView /* && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } { - env e; calldataarg args; uint256 id; - require(deadlineHasBeenExtended(id)); + // 2. cant unextend RULE PASSING*; ADV SANITY PASSING +rule deadlineCantBeUnextended(method f) + filtered { + f -> !f.isView + && f.selector != updateQuorumNumerator(uint256).selector // * fails for this function + } { + env e; calldataarg args; uint256 pId; + + require(deadlineHasBeenExtended(pId)); + require(proposalCreated(pId)); + f(e, args); - assert(deadlineHasBeenExtended(id)); + + assert(deadlineHasBeenExtended(pId)); } - // 3. extended => can't change deadline + // 3. extended => can't change deadline RULE PASSING; ADV SANITY PASSING //@note if deadline changed, then it wasnt extended and castvote was called -rule canExtendDeadlineOnce(method f) filtered {f -> !f.isView /* && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } { - env e; calldataarg args; - uint256 id; - require(deadlineHasBeenExtended(id)); // stays true - require (proposalDeadline(id) > e.block.number - && proposalDeadline(id) >= proposalSnapshot(id) + votingPeriod() - && proposalSnapshot(id) < e.block.number); - uint256 deadlineBefore = proposalDeadline(id); +rule canExtendDeadlineOnce(method f) + filtered { + f -> !f.isView + } { + env e; calldataarg args; uint256 pId; + + require(deadlineHasBeenExtended(pId)); + require(proposalCreated(pId)); + + uint256 deadlineBefore = proposalDeadline(pId); f(e, args); - uint256 deadlineAfter = proposalDeadline(id); + uint256 deadlineAfter = proposalDeadline(pId); + assert(deadlineBefore == deadlineAfter, "deadline can not be extended twice"); } - - -// RULE deadline can only extended if quorum reached w/ <= timeOfExtension left to vote -// 3 rules - // 1. voting increases total votes - // 2. number of votes > quorum => quorum reached - // 3. deadline can only extended if quorum reached w/ <= timeOfExtension left to vote -rule deadlineCanOnlyBeExtenededIfQuorumReached() { - env e; method f; calldataarg args; - uint256 id; - require(getExtendedDeadlineIsUnset(id)); - f(e, args); - assert(false); -} - -// RULE extendedDeadline is used iff quorum is reached w/ <= extensionTime left to vote - -// RULE extendedDeadlineField is set iff quroum is reached - -// RULE if the deadline/extendedDeadline has not been reached, you can still vote (base) \ No newline at end of file From fa89068f2bdd28763d7984ad19e537688935cb88 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Thu, 19 May 2022 15:37:58 -0700 Subject: [PATCH 184/254] 8.5/10 rules finished --- certora/applyHarness.patch | 8 +- .../GovernorPreventLateQuorumHarness.sol | 13 +- .../verifyGovernorPreventLateQuorum.sh | 6 +- certora/specs/GovernorBase.spec | 1 - certora/specs/GovernorCountingSimple.spec | 3 +- certora/specs/GovernorPreventLateQuorum.spec | 272 ++++++++++++++---- 6 files changed, 231 insertions(+), 72 deletions(-) diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 1403850fb..a960f6e40 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -24,7 +24,7 @@ diff -ruN governance/Governor.sol governance/Governor.sol // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol --- governance/TimelockController.sol 2022-05-06 13:44:28.000000000 -0700 -+++ governance/TimelockController.sol 2022-05-12 19:13:19.000000000 -0700 ++++ governance/TimelockController.sol 2022-05-15 14:10:49.000000000 -0700 @@ -24,10 +24,10 @@ bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); @@ -44,7 +44,7 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol ) private { - (bool success, ) = target.call{value: value}(data); - require(success, "TimelockController: underlying transaction reverted"); -+ return; // can't deal with external calls ++ return; // haven't dealt with external calls yet + // (bool success, ) = target.call{value: value}(data); + // require(success, "TimelockController: underlying transaction reverted"); @@ -401,12 +401,12 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ } diff -ruN utils/Address.sol utils/Address.sol --- utils/Address.sol 2022-05-06 13:43:21.000000000 -0700 -+++ utils/Address.sol 2022-05-15 10:58:38.000000000 -0700 ++++ utils/Address.sol 2022-05-16 14:24:44.000000000 -0700 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage ) internal returns (bytes memory) { -+ return ""; ++ return ""; // external calls havoc require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); diff --git a/certora/harnesses/GovernorPreventLateQuorumHarness.sol b/certora/harnesses/GovernorPreventLateQuorumHarness.sol index cd9133e59..773d2ef54 100644 --- a/certora/harnesses/GovernorPreventLateQuorumHarness.sol +++ b/certora/harnesses/GovernorPreventLateQuorumHarness.sol @@ -35,6 +35,10 @@ contract GovernorPreventLateQuorumHarness is Governor, GovernorCountingSimple, G return _extendedDeadlines[id].isUnset(); } + function getExtendedDeadlineIsStarted(uint256 id) public view returns(bool) { + return _extendedDeadlines[id].isStarted(); + } + function getExtendedDeadline(uint256 id) public view returns(uint64) { return _extendedDeadlines[id].getDeadline(); } @@ -49,15 +53,6 @@ contract GovernorPreventLateQuorumHarness is Governor, GovernorCountingSimple, G return _voteSucceeded(proposalId); } - function countVote(uint256 proposalId, - address account, - uint8 support, - uint256 weight, - bytes memory // params - ) public view { - return _countVote(proposalId,account,support,weight,""); - } - // Harness from Governor // function getExecutor() public view returns (address){ diff --git a/certora/scripts/verifyGovernorPreventLateQuorum.sh b/certora/scripts/verifyGovernorPreventLateQuorum.sh index cbea7b40e..72b7fa651 100644 --- a/certora/scripts/verifyGovernorPreventLateQuorum.sh +++ b/certora/scripts/verifyGovernorPreventLateQuorum.sh @@ -1,12 +1,12 @@ certoraRun \ certora/harnesses/ERC721VotesHarness.sol certora/munged/governance/TimelockController.sol certora/harnesses/GovernorPreventLateQuorumHarness.sol \ - --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorCountingSimple.spec \ + --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorPreventLateQuorum.spec \ --solc solc \ --optimistic_loop \ - --loop_iter 1 \ + --disable_auto_cache_key_gen \ --staging \ - --rule_sanity advanced \ --send_only \ + --loop_iter 1 \ --rule $1 \ --msg "$1" \ diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 3936e77c4..83c47dde1 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -70,7 +70,6 @@ function helperFunctionsWithRevert(uint256 proposalId, method f, env e) { calldataarg args; f@withrevert(e, args); } - } /* diff --git a/certora/specs/GovernorCountingSimple.spec b/certora/specs/GovernorCountingSimple.spec index 3660e70b4..20b5d4ede 100644 --- a/certora/specs/GovernorCountingSimple.spec +++ b/certora/specs/GovernorCountingSimple.spec @@ -183,7 +183,8 @@ rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) { bool hasVotedAfter = hasVoted(e, pId, acc); - assert (!hasVotedBefore && hasVotedAfter) => againstBefore <= againstAfter && forBefore <= forAfter && abstainBefore <= abstainAfter, "no correlation"; + // want all vote categories to not decrease and at least one category to increase + assert (!hasVotedBefore && hasVotedAfter) => (againstBefore <= againstAfter && forBefore <= forAfter && abstainBefore <= abstainAfter), "no correlation: some category decreased"; } diff --git a/certora/specs/GovernorPreventLateQuorum.spec b/certora/specs/GovernorPreventLateQuorum.spec index 6ef3745c3..bcadf76da 100644 --- a/certora/specs/GovernorPreventLateQuorum.spec +++ b/certora/specs/GovernorPreventLateQuorum.spec @@ -1,22 +1,8 @@ -////////////////////////////////////////////////////////////////////////////// -///////////////////// Governor.sol base definitions ////////////////////////// -////////////////////////////////////////////////////////////////////////////// +import "GovernorCountingSimple.spec" using ERC721VotesHarness as erc721votes -using ERC20VotesHarness as erc20votes methods { - proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart - proposalDeadline(uint256) returns uint256 envfree // matches proposalVoteEnd - hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree - isExecuted(uint256) returns bool envfree - isCanceled(uint256) returns bool envfree - execute(address[], uint256[], bytes[], bytes32) returns uint256 - hasVoted(uint256, address) returns bool - castVote(uint256, uint8) returns uint256 - updateQuorumNumerator(uint256) - queue(address[], uint256[], bytes[], bytes32) returns uint256 - quorumNumerator() returns uint256 envfree quorumDenominator() returns uint256 envfree votingPeriod() returns uint256 envfree lateQuorumVoteExtension() returns uint64 envfree @@ -24,20 +10,12 @@ methods { // harness getExtendedDeadlineIsUnset(uint256) returns bool envfree + getExtendedDeadlineIsStarted(uint256) returns bool envfree getExtendedDeadline(uint256) returns uint64 envfree - quorumReached(uint256) returns bool envfree - voteSucceeded(uint256) returns bool envfree - quorum(uint256) returns uint256 - latestCastVoteCall() returns uint256 envfree // more robust check than f.selector == _castVote(...).selector + + // more robust check than f.selector == _castVote(...).selector + latestCastVoteCall() returns uint256 envfree - // function summarization - proposalThreshold() returns uint256 envfree - - // erc20votes dispatch - getVotes(address, uint256) returns uint256 => DISPATCHER(true) - // erc721votes/Votes dispatch - getPastTotalSupply(uint256) returns uint256 => DISPATCHER(true) - getPastVotes(address, uint256) returns uint256 => DISPATCHER(true) // timelock dispatch getMinDelay() returns uint256 => DISPATCHER(true) @@ -46,34 +24,72 @@ methods { scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256) => CONSTANT } + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Helper Functions /////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +function helperFunctionsWithRevertOnlyCastVote(uint256 proposalId, method f, env e) { + string reason; uint8 support; uint8 v; bytes32 r; bytes32 s; bytes params; + if (f.selector == castVote(uint256, uint8).selector) { + castVote@withrevert(e, proposalId, support); + } else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) { + castVoteWithReason@withrevert(e, proposalId, support, reason); + } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) { + castVoteBySig@withrevert(e, proposalId, support, v, r, s); + } else if (f.selector == castVoteWithReasonAndParamsBySig(uint256,uint8,string,bytes,uint8,bytes32,bytes32).selector) { + castVoteWithReasonAndParamsBySig@withrevert(e, proposalId, support, reason, params, v, r, s); + } else if (f.selector == castVoteWithReasonAndParams(uint256,uint8,string,bytes).selector) { + castVoteWithReasonAndParams@withrevert(e, proposalId, support, reason, params); + } else { + calldataarg args; + f@withrevert(e, args); + } +} + + ////////////////////////////////////////////////////////////////////////////// //////////////////////////////// Definitions ///////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -// where can invariants help? -// can I replace definitions with invariants? +// proposal deadline can be extended (but isn't) +definition deadlineExtendable(env e, uint256 pId) returns bool = + getExtendedDeadlineIsUnset(pId) + && !quorumReached(e, pId); -// create definition for extended -definition deadlineCanBeExtended(uint256 pId) returns bool = - getExtendedDeadlineIsUnset(pId) && - !quorumReached(pId); +// proposal deadline has been extended +definition deadlineExtended(env e, uint256 pId) returns bool = + getExtendedDeadlineIsStarted(pId) + && quorumReached(e, pId); -definition deadlineHasBeenExtended(uint256 pId) returns bool = - !getExtendedDeadlineIsUnset(pId) && - quorumReached(pId); -definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0; +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// Invariants ///////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/* + * I1: A propsal must be in state deadlineExtendable or deadlineExtended. + * --INVARIANT PASSING // fails for updateQuorumNumerator + * --ADVANCED SANITY PASSING // can't sanity test failing rules, not sure how it works for invariants + */ +invariant proposalInOneState(env e, uint256 pId) + deadlineExtendable(e, pId) || deadlineExtended(e, pId) + { preserved { require proposalCreated(pId); } } + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// Rules ///////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -// RULE deadline can only be extended only once PASSING but vacuous - // 1. if deadline changes then we have state transition to deadlineHasBeenExtended RULE PASSING; ADV SANITY PASSING -rule deadlineChangeEffects(method f) - filtered { - f -> !f.isView - } { +///////////////////////////// first set of rules ///////////////////////////// + +// R1 and R2 are assumed in R3, so we prove them first +/* + * R1: If deadline increases then we are in deadlineExtended state and castVote was called. + * RULE PASSING + * ADVANCED SANITY PASSING + */ +rule deadlineChangeEffects(method f) filtered {f -> !f.isView} { env e; calldataarg args; uint256 pId; require (proposalCreated(pId)); @@ -82,10 +98,15 @@ rule deadlineChangeEffects(method f) f(e, args); uint256 deadlineAfter = proposalDeadline(pId); - assert(deadlineAfter > deadlineBefore => latestCastVoteCall() == e.block.number && deadlineHasBeenExtended(pId)); + assert(deadlineAfter > deadlineBefore => latestCastVoteCall() == e.block.number && deadlineExtended(e, pId)); } - // 2. cant unextend RULE PASSING*; ADV SANITY PASSING + +/* + * R2: A proposal can't leave deadlineExtended state. + * RULE PASSING* + * ADVANCED SANITY PASSING + */ rule deadlineCantBeUnextended(method f) filtered { f -> !f.isView @@ -93,23 +114,24 @@ rule deadlineCantBeUnextended(method f) } { env e; calldataarg args; uint256 pId; - require(deadlineHasBeenExtended(pId)); + require(deadlineExtended(e, pId)); require(proposalCreated(pId)); f(e, args); - assert(deadlineHasBeenExtended(pId)); + assert(deadlineExtended(e, pId)); } - // 3. extended => can't change deadline RULE PASSING; ADV SANITY PASSING - //@note if deadline changed, then it wasnt extended and castvote was called -rule canExtendDeadlineOnce(method f) - filtered { - f -> !f.isView - } { + +/* + * R3: A proposal's deadline can't change in deadlineExtended state. + * RULE PASSING* + * ADVANCED SANITY PASSING + */ +rule canExtendDeadlineOnce(method f) filtered {f -> !f.isView} { env e; calldataarg args; uint256 pId; - require(deadlineHasBeenExtended(pId)); + require(deadlineExtended(e, pId)); require(proposalCreated(pId)); uint256 deadlineBefore = proposalDeadline(pId); @@ -118,3 +140,145 @@ rule canExtendDeadlineOnce(method f) assert(deadlineBefore == deadlineAfter, "deadline can not be extended twice"); } + + +//////////////////////////// second set of rules //////////////////////////// + +// HIGH LEVEL RULE R6: deadline can only extended if quorum reached w/ <= timeOfExtension left to vote +// I1, R4 and R5 are assumed in R6 so we prove them first + +/* + * R4: A change in hasVoted must be correlated with an increasing of the vote supports, i.e. casting a vote increases the total number of votes. + * RULE PASSING + * ADVANCED SANITY PASSING + */ +rule hasVotedCorrelationNonzero(uint256 pId, method f, env e) filtered {f -> !f.isView} { + address acc = e.msg.sender; + + require(getVotes(e, acc, proposalSnapshot(pId)) > 0); // assuming voter has non-zero voting power + + uint256 againstBefore = votesAgainst(); + uint256 forBefore = votesFor(); + uint256 abstainBefore = votesAbstain(); + + bool hasVotedBefore = hasVoted(e, pId, acc); + + helperFunctionsWithRevertOnlyCastVote(pId, f, e); + + uint256 againstAfter = votesAgainst(); + uint256 forAfter = votesFor(); + uint256 abstainAfter = votesAbstain(); + + bool hasVotedAfter = hasVoted(e, pId, acc); + + // want all vote categories to not decrease and at least one category to increase + assert + (!hasVotedBefore && hasVotedAfter) => + (againstBefore <= againstAfter && forBefore <= forAfter && abstainBefore <= abstainAfter), + "no correlation: some category decreased"; // currently vacous but keeping for CI tests + assert + (!hasVotedBefore && hasVotedAfter) => + (againstBefore < againstAfter || forBefore < forAfter || abstainBefore < abstainAfter), + "no correlation: no category increased"; +} + + +/* + * R5: An against vote does not make a proposal reach quorum. + * RULE PASSING + * --ADVANCED SANITY PASSING vacuous but keeping + */ +rule againstVotesDontCount(method f) filtered {f -> !f.isView} { + env e; calldataarg args; uint256 pId; + address acc = e.msg.sender; + + bool quorumBefore = quorumReached(e, pId); + uint256 againstBefore = votesAgainst(); + + f(e, args); + + bool quorumAfter = quorumReached(e, pId); + uint256 againstAfter = votesAgainst(); + + assert (againstBefore < againstAfter) => quorumBefore == quorumAfter, "quorum reached with against vote"; +} + +/* + * R6: Deadline can only be extended from a `deadlineExtendible` state with quorum being reached with <= `lateQuorumVoteExtension` time left to vote + * RULE PASSING + * ADVANCED SANITY PASSING + */ +rule deadlineExtenededIfQuorumReached(method f) filtered {f -> !f.isView} { + env e; calldataarg args; uint256 pId; + + // need invariant that proves that a propsal must be in state deadlineExtendable or deadlineExtended + require(deadlineExtended(e, pId) || deadlineExtendable(e, pId)); + require(proposalCreated(pId)); + + bool wasDeadlineExtendable = deadlineExtendable(e, pId); + uint64 extension = lateQuorumVoteExtension(); + uint256 deadlineBefore = proposalDeadline(pId); + f(e, args); + uint256 deadlineAfter = proposalDeadline(pId); + + assert(deadlineAfter > deadlineBefore => wasDeadlineExtendable, "deadline was not extendable"); + assert(deadlineAfter > deadlineBefore => deadlineBefore - e.block.number <= extension, "deadline extension should not be used"); +} + +/* + * R7: `extendedDeadlineField` is set iff `_castVote` is called and quroum is reached. + * RULE PASSING + * ADVANCED SANITY PASSING + */ +rule extendedDeadlineValueSetIfQuorumReached(method f) filtered {f -> !f.isView} { + env e; calldataarg args; uint256 pId; + require(deadlineExtended(e, pId) || deadlineExtendable(e, pId)); + + bool extendedBefore = deadlineExtended(e, pId); + f(e, args); + bool extendedAfter = deadlineExtended(e, pId); + uint256 extDeadline = getExtendedDeadline(pId); + + assert( + !extendedBefore && extendedAfter + => extDeadline == e.block.number + lateQuorumVoteExtension(), + "extended deadline was not set" + ); +} + +/* +* R8: If the deadline for a proposal has not been reached, users can still vote. +* --RULE PASSING +* --ADVANCED SANITY PASSING +*/ +rule canVote(method f) filtered {f -> !f.isView} { + env e; calldataarg args; uint256 pId; + address acc = e.msg.sender; + uint256 deadline = proposalDeadline(pId); + bool votedBefore = hasVoted(e, pId, acc); + + require(proposalCreated(pId)); + require(deadline >= e.block.number); + // last error? + helperFunctionsWithRevertOnlyCastVote(pId, f, e); + bool votedAfter = hasVoted(e, pId, acc); + + assert !votedBefore && votedAfter => deadline >= e.block.number; +} + +/* + * R9: Deadline can never be reduced. + * RULE PASSING + * ADVANCED SANITY PASSING + */ +rule deadlineNeverReduced(method f) filtered {f -> !f.isView} { + env e; calldataarg args; uint256 pId; + require(proposalCreated(pId)); + + uint256 deadlineBefore = proposalDeadline(pId); + f(e, args); + uint256 deadlineAfter = proposalDeadline(pId); + + assert(deadlineAfter >= deadlineBefore); +} + From 46cb74f3cf3efcad3f3f1244ff316d786ca0ee3b Mon Sep 17 00:00:00 2001 From: Michael George Date: Fri, 20 May 2022 16:27:05 -0400 Subject: [PATCH 185/254] held tokens should exist passing --- .../ERC1155/ERC1155SupplyHarness.sol | 6 ++ certora/specs/ERC1155Supply.spec | 57 ++++++++++++++++--- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol b/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol index 53b73dc6c..7bea70487 100644 --- a/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol +++ b/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol @@ -4,5 +4,11 @@ contract ERC1155SupplyHarness is ERC1155Supply { constructor(string memory uri_) ERC1155(uri_) {} + + // workaround for problem caused by `exists` being a CVL keyword + function exists_wrapper(uint256 id) public view virtual returns (bool) { + return exists(id); + } + } diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index e6769afc0..b5c84cc3f 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -2,6 +2,7 @@ methods { totalSupply(uint256) returns uint256 envfree balanceOf(address, uint256) returns uint256 envfree + exists_wrapper(uint256) returns bool envfree } /// given two different token ids, if totalSupply for one changes, then @@ -29,15 +30,56 @@ filtered { "methods must not change the total supply of more than one token"; } -invariant sum_user_token_balances_vs_totalSupply(uint256 id, address user1, address user2) - balanceOf(user1, id) + balanceOf(user2, id) <= totalSupply(id) -{ preserved { - require user1 != user2; - //for every address not user1 or user2, balance is < user1 and < user2 - require forall address user3. (user3 != user1 && user3 != user2) => balanceOf(user3, id) < balanceOf(user1, id) && balanceOf(user3, id) < balanceOf(user2, id); - } +/******************************************************************************/ + +ghost mapping(uint256 => mathint) sumOfBalances; + +hook Sstore _balances[KEY uint256 token][KEY address user] uint256 newValue (uint256 oldValue) STORAGE { + sumOfBalances[token] = sumOfBalances[token] + newValue - oldValue; } +// status: not passing, because mint and burn are the same as transferring to/from +// the 0 address. +invariant total_supply_is_sum_of_balances(uint256 token) + sumOfBalances[token] == totalSupply(token) + balanceOf(0, token) + +rule total_supply_is_sum_of_balances_as_rule { + uint256 token; + + require sumOfBalances[token] == totalSupply(token) + balanceOf(0, token); + + mathint sum_before = sumOfBalances[token]; + + method f; calldataarg arg; env e; + f(e, arg); + + mathint sum_after = sumOfBalances[token]; + + assert sumOfBalances[token] == totalSupply(token) + balanceOf(0, token); +} + +/******************************************************************************/ + +// if a user has a token, then the token should exist + +/* +hook Sload _balances[...] { + require balance <= totalSupply +} +*/ + +rule held_tokens_should_exist { + address user; uint256 token; + + // This assumption is safe because of total_supply_is_sum_of_balances + require balanceOf(user, token) <= totalSupply(token); + + assert balanceOf(user, token) > 0 => exists_wrapper(token), + "if a user's balance for a token is positive, the token must exist"; +} + +/******************************************************************************/ + rule sanity { method f; env e; calldataarg args; @@ -45,4 +87,3 @@ rule sanity { assert false; } - From 7a2b502b9c1fd15b4c1acb98540a53ade862469d Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 23 May 2022 09:32:50 -0700 Subject: [PATCH 186/254] Added harness for ERC1155Burnable --- certora/harnesses/ERC1155/ERC1155BurnableHarness.sol | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 certora/harnesses/ERC1155/ERC1155BurnableHarness.sol diff --git a/certora/harnesses/ERC1155/ERC1155BurnableHarness.sol b/certora/harnesses/ERC1155/ERC1155BurnableHarness.sol new file mode 100644 index 000000000..5f7fbd706 --- /dev/null +++ b/certora/harnesses/ERC1155/ERC1155BurnableHarness.sol @@ -0,0 +1,8 @@ +import "../../munged/token/ERC1155/extensions/ERC1155Burnable.sol"; + +contract ERC1155BurnableHarness is ERC1155Burnable { + constructor(string memory uri_) + ERC1155(uri_) + {} +} + From 39f29ec3fd95a715d06373cad6dd264cf4675eaa Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 23 May 2022 09:52:25 -0700 Subject: [PATCH 187/254] Added spec for ERC1155Burnable with rule sanity --- certora/specs/ERC1155Burnable.spec | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 certora/specs/ERC1155Burnable.spec diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec new file mode 100644 index 000000000..3e67c1919 --- /dev/null +++ b/certora/specs/ERC1155Burnable.spec @@ -0,0 +1,7 @@ +rule sanity { + method f; env e; calldataarg args; + + f(e, args); + + assert false; +} \ No newline at end of file From 36327ce8c51913c8a8ae94994750248f6cf91c22 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 23 May 2022 10:00:09 -0700 Subject: [PATCH 188/254] Added script to verify ERC1155Burnable --- certora/scripts/verifyERC1155Burnable.sh | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 certora/scripts/verifyERC1155Burnable.sh diff --git a/certora/scripts/verifyERC1155Burnable.sh b/certora/scripts/verifyERC1155Burnable.sh new file mode 100644 index 000000000..22187e5dd --- /dev/null +++ b/certora/scripts/verifyERC1155Burnable.sh @@ -0,0 +1,8 @@ +certoraRun \ + certora/harnesses/ERC1155/ERC1155BurnableHarness.sol \ + --verify ERC1155BurnableHarness:certora/specs/ERC1155Burnable.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --cloud \ + --msg "ERC1155 Burnable verification" \ No newline at end of file From 0321f3805467f0942d3b6cdb5a030faf6072ce83 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 24 May 2022 16:11:18 -0700 Subject: [PATCH 189/254] Added remaining rules, unclear if rules_sanity is passing --- certora/specs/ERC1155Pausable.spec | 112 ++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/certora/specs/ERC1155Pausable.spec b/certora/specs/ERC1155Pausable.spec index 70f738896..81fe40029 100644 --- a/certora/specs/ERC1155Pausable.spec +++ b/certora/specs/ERC1155Pausable.spec @@ -1,9 +1,117 @@ +methods { + balanceOf(address, uint256) returns uint256 envfree + paused() returns bool envfree +} +/// When a contract is in a paused state, the token balance for a given user and +/// token must not change. +rule balancesUnchangedWhenPaused() { + address user; uint256 token; + uint256 balanceBefore = balanceOf(user, token); + + require paused(); + + method f; calldataarg arg; env e; + f(e, arg); + + uint256 balanceAfter = balanceOf(user, token); + + assert balanceBefore == balanceAfter, + "Token balance for a user must not change in a paused contract"; +} + +/// When a contract is in a paused state, transfer methods must revert. +rule transferMethodsRevertWhenPaused (method f) +filtered { + f -> f.selector == safeTransferFrom(address,address,uint256,uint256,bytes).selector + || f.selector == safeBatchTransferFrom(address,address,uint256[],uint256[],bytes).selector +} +{ + require paused(); + + env e; calldataarg args; + f@withrevert(e, args); + + assert lastReverted, + "Transfer methods must revert in a paused contract"; +} + + +/// Calling pause must pause an unpaused contract. +/// When a contract is in an unpaused state, calling pause() must pause. +rule pauseMethodPausesContract { + require !paused(); + + env e; + pause(e); + + assert paused(), + "Calling pause must pause an unpaused contract"; +} + +/// When a contract is in a paused state, calling unpause() must unpause. +rule unpauseMethodUnpausesContract { + require paused(); + + env e; + unpause(e); + + assert !paused(), + "Calling unpause must unpause a paused contract"; +} + +/// When a contract is in a paused state, calling pause() must revert. +rule cannotPauseWhilePaused { + require paused(); + + env e; + pause@withrevert(e); + + assert lastReverted, + "A call to pause when already paused must revert"; +} + +/// When a contract is in an unpaused state, calling unpause() must revert. +rule cannotUnpauseWhileUnpaused { + require !paused(); + + env e; + unpause@withrevert(e); + + assert lastReverted, + "A call to unpause when already unpaused must revert"; +} + +/// When a contract is in a paused state, functions with the whenNotPaused +/// modifier must revert. +rule whenNotPausedModifierCausesRevertIfPaused { + require paused(); + + env e; calldataarg args; + onlyWhenNotPausedMethod@withrevert(e, args); + + assert lastReverted, + "Functions with the whenNotPaused modifier must revert if the contract is paused"; +} + +/// When a contract is in an unpaused state, functions with the whenPaused +/// modifier must revert. +rule whenPausedModifierCausesRevertIfUnpaused { + require !paused(); + + env e; calldataarg args; + onlyWhenPausedMethod@withrevert(e, args); + + assert lastReverted, + "Functions with the whenPaused modifier must revert if the contract is not paused"; +} + +/// This rule should always fail. rule sanity { method f; env e; calldataarg args; f(e, args); - assert false; + assert false, + "This rule should always fail"; } - From 94eba740161e64c5c9f4574c16422ad7eb1f273d Mon Sep 17 00:00:00 2001 From: Michael George Date: Wed, 25 May 2022 16:06:34 -0400 Subject: [PATCH 190/254] removed some patch cruft --- certora/applyHarness.patch | 1992 +----------------------------------- 1 file changed, 12 insertions(+), 1980 deletions(-) diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 9dbbc8e43..833d550dc 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,6 +1,6 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol --- access/AccessControl.sol 2022-05-25 15:36:58.849363940 -0400 -+++ access/AccessControl.sol 2022-05-25 15:37:51.040083904 -0400 ++++ access/AccessControl.sol 2022-05-25 16:04:57.725255723 -0400 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -12,7 +12,7 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol --- governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 15:37:51.044083806 -0400 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 16:04:57.725255723 -0400 @@ -21,8 +21,8 @@ using SafeCast for uint256; using Timers for Timers.BlockNumber; @@ -26,7 +26,7 @@ diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensi event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/Governor.sol governance/Governor.sol --- governance/Governor.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/Governor.sol 2022-05-25 15:37:51.040083904 -0400 ++++ governance/Governor.sol 2022-05-25 16:04:57.725255723 -0400 @@ -44,7 +44,7 @@ string private _name; @@ -36,609 +36,9 @@ diff -ruN governance/Governor.sol governance/Governor.sol // This queue keeps track of the governor operating on itself. Calls to functions protected by the // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, -diff -ruN governance/Governor.sol.orig governance/Governor.sol.orig ---- governance/Governor.sol.orig 1969-12-31 19:00:00.000000000 -0500 -+++ governance/Governor.sol.orig 2022-05-25 15:37:51.036084000 -0400 -@@ -0,0 +1,596 @@ -+// SPDX-License-Identifier: MIT -+// OpenZeppelin Contracts (last updated v4.6.0) (governance/Governor.sol) -+ -+pragma solidity ^0.8.0; -+ -+import "../token/ERC721/IERC721Receiver.sol"; -+import "../token/ERC1155/IERC1155Receiver.sol"; -+import "../utils/cryptography/ECDSA.sol"; -+import "../utils/cryptography/draft-EIP712.sol"; -+import "../utils/introspection/ERC165.sol"; -+import "../utils/math/SafeCast.sol"; -+import "../utils/structs/DoubleEndedQueue.sol"; -+import "../utils/Address.sol"; -+import "../utils/Context.sol"; -+import "../utils/Timers.sol"; -+import "./IGovernor.sol"; -+ -+/** -+ * @dev Core of the governance system, designed to be extended though various modules. -+ * -+ * This contract is abstract and requires several function to be implemented in various modules: -+ * -+ * - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote} -+ * - A voting module must implement {_getVotes} -+ * - Additionanly, the {votingPeriod} must also be implemented -+ * -+ * _Available since v4.3._ -+ */ -+abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receiver, IERC1155Receiver { -+ using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; -+ using SafeCast for uint256; -+ using Timers for Timers.BlockNumber; -+ -+ bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); -+ bytes32 public constant EXTENDED_BALLOT_TYPEHASH = -+ keccak256("ExtendedBallot(uint256 proposalId,uint8 support,string reason,bytes params)"); -+ -+ struct ProposalCore { -+ Timers.BlockNumber voteStart; -+ Timers.BlockNumber voteEnd; -+ bool executed; -+ bool canceled; -+ } -+ -+ string private _name; -+ -+ mapping(uint256 => ProposalCore) private _proposals; -+ -+ // This queue keeps track of the governor operating on itself. Calls to functions protected by the -+ // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, -+ // consumed by the {onlyGovernance} modifier and eventually reset in {_afterExecute}. This ensures that the -+ // execution of {onlyGovernance} protected calls can only be achieved through successful proposals. -+ DoubleEndedQueue.Bytes32Deque private _governanceCall; -+ -+ /** -+ * @dev Restricts a function so it can only be executed through governance proposals. For example, governance -+ * parameter setters in {GovernorSettings} are protected using this modifier. -+ * -+ * The governance executing address may be different from the Governor's own address, for example it could be a -+ * timelock. This can be customized by modules by overriding {_executor}. The executor is only able to invoke these -+ * functions during the execution of the governor's {execute} function, and not under any other circumstances. Thus, -+ * for example, additional timelock proposers are not able to change governance parameters without going through the -+ * governance protocol (since v4.6). -+ */ -+ modifier onlyGovernance() { -+ require(_msgSender() == _executor(), "Governor: onlyGovernance"); -+ if (_executor() != address(this)) { -+ bytes32 msgDataHash = keccak256(_msgData()); -+ // loop until popping the expected operation - throw if deque is empty (operation not authorized) -+ while (_governanceCall.popFront() != msgDataHash) {} -+ } -+ _; -+ } -+ -+ /** -+ * @dev Sets the value for {name} and {version} -+ */ -+ constructor(string memory name_) EIP712(name_, version()) { -+ _name = name_; -+ } -+ -+ /** -+ * @dev Function to receive ETH that will be handled by the governor (disabled if executor is a third party contract) -+ */ -+ receive() external payable virtual { -+ require(_executor() == address(this)); -+ } -+ -+ /** -+ * @dev See {IERC165-supportsInterface}. -+ */ -+ function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { -+ // In addition to the current interfaceId, also support previous version of the interfaceId that did not -+ // include the castVoteWithReasonAndParams() function as standard -+ return -+ interfaceId == -+ (type(IGovernor).interfaceId ^ -+ this.castVoteWithReasonAndParams.selector ^ -+ this.castVoteWithReasonAndParamsBySig.selector ^ -+ this.getVotesWithParams.selector) || -+ interfaceId == type(IGovernor).interfaceId || -+ interfaceId == type(IERC1155Receiver).interfaceId || -+ super.supportsInterface(interfaceId); -+ } -+ -+ /** -+ * @dev See {IGovernor-name}. -+ */ -+ function name() public view virtual override returns (string memory) { -+ return _name; -+ } -+ -+ /** -+ * @dev See {IGovernor-version}. -+ */ -+ function version() public view virtual override returns (string memory) { -+ return "1"; -+ } -+ -+ /** -+ * @dev See {IGovernor-hashProposal}. -+ * -+ * The proposal id is produced by hashing the RLC encoded `targets` array, the `values` array, the `calldatas` array -+ * and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id -+ * can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in -+ * advance, before the proposal is submitted. -+ * -+ * Note that the chainId and the governor address are not part of the proposal id computation. Consequently, the -+ * same proposal (with same operation and same description) will have the same id if submitted on multiple governors -+ * across multiple networks. This also means that in order to execute the same operation twice (on the same -+ * governor) the proposer will have to change the description in order to avoid proposal id conflicts. -+ */ -+ function hashProposal( -+ address[] memory targets, -+ uint256[] memory values, -+ bytes[] memory calldatas, -+ bytes32 descriptionHash -+ ) public pure virtual override returns (uint256) { -+ return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash))); -+ } -+ -+ /** -+ * @dev See {IGovernor-state}. -+ */ -+ function state(uint256 proposalId) public view virtual override returns (ProposalState) { -+ ProposalCore storage proposal = _proposals[proposalId]; -+ -+ if (proposal.executed) { -+ return ProposalState.Executed; -+ } -+ -+ if (proposal.canceled) { -+ return ProposalState.Canceled; -+ } -+ -+ uint256 snapshot = proposalSnapshot(proposalId); -+ -+ if (snapshot == 0) { -+ revert("Governor: unknown proposal id"); -+ } -+ -+ if (snapshot >= block.number) { -+ return ProposalState.Pending; -+ } -+ -+ uint256 deadline = proposalDeadline(proposalId); -+ -+ if (deadline >= block.number) { -+ return ProposalState.Active; -+ } -+ -+ if (_quorumReached(proposalId) && _voteSucceeded(proposalId)) { -+ return ProposalState.Succeeded; -+ } else { -+ return ProposalState.Defeated; -+ } -+ } -+ -+ /** -+ * @dev See {IGovernor-proposalSnapshot}. -+ */ -+ function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) { -+ return _proposals[proposalId].voteStart.getDeadline(); -+ } -+ -+ /** -+ * @dev See {IGovernor-proposalDeadline}. -+ */ -+ function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) { -+ return _proposals[proposalId].voteEnd.getDeadline(); -+ } -+ -+ /** -+ * @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_. -+ */ -+ function proposalThreshold() public view virtual returns (uint256) { -+ return 0; -+ } -+ -+ /** -+ * @dev Amount of votes already cast passes the threshold limit. -+ */ -+ function _quorumReached(uint256 proposalId) internal view virtual returns (bool); -+ -+ /** -+ * @dev Is the proposal successful or not. -+ */ -+ function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool); -+ -+ /** -+ * @dev Get the voting weight of `account` at a specific `blockNumber`, for a vote as described by `params`. -+ */ -+ function _getVotes( -+ address account, -+ uint256 blockNumber, -+ bytes memory params -+ ) internal view virtual returns (uint256); -+ -+ /** -+ * @dev Register a vote for `proposalId` by `account` with a given `support`, voting `weight` and voting `params`. -+ * -+ * Note: Support is generic and can represent various things depending on the voting system used. -+ */ -+ function _countVote( -+ uint256 proposalId, -+ address account, -+ uint8 support, -+ uint256 weight, -+ bytes memory params -+ ) internal virtual; -+ -+ /** -+ * @dev Default additional encoded parameters used by castVote methods that don't include them -+ * -+ * Note: Should be overridden by specific implementations to use an appropriate value, the -+ * meaning of the additional params, in the context of that implementation -+ */ -+ function _defaultParams() internal view virtual returns (bytes memory) { -+ return ""; -+ } -+ -+ /** -+ * @dev See {IGovernor-propose}. -+ */ -+ function propose( -+ address[] memory targets, -+ uint256[] memory values, -+ bytes[] memory calldatas, -+ string memory description -+ ) public virtual override returns (uint256) { -+ require( -+ getVotes(_msgSender(), block.number - 1) >= proposalThreshold(), -+ "Governor: proposer votes below proposal threshold" -+ ); -+ -+ uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description))); -+ -+ require(targets.length == values.length, "Governor: invalid proposal length"); -+ require(targets.length == calldatas.length, "Governor: invalid proposal length"); -+ require(targets.length > 0, "Governor: empty proposal"); -+ -+ ProposalCore storage proposal = _proposals[proposalId]; -+ require(proposal.voteStart.isUnset(), "Governor: proposal already exists"); -+ -+ uint64 snapshot = block.number.toUint64() + votingDelay().toUint64(); -+ uint64 deadline = snapshot + votingPeriod().toUint64(); -+ -+ proposal.voteStart.setDeadline(snapshot); -+ proposal.voteEnd.setDeadline(deadline); -+ -+ emit ProposalCreated( -+ proposalId, -+ _msgSender(), -+ targets, -+ values, -+ new string[](targets.length), -+ calldatas, -+ snapshot, -+ deadline, -+ description -+ ); -+ -+ return proposalId; -+ } -+ -+ /** -+ * @dev See {IGovernor-execute}. -+ */ -+ function execute( -+ address[] memory targets, -+ uint256[] memory values, -+ bytes[] memory calldatas, -+ bytes32 descriptionHash -+ ) public payable virtual override returns (uint256) { -+ uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); -+ -+ ProposalState status = state(proposalId); -+ require( -+ status == ProposalState.Succeeded || status == ProposalState.Queued, -+ "Governor: proposal not successful" -+ ); -+ _proposals[proposalId].executed = true; -+ -+ emit ProposalExecuted(proposalId); -+ -+ _beforeExecute(proposalId, targets, values, calldatas, descriptionHash); -+ _execute(proposalId, targets, values, calldatas, descriptionHash); -+ _afterExecute(proposalId, targets, values, calldatas, descriptionHash); -+ -+ return proposalId; -+ } -+ -+ /** -+ * @dev Internal execution mechanism. Can be overridden to implement different execution mechanism -+ */ -+ function _execute( -+ uint256, /* proposalId */ -+ address[] memory targets, -+ uint256[] memory values, -+ bytes[] memory calldatas, -+ bytes32 /*descriptionHash*/ -+ ) internal virtual { -+ string memory errorMessage = "Governor: call reverted without message"; -+ for (uint256 i = 0; i < targets.length; ++i) { -+ (bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]); -+ Address.verifyCallResult(success, returndata, errorMessage); -+ } -+ } -+ -+ /** -+ * @dev Hook before execution is triggered. -+ */ -+ function _beforeExecute( -+ uint256, /* proposalId */ -+ address[] memory targets, -+ uint256[] memory, /* values */ -+ bytes[] memory calldatas, -+ bytes32 /*descriptionHash*/ -+ ) internal virtual { -+ if (_executor() != address(this)) { -+ for (uint256 i = 0; i < targets.length; ++i) { -+ if (targets[i] == address(this)) { -+ _governanceCall.pushBack(keccak256(calldatas[i])); -+ } -+ } -+ } -+ } -+ -+ /** -+ * @dev Hook after execution is triggered. -+ */ -+ function _afterExecute( -+ uint256, /* proposalId */ -+ address[] memory, /* targets */ -+ uint256[] memory, /* values */ -+ bytes[] memory, /* calldatas */ -+ bytes32 /*descriptionHash*/ -+ ) internal virtual { -+ if (_executor() != address(this)) { -+ if (!_governanceCall.empty()) { -+ _governanceCall.clear(); -+ } -+ } -+ } -+ -+ /** -+ * @dev Internal cancel mechanism: locks up the proposal timer, preventing it from being re-submitted. Marks it as -+ * canceled to allow distinguishing it from executed proposals. -+ * -+ * Emits a {IGovernor-ProposalCanceled} event. -+ */ -+ function _cancel( -+ address[] memory targets, -+ uint256[] memory values, -+ bytes[] memory calldatas, -+ bytes32 descriptionHash -+ ) internal virtual returns (uint256) { -+ uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); -+ ProposalState status = state(proposalId); -+ -+ require( -+ status != ProposalState.Canceled && status != ProposalState.Expired && status != ProposalState.Executed, -+ "Governor: proposal not active" -+ ); -+ _proposals[proposalId].canceled = true; -+ -+ emit ProposalCanceled(proposalId); -+ -+ return proposalId; -+ } -+ -+ /** -+ * @dev See {IGovernor-getVotes}. -+ */ -+ function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { -+ return _getVotes(account, blockNumber, _defaultParams()); -+ } -+ -+ /** -+ * @dev See {IGovernor-getVotesWithParams}. -+ */ -+ function getVotesWithParams( -+ address account, -+ uint256 blockNumber, -+ bytes memory params -+ ) public view virtual override returns (uint256) { -+ return _getVotes(account, blockNumber, params); -+ } -+ -+ /** -+ * @dev See {IGovernor-castVote}. -+ */ -+ function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { -+ address voter = _msgSender(); -+ return _castVote(proposalId, voter, support, ""); -+ } -+ -+ /** -+ * @dev See {IGovernor-castVoteWithReason}. -+ */ -+ function castVoteWithReason( -+ uint256 proposalId, -+ uint8 support, -+ string calldata reason -+ ) public virtual override returns (uint256) { -+ address voter = _msgSender(); -+ return _castVote(proposalId, voter, support, reason); -+ } -+ -+ /** -+ * @dev See {IGovernor-castVoteWithReasonAndParams}. -+ */ -+ function castVoteWithReasonAndParams( -+ uint256 proposalId, -+ uint8 support, -+ string calldata reason, -+ bytes memory params -+ ) public virtual override returns (uint256) { -+ address voter = _msgSender(); -+ return _castVote(proposalId, voter, support, reason, params); -+ } -+ -+ /** -+ * @dev See {IGovernor-castVoteBySig}. -+ */ -+ function castVoteBySig( -+ uint256 proposalId, -+ uint8 support, -+ uint8 v, -+ bytes32 r, -+ bytes32 s -+ ) public virtual override returns (uint256) { -+ address voter = ECDSA.recover( -+ _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))), -+ v, -+ r, -+ s -+ ); -+ return _castVote(proposalId, voter, support, ""); -+ } -+ -+ /** -+ * @dev See {IGovernor-castVoteWithReasonAndParamsBySig}. -+ */ -+ function castVoteWithReasonAndParamsBySig( -+ uint256 proposalId, -+ uint8 support, -+ string calldata reason, -+ bytes memory params, -+ uint8 v, -+ bytes32 r, -+ bytes32 s -+ ) public virtual override returns (uint256) { -+ address voter = ECDSA.recover( -+ _hashTypedDataV4( -+ keccak256( -+ abi.encode( -+ EXTENDED_BALLOT_TYPEHASH, -+ proposalId, -+ support, -+ keccak256(bytes(reason)), -+ keccak256(params) -+ ) -+ ) -+ ), -+ v, -+ r, -+ s -+ ); -+ -+ return _castVote(proposalId, voter, support, reason, params); -+ } -+ -+ /** -+ * @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve -+ * voting weight using {IGovernor-getVotes} and call the {_countVote} internal function. Uses the _defaultParams(). -+ * -+ * Emits a {IGovernor-VoteCast} event. -+ */ -+ function _castVote( -+ uint256 proposalId, -+ address account, -+ uint8 support, -+ string memory reason -+ ) internal virtual returns (uint256) { -+ return _castVote(proposalId, account, support, reason, _defaultParams()); -+ } -+ -+ /** -+ * @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve -+ * voting weight using {IGovernor-getVotes} and call the {_countVote} internal function. -+ * -+ * Emits a {IGovernor-VoteCast} event. -+ */ -+ function _castVote( -+ uint256 proposalId, -+ address account, -+ uint8 support, -+ string memory reason, -+ bytes memory params -+ ) internal virtual returns (uint256) { -+ ProposalCore storage proposal = _proposals[proposalId]; -+ require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active"); -+ -+ uint256 weight = _getVotes(account, proposal.voteStart.getDeadline(), params); -+ _countVote(proposalId, account, support, weight, params); -+ -+ if (params.length == 0) { -+ emit VoteCast(account, proposalId, support, weight, reason); -+ } else { -+ emit VoteCastWithParams(account, proposalId, support, weight, reason, params); -+ } -+ -+ return weight; -+ } -+ -+ /** -+ * @dev Relays a transaction or function call to an arbitrary target. In cases where the governance executor -+ * is some contract other than the governor itself, like when using a timelock, this function can be invoked -+ * in a governance proposal to recover tokens or Ether that was sent to the governor contract by mistake. -+ * Note that if the executor is simply the governor itself, use of `relay` is redundant. -+ */ -+ function relay( -+ address target, -+ uint256 value, -+ bytes calldata data -+ ) external virtual onlyGovernance { -+ Address.functionCallWithValue(target, data, value); -+ } -+ -+ /** -+ * @dev Address through which the governor executes action. Will be overloaded by module that execute actions -+ * through another contract such as a timelock. -+ */ -+ function _executor() internal view virtual returns (address) { -+ return address(this); -+ } -+ -+ /** -+ * @dev See {IERC721Receiver-onERC721Received}. -+ */ -+ function onERC721Received( -+ address, -+ address, -+ uint256, -+ bytes memory -+ ) public virtual override returns (bytes4) { -+ return this.onERC721Received.selector; -+ } -+ -+ /** -+ * @dev See {IERC1155Receiver-onERC1155Received}. -+ */ -+ function onERC1155Received( -+ address, -+ address, -+ uint256, -+ uint256, -+ bytes memory -+ ) public virtual override returns (bytes4) { -+ return this.onERC1155Received.selector; -+ } -+ -+ /** -+ * @dev See {IERC1155Receiver-onERC1155BatchReceived}. -+ */ -+ function onERC1155BatchReceived( -+ address, -+ address, -+ uint256[] memory, -+ uint256[] memory, -+ bytes memory -+ ) public virtual override returns (bytes4) { -+ return this.onERC1155BatchReceived.selector; -+ } -+} diff -ruN governance/TimelockController.sol governance/TimelockController.sol --- governance/TimelockController.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/TimelockController.sol 2022-05-25 15:52:28.134311018 -0400 ++++ governance/TimelockController.sol 2022-05-25 16:04:57.725255723 -0400 @@ -28,10 +28,10 @@ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); @@ -652,430 +52,9 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol /** * @dev Emitted when a call is scheduled as part of operation `id`. -diff -ruN governance/TimelockController.sol.orig governance/TimelockController.sol.orig ---- governance/TimelockController.sol.orig 1969-12-31 19:00:00.000000000 -0500 -+++ governance/TimelockController.sol.orig 2022-05-25 15:37:51.036084000 -0400 -@@ -0,0 +1,417 @@ -+// SPDX-License-Identifier: MIT -+// OpenZeppelin Contracts (last updated v4.6.0) (governance/TimelockController.sol) -+ -+pragma solidity ^0.8.0; -+ -+import "../access/AccessControl.sol"; -+import "../token/ERC721/IERC721Receiver.sol"; -+import "../token/ERC1155/IERC1155Receiver.sol"; -+import "../utils/Address.sol"; -+ -+/** -+ * @dev Contract module which acts as a timelocked controller. When set as the -+ * owner of an `Ownable` smart contract, it enforces a timelock on all -+ * `onlyOwner` maintenance operations. This gives time for users of the -+ * controlled contract to exit before a potentially dangerous maintenance -+ * operation is applied. -+ * -+ * By default, this contract is self administered, meaning administration tasks -+ * have to go through the timelock process. The proposer (resp executor) role -+ * is in charge of proposing (resp executing) operations. A common use case is -+ * to position this {TimelockController} as the owner of a smart contract, with -+ * a multisig or a DAO as the sole proposer. -+ * -+ * _Available since v3.3._ -+ */ -+contract TimelockController is AccessControl, IERC721Receiver, IERC1155Receiver { -+ bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); -+ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); -+ bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); -+ bytes32 public constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE"); -+ uint256 internal constant _DONE_TIMESTAMP = uint256(1); -+ -+ mapping(bytes32 => uint256) private _timestamps; -+ uint256 private _minDelay; -+ -+ /** -+ * @dev Emitted when a call is scheduled as part of operation `id`. -+ */ -+ event CallScheduled( -+ bytes32 indexed id, -+ uint256 indexed index, -+ address target, -+ uint256 value, -+ bytes data, -+ bytes32 predecessor, -+ uint256 delay -+ ); -+ -+ /** -+ * @dev Emitted when a call is performed as part of operation `id`. -+ */ -+ event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data); -+ -+ /** -+ * @dev Emitted when operation `id` is cancelled. -+ */ -+ event Cancelled(bytes32 indexed id); -+ -+ /** -+ * @dev Emitted when the minimum delay for future operations is modified. -+ */ -+ event MinDelayChange(uint256 oldDuration, uint256 newDuration); -+ -+ /** -+ * @dev Initializes the contract with a given `minDelay`, and a list of -+ * initial proposers and executors. The proposers receive both the -+ * proposer and the canceller role (for backward compatibility). The -+ * executors receive the executor role. -+ * -+ * NOTE: At construction, both the deployer and the timelock itself are -+ * administrators. This helps further configuration of the timelock by the -+ * deployer. After configuration is done, it is recommended that the -+ * deployer renounces its admin position and relies on timelocked -+ * operations to perform future maintenance. -+ */ -+ constructor( -+ uint256 minDelay, -+ address[] memory proposers, -+ address[] memory executors -+ ) { -+ _setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE); -+ _setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE); -+ _setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE); -+ _setRoleAdmin(CANCELLER_ROLE, TIMELOCK_ADMIN_ROLE); -+ -+ // deployer + self administration -+ _setupRole(TIMELOCK_ADMIN_ROLE, _msgSender()); -+ _setupRole(TIMELOCK_ADMIN_ROLE, address(this)); -+ -+ // register proposers and cancellers -+ for (uint256 i = 0; i < proposers.length; ++i) { -+ _setupRole(PROPOSER_ROLE, proposers[i]); -+ _setupRole(CANCELLER_ROLE, proposers[i]); -+ } -+ -+ // register executors -+ for (uint256 i = 0; i < executors.length; ++i) { -+ _setupRole(EXECUTOR_ROLE, executors[i]); -+ } -+ -+ _minDelay = minDelay; -+ emit MinDelayChange(0, minDelay); -+ } -+ -+ /** -+ * @dev Modifier to make a function callable only by a certain role. In -+ * addition to checking the sender's role, `address(0)` 's role is also -+ * considered. Granting a role to `address(0)` is equivalent to enabling -+ * this role for everyone. -+ */ -+ modifier onlyRoleOrOpenRole(bytes32 role) { -+ if (!hasRole(role, address(0))) { -+ _checkRole(role, _msgSender()); -+ } -+ _; -+ } -+ -+ /** -+ * @dev Contract might receive/hold ETH as part of the maintenance process. -+ */ -+ receive() external payable {} -+ -+ /** -+ * @dev See {IERC165-supportsInterface}. -+ */ -+ function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, AccessControl) returns (bool) { -+ return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId); -+ } -+ -+ /** -+ * @dev Returns whether an id correspond to a registered operation. This -+ * includes both Pending, Ready and Done operations. -+ */ -+ function isOperation(bytes32 id) public view virtual returns (bool registered) { -+ return getTimestamp(id) > 0; -+ } -+ -+ /** -+ * @dev Returns whether an operation is pending or not. -+ */ -+ function isOperationPending(bytes32 id) public view virtual returns (bool pending) { -+ return getTimestamp(id) > _DONE_TIMESTAMP; -+ } -+ -+ /** -+ * @dev Returns whether an operation is ready or not. -+ */ -+ function isOperationReady(bytes32 id) public view virtual returns (bool ready) { -+ uint256 timestamp = getTimestamp(id); -+ return timestamp > _DONE_TIMESTAMP && timestamp <= block.timestamp; -+ } -+ -+ /** -+ * @dev Returns whether an operation is done or not. -+ */ -+ function isOperationDone(bytes32 id) public view virtual returns (bool done) { -+ return getTimestamp(id) == _DONE_TIMESTAMP; -+ } -+ -+ /** -+ * @dev Returns the timestamp at with an operation becomes ready (0 for -+ * unset operations, 1 for done operations). -+ */ -+ function getTimestamp(bytes32 id) public view virtual returns (uint256 timestamp) { -+ return _timestamps[id]; -+ } -+ -+ /** -+ * @dev Returns the minimum delay for an operation to become valid. -+ * -+ * This value can be changed by executing an operation that calls `updateDelay`. -+ */ -+ function getMinDelay() public view virtual returns (uint256 duration) { -+ return _minDelay; -+ } -+ -+ /** -+ * @dev Returns the identifier of an operation containing a single -+ * transaction. -+ */ -+ function hashOperation( -+ address target, -+ uint256 value, -+ bytes calldata data, -+ bytes32 predecessor, -+ bytes32 salt -+ ) public pure virtual returns (bytes32 hash) { -+ return keccak256(abi.encode(target, value, data, predecessor, salt)); -+ } -+ -+ /** -+ * @dev Returns the identifier of an operation containing a batch of -+ * transactions. -+ */ -+ function hashOperationBatch( -+ address[] calldata targets, -+ uint256[] calldata values, -+ bytes[] calldata payloads, -+ bytes32 predecessor, -+ bytes32 salt -+ ) public pure virtual returns (bytes32 hash) { -+ return keccak256(abi.encode(targets, values, payloads, predecessor, salt)); -+ } -+ -+ /** -+ * @dev Schedule an operation containing a single transaction. -+ * -+ * Emits a {CallScheduled} event. -+ * -+ * Requirements: -+ * -+ * - the caller must have the 'proposer' role. -+ */ -+ function schedule( -+ address target, -+ uint256 value, -+ bytes calldata data, -+ bytes32 predecessor, -+ bytes32 salt, -+ uint256 delay -+ ) public virtual onlyRole(PROPOSER_ROLE) { -+ bytes32 id = hashOperation(target, value, data, predecessor, salt); -+ _schedule(id, delay); -+ emit CallScheduled(id, 0, target, value, data, predecessor, delay); -+ } -+ -+ /** -+ * @dev Schedule an operation containing a batch of transactions. -+ * -+ * Emits one {CallScheduled} event per transaction in the batch. -+ * -+ * Requirements: -+ * -+ * - the caller must have the 'proposer' role. -+ */ -+ function scheduleBatch( -+ address[] calldata targets, -+ uint256[] calldata values, -+ bytes[] calldata payloads, -+ bytes32 predecessor, -+ bytes32 salt, -+ uint256 delay -+ ) public virtual onlyRole(PROPOSER_ROLE) { -+ require(targets.length == values.length, "TimelockController: length mismatch"); -+ require(targets.length == payloads.length, "TimelockController: length mismatch"); -+ -+ bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt); -+ _schedule(id, delay); -+ for (uint256 i = 0; i < targets.length; ++i) { -+ emit CallScheduled(id, i, targets[i], values[i], payloads[i], predecessor, delay); -+ } -+ } -+ -+ /** -+ * @dev Schedule an operation that is to becomes valid after a given delay. -+ */ -+ function _schedule(bytes32 id, uint256 delay) private { -+ require(!isOperation(id), "TimelockController: operation already scheduled"); -+ require(delay >= getMinDelay(), "TimelockController: insufficient delay"); -+ _timestamps[id] = block.timestamp + delay; -+ } -+ -+ /** -+ * @dev Cancel an operation. -+ * -+ * Requirements: -+ * -+ * - the caller must have the 'canceller' role. -+ */ -+ function cancel(bytes32 id) public virtual onlyRole(CANCELLER_ROLE) { -+ require(isOperationPending(id), "TimelockController: operation cannot be cancelled"); -+ delete _timestamps[id]; -+ -+ emit Cancelled(id); -+ } -+ -+ /** -+ * @dev Execute an (ready) operation containing a single transaction. -+ * -+ * Emits a {CallExecuted} event. -+ * -+ * Requirements: -+ * -+ * - the caller must have the 'executor' role. -+ */ -+ // This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending, -+ // thus any modifications to the operation during reentrancy should be caught. -+ // slither-disable-next-line reentrancy-eth -+ function execute( -+ address target, -+ uint256 value, -+ bytes calldata payload, -+ bytes32 predecessor, -+ bytes32 salt -+ ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) { -+ bytes32 id = hashOperation(target, value, payload, predecessor, salt); -+ -+ _beforeCall(id, predecessor); -+ _execute(target, value, payload); -+ emit CallExecuted(id, 0, target, value, payload); -+ _afterCall(id); -+ } -+ -+ /** -+ * @dev Execute an (ready) operation containing a batch of transactions. -+ * -+ * Emits one {CallExecuted} event per transaction in the batch. -+ * -+ * Requirements: -+ * -+ * - the caller must have the 'executor' role. -+ */ -+ function executeBatch( -+ address[] calldata targets, -+ uint256[] calldata values, -+ bytes[] calldata payloads, -+ bytes32 predecessor, -+ bytes32 salt -+ ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) { -+ require(targets.length == values.length, "TimelockController: length mismatch"); -+ require(targets.length == payloads.length, "TimelockController: length mismatch"); -+ -+ bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt); -+ -+ _beforeCall(id, predecessor); -+ for (uint256 i = 0; i < targets.length; ++i) { -+ address target = targets[i]; -+ uint256 value = values[i]; -+ bytes calldata payload = payloads[i]; -+ _execute(target, value, payload); -+ emit CallExecuted(id, i, target, value, payload); -+ } -+ _afterCall(id); -+ } -+ -+ /** -+ * @dev Execute an operation's call. -+ */ -+ function _execute( -+ address target, -+ uint256 value, -+ bytes calldata data -+ ) internal virtual { -+ (bool success, ) = target.call{value: value}(data); -+ require(success, "TimelockController: underlying transaction reverted"); -+ } -+ -+ /** -+ * @dev Checks before execution of an operation's calls. -+ */ -+ function _beforeCall(bytes32 id, bytes32 predecessor) private view { -+ require(isOperationReady(id), "TimelockController: operation is not ready"); -+ require(predecessor == bytes32(0) || isOperationDone(predecessor), "TimelockController: missing dependency"); -+ } -+ -+ /** -+ * @dev Checks after execution of an operation's calls. -+ */ -+ function _afterCall(bytes32 id) private { -+ require(isOperationReady(id), "TimelockController: operation is not ready"); -+ _timestamps[id] = _DONE_TIMESTAMP; -+ } -+ -+ /** -+ * @dev Changes the minimum timelock duration for future operations. -+ * -+ * Emits a {MinDelayChange} event. -+ * -+ * Requirements: -+ * -+ * - the caller must be the timelock itself. This can only be achieved by scheduling and later executing -+ * an operation where the timelock is the target and the data is the ABI-encoded call to this function. -+ */ -+ function updateDelay(uint256 newDelay) external virtual { -+ require(msg.sender == address(this), "TimelockController: caller must be timelock"); -+ emit MinDelayChange(_minDelay, newDelay); -+ _minDelay = newDelay; -+ } -+ -+ /** -+ * @dev See {IERC721Receiver-onERC721Received}. -+ */ -+ function onERC721Received( -+ address, -+ address, -+ uint256, -+ bytes memory -+ ) public virtual override returns (bytes4) { -+ return this.onERC721Received.selector; -+ } -+ -+ /** -+ * @dev See {IERC1155Receiver-onERC1155Received}. -+ */ -+ function onERC1155Received( -+ address, -+ address, -+ uint256, -+ uint256, -+ bytes memory -+ ) public virtual override returns (bytes4) { -+ return this.onERC1155Received.selector; -+ } -+ -+ /** -+ * @dev See {IERC1155Receiver-onERC1155BatchReceived}. -+ */ -+ function onERC1155BatchReceived( -+ address, -+ address, -+ uint256[] memory, -+ uint256[] memory, -+ bytes memory -+ ) public virtual override returns (bytes4) { -+ return this.onERC1155BatchReceived.selector; -+ } -+} diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol --- governance/utils/Votes.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/utils/Votes.sol 2022-05-25 15:49:40.913370387 -0400 ++++ governance/utils/Votes.sol 2022-05-25 16:04:57.725255723 -0400 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -1148,224 +127,9 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol - function _getVotingUnits(address) internal view virtual returns (uint256); + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public } -diff -ruN governance/utils/Votes.sol.orig governance/utils/Votes.sol.orig ---- governance/utils/Votes.sol.orig 1969-12-31 19:00:00.000000000 -0500 -+++ governance/utils/Votes.sol.orig 2022-05-25 15:37:51.036084000 -0400 -@@ -0,0 +1,211 @@ -+// SPDX-License-Identifier: MIT -+// OpenZeppelin Contracts (last updated v4.6.0) (governance/utils/Votes.sol) -+pragma solidity ^0.8.0; -+ -+import "../../utils/Context.sol"; -+import "../../utils/Counters.sol"; -+import "../../utils/Checkpoints.sol"; -+import "../../utils/cryptography/draft-EIP712.sol"; -+import "./IVotes.sol"; -+ -+/** -+ * @dev This is a base abstract contract that tracks voting units, which are a measure of voting power that can be -+ * transferred, and provides a system of vote delegation, where an account can delegate its voting units to a sort of -+ * "representative" that will pool delegated voting units from different accounts and can then use it to vote in -+ * decisions. In fact, voting units _must_ be delegated in order to count as actual votes, and an account has to -+ * delegate those votes to itself if it wishes to participate in decisions and does not have a trusted representative. -+ * -+ * This contract is often combined with a token contract such that voting units correspond to token units. For an -+ * example, see {ERC721Votes}. -+ * -+ * The full history of delegate votes is tracked on-chain so that governance protocols can consider votes as distributed -+ * at a particular block number to protect against flash loans and double voting. The opt-in delegate system makes the -+ * cost of this history tracking optional. -+ * -+ * When using this module the derived contract must implement {_getVotingUnits} (for example, make it return -+ * {ERC721-balanceOf}), and can use {_transferVotingUnits} to track a change in the distribution of those units (in the -+ * previous example, it would be included in {ERC721-_beforeTokenTransfer}). -+ * -+ * _Available since v4.5._ -+ */ -+abstract contract Votes is IVotes, Context, EIP712 { -+ using Checkpoints for Checkpoints.History; -+ using Counters for Counters.Counter; -+ -+ bytes32 private constant _DELEGATION_TYPEHASH = -+ keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); -+ -+ mapping(address => address) private _delegation; -+ mapping(address => Checkpoints.History) private _delegateCheckpoints; -+ Checkpoints.History private _totalCheckpoints; -+ -+ mapping(address => Counters.Counter) private _nonces; -+ -+ /** -+ * @dev Returns the current amount of votes that `account` has. -+ */ -+ function getVotes(address account) public view virtual override returns (uint256) { -+ return _delegateCheckpoints[account].latest(); -+ } -+ -+ /** -+ * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`). -+ * -+ * Requirements: -+ * -+ * - `blockNumber` must have been already mined -+ */ -+ function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { -+ return _delegateCheckpoints[account].getAtBlock(blockNumber); -+ } -+ -+ /** -+ * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`). -+ * -+ * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. -+ * Votes that have not been delegated are still part of total supply, even though they would not participate in a -+ * vote. -+ * -+ * Requirements: -+ * -+ * - `blockNumber` must have been already mined -+ */ -+ function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) { -+ require(blockNumber < block.number, "Votes: block not yet mined"); -+ return _totalCheckpoints.getAtBlock(blockNumber); -+ } -+ -+ /** -+ * @dev Returns the current total supply of votes. -+ */ -+ function _getTotalSupply() internal view virtual returns (uint256) { -+ return _totalCheckpoints.latest(); -+ } -+ -+ /** -+ * @dev Returns the delegate that `account` has chosen. -+ */ -+ function delegates(address account) public view virtual override returns (address) { -+ return _delegation[account]; -+ } -+ -+ /** -+ * @dev Delegates votes from the sender to `delegatee`. -+ */ -+ function delegate(address delegatee) public virtual override { -+ address account = _msgSender(); -+ _delegate(account, delegatee); -+ } -+ -+ /** -+ * @dev Delegates votes from signer to `delegatee`. -+ */ -+ function delegateBySig( -+ address delegatee, -+ uint256 nonce, -+ uint256 expiry, -+ uint8 v, -+ bytes32 r, -+ bytes32 s -+ ) public virtual override { -+ require(block.timestamp <= expiry, "Votes: signature expired"); -+ address signer = ECDSA.recover( -+ _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), -+ v, -+ r, -+ s -+ ); -+ require(nonce == _useNonce(signer), "Votes: invalid nonce"); -+ _delegate(signer, delegatee); -+ } -+ -+ /** -+ * @dev Delegate all of `account`'s voting units to `delegatee`. -+ * -+ * Emits events {DelegateChanged} and {DelegateVotesChanged}. -+ */ -+ function _delegate(address account, address delegatee) internal virtual { -+ address oldDelegate = delegates(account); -+ _delegation[account] = delegatee; -+ -+ emit DelegateChanged(account, oldDelegate, delegatee); -+ _moveDelegateVotes(oldDelegate, delegatee, _getVotingUnits(account)); -+ } -+ -+ /** -+ * @dev Transfers, mints, or burns voting units. To register a mint, `from` should be zero. To register a burn, `to` -+ * should be zero. Total supply of voting units will be adjusted with mints and burns. -+ */ -+ function _transferVotingUnits( -+ address from, -+ address to, -+ uint256 amount -+ ) internal virtual { -+ if (from == address(0)) { -+ _totalCheckpoints.push(_add, amount); -+ } -+ if (to == address(0)) { -+ _totalCheckpoints.push(_subtract, amount); -+ } -+ _moveDelegateVotes(delegates(from), delegates(to), amount); -+ } -+ -+ /** -+ * @dev Moves delegated votes from one delegate to another. -+ */ -+ function _moveDelegateVotes( -+ address from, -+ address to, -+ uint256 amount -+ ) private { -+ if (from != to && amount > 0) { -+ if (from != address(0)) { -+ (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_subtract, amount); -+ emit DelegateVotesChanged(from, oldValue, newValue); -+ } -+ if (to != address(0)) { -+ (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_add, amount); -+ emit DelegateVotesChanged(to, oldValue, newValue); -+ } -+ } -+ } -+ -+ function _add(uint256 a, uint256 b) private pure returns (uint256) { -+ return a + b; -+ } -+ -+ function _subtract(uint256 a, uint256 b) private pure returns (uint256) { -+ return a - b; -+ } -+ -+ /** -+ * @dev Consumes a nonce. -+ * -+ * Returns the current value and increments nonce. -+ */ -+ function _useNonce(address owner) internal virtual returns (uint256 current) { -+ Counters.Counter storage nonce = _nonces[owner]; -+ current = nonce.current(); -+ nonce.increment(); -+ } -+ -+ /** -+ * @dev Returns an address nonce. -+ */ -+ function nonces(address owner) public view virtual returns (uint256) { -+ return _nonces[owner].current(); -+ } -+ -+ /** -+ * @dev Returns the contract's {EIP712} domain separator. -+ */ -+ // solhint-disable-next-line func-name-mixedcase -+ function DOMAIN_SEPARATOR() external view returns (bytes32) { -+ return _domainSeparatorV4(); -+ } -+ -+ /** -+ * @dev Must return the voting units held by an account. -+ */ -+ function _getVotingUnits(address) internal view virtual returns (uint256); -+} diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol --- token/ERC1155/ERC1155.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC1155/ERC1155.sol 2022-05-25 15:37:51.044083806 -0400 ++++ token/ERC1155/ERC1155.sol 2022-05-25 16:04:57.725255723 -0400 @@ -268,7 +268,7 @@ uint256 id, uint256 amount, @@ -1420,530 +184,9 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol if (to.isContract()) { try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response -diff -ruN token/ERC1155/ERC1155.sol.orig token/ERC1155/ERC1155.sol.orig ---- token/ERC1155/ERC1155.sol.orig 1969-12-31 19:00:00.000000000 -0500 -+++ token/ERC1155/ERC1155.sol.orig 2022-05-25 15:37:51.036084000 -0400 -@@ -0,0 +1,517 @@ -+// SPDX-License-Identifier: MIT -+// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/ERC1155.sol) -+ -+pragma solidity ^0.8.0; -+ -+import "./IERC1155.sol"; -+import "./IERC1155Receiver.sol"; -+import "./extensions/IERC1155MetadataURI.sol"; -+import "../../utils/Address.sol"; -+import "../../utils/Context.sol"; -+import "../../utils/introspection/ERC165.sol"; -+ -+/** -+ * @dev Implementation of the basic standard multi-token. -+ * See https://eips.ethereum.org/EIPS/eip-1155 -+ * Originally based on code by Enjin: https://github.com/enjin/erc-1155 -+ * -+ * _Available since v3.1._ -+ */ -+contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { -+ using Address for address; -+ -+ // Mapping from token ID to account balances -+ mapping(uint256 => mapping(address => uint256)) private _balances; -+ -+ // Mapping from account to operator approvals -+ mapping(address => mapping(address => bool)) private _operatorApprovals; -+ -+ // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json -+ string private _uri; -+ -+ /** -+ * @dev See {_setURI}. -+ */ -+ constructor(string memory uri_) { -+ _setURI(uri_); -+ } -+ -+ /** -+ * @dev See {IERC165-supportsInterface}. -+ */ -+ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { -+ return -+ interfaceId == type(IERC1155).interfaceId || -+ interfaceId == type(IERC1155MetadataURI).interfaceId || -+ super.supportsInterface(interfaceId); -+ } -+ -+ /** -+ * @dev See {IERC1155MetadataURI-uri}. -+ * -+ * This implementation returns the same URI for *all* token types. It relies -+ * on the token type ID substitution mechanism -+ * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. -+ * -+ * Clients calling this function must replace the `\{id\}` substring with the -+ * actual token type ID. -+ */ -+ function uri(uint256) public view virtual override returns (string memory) { -+ return _uri; -+ } -+ -+ /** -+ * @dev See {IERC1155-balanceOf}. -+ * -+ * Requirements: -+ * -+ * - `account` cannot be the zero address. -+ */ -+ function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { -+ require(account != address(0), "ERC1155: address zero is not a valid owner"); -+ return _balances[id][account]; -+ } -+ -+ /** -+ * @dev See {IERC1155-balanceOfBatch}. -+ * -+ * Requirements: -+ * -+ * - `accounts` and `ids` must have the same length. -+ */ -+ function balanceOfBatch(address[] memory accounts, uint256[] memory ids) -+ public -+ view -+ virtual -+ override -+ returns (uint256[] memory) -+ { -+ require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch"); -+ -+ uint256[] memory batchBalances = new uint256[](accounts.length); -+ -+ for (uint256 i = 0; i < accounts.length; ++i) { -+ batchBalances[i] = balanceOf(accounts[i], ids[i]); -+ } -+ -+ return batchBalances; -+ } -+ -+ /** -+ * @dev See {IERC1155-setApprovalForAll}. -+ */ -+ function setApprovalForAll(address operator, bool approved) public virtual override { -+ _setApprovalForAll(_msgSender(), operator, approved); -+ } -+ -+ /** -+ * @dev See {IERC1155-isApprovedForAll}. -+ */ -+ function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { -+ return _operatorApprovals[account][operator]; -+ } -+ -+ /** -+ * @dev See {IERC1155-safeTransferFrom}. -+ */ -+ function safeTransferFrom( -+ address from, -+ address to, -+ uint256 id, -+ uint256 amount, -+ bytes memory data -+ ) public virtual override { -+ require( -+ from == _msgSender() || isApprovedForAll(from, _msgSender()), -+ "ERC1155: caller is not owner nor approved" -+ ); -+ _safeTransferFrom(from, to, id, amount, data); -+ } -+ -+ /** -+ * @dev See {IERC1155-safeBatchTransferFrom}. -+ */ -+ function safeBatchTransferFrom( -+ address from, -+ address to, -+ uint256[] memory ids, -+ uint256[] memory amounts, -+ bytes memory data -+ ) public virtual override { -+ require( -+ from == _msgSender() || isApprovedForAll(from, _msgSender()), -+ "ERC1155: transfer caller is not owner nor approved" -+ ); -+ _safeBatchTransferFrom(from, to, ids, amounts, data); -+ } -+ -+ /** -+ * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. -+ * -+ * Emits a {TransferSingle} event. -+ * -+ * Requirements: -+ * -+ * - `to` cannot be the zero address. -+ * - `from` must have a balance of tokens of type `id` of at least `amount`. -+ * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the -+ * acceptance magic value. -+ */ -+ function _safeTransferFrom( -+ address from, -+ address to, -+ uint256 id, -+ uint256 amount, -+ bytes memory data -+ ) internal virtual { -+ require(to != address(0), "ERC1155: transfer to the zero address"); -+ -+ address operator = _msgSender(); -+ uint256[] memory ids = _asSingletonArray(id); -+ uint256[] memory amounts = _asSingletonArray(amount); -+ -+ _beforeTokenTransfer(operator, from, to, ids, amounts, data); -+ -+ uint256 fromBalance = _balances[id][from]; -+ require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); -+ unchecked { -+ _balances[id][from] = fromBalance - amount; -+ } -+ _balances[id][to] += amount; -+ -+ emit TransferSingle(operator, from, to, id, amount); -+ -+ _afterTokenTransfer(operator, from, to, ids, amounts, data); -+ -+ _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); -+ } -+ -+ /** -+ * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. -+ * -+ * Emits a {TransferBatch} event. -+ * -+ * Requirements: -+ * -+ * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the -+ * acceptance magic value. -+ */ -+ function _safeBatchTransferFrom( -+ address from, -+ address to, -+ uint256[] memory ids, -+ uint256[] memory amounts, -+ bytes memory data -+ ) internal virtual { -+ require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); -+ require(to != address(0), "ERC1155: transfer to the zero address"); -+ -+ address operator = _msgSender(); -+ -+ _beforeTokenTransfer(operator, from, to, ids, amounts, data); -+ -+ for (uint256 i = 0; i < ids.length; ++i) { -+ uint256 id = ids[i]; -+ uint256 amount = amounts[i]; -+ -+ uint256 fromBalance = _balances[id][from]; -+ require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); -+ unchecked { -+ _balances[id][from] = fromBalance - amount; -+ } -+ _balances[id][to] += amount; -+ } -+ -+ emit TransferBatch(operator, from, to, ids, amounts); -+ -+ _afterTokenTransfer(operator, from, to, ids, amounts, data); -+ -+ _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); -+ } -+ -+ /** -+ * @dev Sets a new URI for all token types, by relying on the token type ID -+ * substitution mechanism -+ * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. -+ * -+ * By this mechanism, any occurrence of the `\{id\}` substring in either the -+ * URI or any of the amounts in the JSON file at said URI will be replaced by -+ * clients with the token type ID. -+ * -+ * For example, the `https://token-cdn-domain/\{id\}.json` URI would be -+ * interpreted by clients as -+ * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` -+ * for token type ID 0x4cce0. -+ * -+ * See {uri}. -+ * -+ * Because these URIs cannot be meaningfully represented by the {URI} event, -+ * this function emits no events. -+ */ -+ function _setURI(string memory newuri) internal virtual { -+ _uri = newuri; -+ } -+ -+ /** -+ * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`. -+ * -+ * Emits a {TransferSingle} event. -+ * -+ * Requirements: -+ * -+ * - `to` cannot be the zero address. -+ * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the -+ * acceptance magic value. -+ */ -+ function _mint( -+ address to, -+ uint256 id, -+ uint256 amount, -+ bytes memory data -+ ) internal virtual { -+ require(to != address(0), "ERC1155: mint to the zero address"); -+ -+ address operator = _msgSender(); -+ uint256[] memory ids = _asSingletonArray(id); -+ uint256[] memory amounts = _asSingletonArray(amount); -+ -+ _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); -+ -+ _balances[id][to] += amount; -+ emit TransferSingle(operator, address(0), to, id, amount); -+ -+ _afterTokenTransfer(operator, address(0), to, ids, amounts, data); -+ -+ _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data); -+ } -+ -+ /** -+ * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}. -+ * -+ * Emits a {TransferBatch} event. -+ * -+ * Requirements: -+ * -+ * - `ids` and `amounts` must have the same length. -+ * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the -+ * acceptance magic value. -+ */ -+ function _mintBatch( -+ address to, -+ uint256[] memory ids, -+ uint256[] memory amounts, -+ bytes memory data -+ ) internal virtual { -+ require(to != address(0), "ERC1155: mint to the zero address"); -+ require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); -+ -+ address operator = _msgSender(); -+ -+ _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); -+ -+ for (uint256 i = 0; i < ids.length; i++) { -+ _balances[ids[i]][to] += amounts[i]; -+ } -+ -+ emit TransferBatch(operator, address(0), to, ids, amounts); -+ -+ _afterTokenTransfer(operator, address(0), to, ids, amounts, data); -+ -+ _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data); -+ } -+ -+ /** -+ * @dev Destroys `amount` tokens of token type `id` from `from` -+ * -+ * Emits a {TransferSingle} event. -+ * -+ * Requirements: -+ * -+ * - `from` cannot be the zero address. -+ * - `from` must have at least `amount` tokens of token type `id`. -+ */ -+ function _burn( -+ address from, -+ uint256 id, -+ uint256 amount -+ ) internal virtual { -+ require(from != address(0), "ERC1155: burn from the zero address"); -+ -+ address operator = _msgSender(); -+ uint256[] memory ids = _asSingletonArray(id); -+ uint256[] memory amounts = _asSingletonArray(amount); -+ -+ _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); -+ -+ uint256 fromBalance = _balances[id][from]; -+ require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); -+ unchecked { -+ _balances[id][from] = fromBalance - amount; -+ } -+ -+ emit TransferSingle(operator, from, address(0), id, amount); -+ -+ _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); -+ } -+ -+ /** -+ * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}. -+ * -+ * Emits a {TransferBatch} event. -+ * -+ * Requirements: -+ * -+ * - `ids` and `amounts` must have the same length. -+ */ -+ function _burnBatch( -+ address from, -+ uint256[] memory ids, -+ uint256[] memory amounts -+ ) internal virtual { -+ require(from != address(0), "ERC1155: burn from the zero address"); -+ require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); -+ -+ address operator = _msgSender(); -+ -+ _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); -+ -+ for (uint256 i = 0; i < ids.length; i++) { -+ uint256 id = ids[i]; -+ uint256 amount = amounts[i]; -+ -+ uint256 fromBalance = _balances[id][from]; -+ require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); -+ unchecked { -+ _balances[id][from] = fromBalance - amount; -+ } -+ } -+ -+ emit TransferBatch(operator, from, address(0), ids, amounts); -+ -+ _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); -+ } -+ -+ /** -+ * @dev Approve `operator` to operate on all of `owner` tokens -+ * -+ * Emits an {ApprovalForAll} event. -+ */ -+ function _setApprovalForAll( -+ address owner, -+ address operator, -+ bool approved -+ ) internal virtual { -+ require(owner != operator, "ERC1155: setting approval status for self"); -+ _operatorApprovals[owner][operator] = approved; -+ emit ApprovalForAll(owner, operator, approved); -+ } -+ -+ /** -+ * @dev Hook that is called before any token transfer. This includes minting -+ * and burning, as well as batched variants. -+ * -+ * The same hook is called on both single and batched variants. For single -+ * transfers, the length of the `ids` and `amounts` arrays will be 1. -+ * -+ * Calling conditions (for each `id` and `amount` pair): -+ * -+ * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens -+ * of token type `id` will be transferred to `to`. -+ * - When `from` is zero, `amount` tokens of token type `id` will be minted -+ * for `to`. -+ * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` -+ * will be burned. -+ * - `from` and `to` are never both zero. -+ * - `ids` and `amounts` have the same, non-zero length. -+ * -+ * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. -+ */ -+ function _beforeTokenTransfer( -+ address operator, -+ address from, -+ address to, -+ uint256[] memory ids, -+ uint256[] memory amounts, -+ bytes memory data -+ ) internal virtual {} -+ -+ /** -+ * @dev Hook that is called after any token transfer. This includes minting -+ * and burning, as well as batched variants. -+ * -+ * The same hook is called on both single and batched variants. For single -+ * transfers, the length of the `id` and `amount` arrays will be 1. -+ * -+ * Calling conditions (for each `id` and `amount` pair): -+ * -+ * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens -+ * of token type `id` will be transferred to `to`. -+ * - When `from` is zero, `amount` tokens of token type `id` will be minted -+ * for `to`. -+ * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` -+ * will be burned. -+ * - `from` and `to` are never both zero. -+ * - `ids` and `amounts` have the same, non-zero length. -+ * -+ * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. -+ */ -+ function _afterTokenTransfer( -+ address operator, -+ address from, -+ address to, -+ uint256[] memory ids, -+ uint256[] memory amounts, -+ bytes memory data -+ ) internal virtual {} -+ -+ function _doSafeTransferAcceptanceCheck( -+ address operator, -+ address from, -+ address to, -+ uint256 id, -+ uint256 amount, -+ bytes memory data -+ ) private { -+ if (to.isContract()) { -+ try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { -+ if (response != IERC1155Receiver.onERC1155Received.selector) { -+ revert("ERC1155: ERC1155Receiver rejected tokens"); -+ } -+ } catch Error(string memory reason) { -+ revert(reason); -+ } catch { -+ revert("ERC1155: transfer to non ERC1155Receiver implementer"); -+ } -+ } -+ } -+ -+ function _doSafeBatchTransferAcceptanceCheck( -+ address operator, -+ address from, -+ address to, -+ uint256[] memory ids, -+ uint256[] memory amounts, -+ bytes memory data -+ ) private { -+ if (to.isContract()) { -+ try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( -+ bytes4 response -+ ) { -+ if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { -+ revert("ERC1155: ERC1155Receiver rejected tokens"); -+ } -+ } catch Error(string memory reason) { -+ revert(reason); -+ } catch { -+ revert("ERC1155: transfer to non ERC1155Receiver implementer"); -+ } -+ } -+ } -+ -+ function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { -+ uint256[] memory array = new uint256[](1); -+ array[0] = element; -+ -+ return array; -+ } -+} diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol --- token/ERC20/ERC20.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/ERC20.sol 2022-05-25 15:37:51.044083806 -0400 ++++ token/ERC20/ERC20.sol 2022-05-25 16:04:57.725255723 -0400 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -1964,7 +207,7 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol /** diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol --- token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 15:37:51.044083806 -0400 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 16:04:57.725255723 -0400 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -1978,109 +221,9 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 /** * @dev Returns the receiver address of the flash fee. By default this * implementation returns the address(0) which means the fee amount will be burnt. -diff -ruN token/ERC20/extensions/ERC20FlashMint.sol.orig token/ERC20/extensions/ERC20FlashMint.sol.orig ---- token/ERC20/extensions/ERC20FlashMint.sol.orig 1969-12-31 19:00:00.000000000 -0500 -+++ token/ERC20/extensions/ERC20FlashMint.sol.orig 2022-05-25 15:37:51.036084000 -0400 -@@ -0,0 +1,96 @@ -+// SPDX-License-Identifier: MIT -+// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/extensions/ERC20FlashMint.sol) -+ -+pragma solidity ^0.8.0; -+ -+import "../../../interfaces/IERC3156FlashBorrower.sol"; -+import "../../../interfaces/IERC3156FlashLender.sol"; -+import "../ERC20.sol"; -+ -+/** -+ * @dev Implementation of the ERC3156 Flash loans extension, as defined in -+ * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. -+ * -+ * Adds the {flashLoan} method, which provides flash loan support at the token -+ * level. By default there is no fee, but this can be changed by overriding {flashFee}. -+ * -+ * _Available since v4.1._ -+ */ -+abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { -+ bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); -+ -+ /** -+ * @dev Returns the maximum amount of tokens available for loan. -+ * @param token The address of the token that is requested. -+ * @return The amount of token that can be loaned. -+ */ -+ function maxFlashLoan(address token) public view virtual override returns (uint256) { -+ return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0; -+ } -+ -+ /** -+ * @dev Returns the fee applied when doing flash loans. By default this -+ * implementation has 0 fees. This function can be overloaded to make -+ * the flash loan mechanism deflationary. -+ * @param token The token to be flash loaned. -+ * @param amount The amount of tokens to be loaned. -+ * @return The fees applied to the corresponding flash loan. -+ */ -+ function flashFee(address token, uint256 amount) public view virtual override returns (uint256) { -+ require(token == address(this), "ERC20FlashMint: wrong token"); -+ // silence warning about unused variable without the addition of bytecode. -+ amount; -+ return 0; -+ } -+ -+ /** -+ * @dev Returns the receiver address of the flash fee. By default this -+ * implementation returns the address(0) which means the fee amount will be burnt. -+ * This function can be overloaded to change the fee receiver. -+ * @return The address for which the flash fee will be sent to. -+ */ -+ function _flashFeeReceiver() internal view virtual returns (address) { -+ return address(0); -+ } -+ -+ /** -+ * @dev Performs a flash loan. New tokens are minted and sent to the -+ * `receiver`, who is required to implement the {IERC3156FlashBorrower} -+ * interface. By the end of the flash loan, the receiver is expected to own -+ * amount + fee tokens and have them approved back to the token contract itself so -+ * they can be burned. -+ * @param receiver The receiver of the flash loan. Should implement the -+ * {IERC3156FlashBorrower.onFlashLoan} interface. -+ * @param token The token to be flash loaned. Only `address(this)` is -+ * supported. -+ * @param amount The amount of tokens to be loaned. -+ * @param data An arbitrary datafield that is passed to the receiver. -+ * @return `true` if the flash loan was successful. -+ */ -+ // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount -+ // minted at the beginning is always recovered and burned at the end, or else the entire function will revert. -+ // slither-disable-next-line reentrancy-no-eth -+ function flashLoan( -+ IERC3156FlashBorrower receiver, -+ address token, -+ uint256 amount, -+ bytes calldata data -+ ) public virtual override returns (bool) { -+ require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan"); -+ uint256 fee = flashFee(token, amount); -+ _mint(address(receiver), amount); -+ require( -+ receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, -+ "ERC20FlashMint: invalid return value" -+ ); -+ address flashFeeReceiver = _flashFeeReceiver(); -+ _spendAllowance(address(receiver), address(this), amount + fee); -+ if (fee == 0 || flashFeeReceiver == address(0)) { -+ _burn(address(receiver), amount + fee); -+ } else { -+ _burn(address(receiver), amount); -+ _transfer(address(receiver), flashFeeReceiver, fee); -+ } -+ return true; -+ } -+} diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol --- token/ERC20/extensions/ERC20Votes.sol 2022-03-02 13:56:14.831535075 -0500 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-05-25 15:37:51.044083806 -0400 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-05-25 16:04:57.725255723 -0400 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -2094,7 +237,7 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote /** diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol --- token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 15:37:51.044083806 -0400 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 16:04:57.733255587 -0400 @@ -55,7 +55,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -2104,76 +247,9 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr uint256 value = underlying.balanceOf(address(this)) - totalSupply(); _mint(account, value); return value; -diff -ruN token/ERC20/extensions/ERC20Wrapper.sol.orig token/ERC20/extensions/ERC20Wrapper.sol.orig ---- token/ERC20/extensions/ERC20Wrapper.sol.orig 1969-12-31 19:00:00.000000000 -0500 -+++ token/ERC20/extensions/ERC20Wrapper.sol.orig 2022-05-25 15:37:51.036084000 -0400 -@@ -0,0 +1,63 @@ -+// SPDX-License-Identifier: MIT -+// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/extensions/ERC20Wrapper.sol) -+ -+pragma solidity ^0.8.0; -+ -+import "../ERC20.sol"; -+import "../utils/SafeERC20.sol"; -+ -+/** -+ * @dev Extension of the ERC20 token contract to support token wrapping. -+ * -+ * Users can deposit and withdraw "underlying tokens" and receive a matching number of "wrapped tokens". This is useful -+ * in conjunction with other modules. For example, combining this wrapping mechanism with {ERC20Votes} will allow the -+ * wrapping of an existing "basic" ERC20 into a governance token. -+ * -+ * _Available since v4.2._ -+ */ -+abstract contract ERC20Wrapper is ERC20 { -+ IERC20 public immutable underlying; -+ -+ constructor(IERC20 underlyingToken) { -+ underlying = underlyingToken; -+ } -+ -+ /** -+ * @dev See {ERC20-decimals}. -+ */ -+ function decimals() public view virtual override returns (uint8) { -+ try IERC20Metadata(address(underlying)).decimals() returns (uint8 value) { -+ return value; -+ } catch { -+ return super.decimals(); -+ } -+ } -+ -+ /** -+ * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens. -+ */ -+ function depositFor(address account, uint256 amount) public virtual returns (bool) { -+ SafeERC20.safeTransferFrom(underlying, _msgSender(), address(this), amount); -+ _mint(account, amount); -+ return true; -+ } -+ -+ /** -+ * @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens. -+ */ -+ function withdrawTo(address account, uint256 amount) public virtual returns (bool) { -+ _burn(_msgSender(), amount); -+ SafeERC20.safeTransfer(underlying, account, amount); -+ return true; -+ } -+ -+ /** -+ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal -+ * function that can be exposed with access control if desired. -+ */ -+ function _recover(address account) internal virtual returns (uint256) { -+ uint256 value = underlying.balanceOf(address(this)) - totalSupply(); -+ _mint(account, value); -+ return value; -+ } -+} diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol --- token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 15:59:49.842532468 -0400 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 16:04:57.733255587 -0400 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. @@ -2183,53 +259,9 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ return balanceOf(account); } } -diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol.orig token/ERC721/extensions/draft-ERC721Votes.sol.orig ---- token/ERC721/extensions/draft-ERC721Votes.sol.orig 1969-12-31 19:00:00.000000000 -0500 -+++ token/ERC721/extensions/draft-ERC721Votes.sol.orig 2022-05-25 15:37:51.044083806 -0400 -@@ -0,0 +1,40 @@ -+// SPDX-License-Identifier: MIT -+// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/extensions/draft-ERC721Votes.sol) -+ -+pragma solidity ^0.8.0; -+ -+import "../ERC721.sol"; -+import "../../../governance/utils/Votes.sol"; -+ -+/** -+ * @dev Extension of ERC721 to support voting and delegation as implemented by {Votes}, where each individual NFT counts -+ * as 1 vote unit. -+ * -+ * Tokens do not count as votes until they are delegated, because votes must be tracked which incurs an additional cost -+ * on every transfer. Token holders can either delegate to a trusted representative who will decide how to make use of -+ * the votes in governance decisions, or they can delegate to themselves to be their own representative. -+ * -+ * _Available since v4.5._ -+ */ -+abstract contract ERC721Votes is ERC721, Votes { -+ /** -+ * @dev Adjusts votes when tokens are transferred. -+ * -+ * Emits a {Votes-DelegateVotesChanged} event. -+ */ -+ function _afterTokenTransfer( -+ address from, -+ address to, -+ uint256 tokenId -+ ) internal virtual override { -+ _transferVotingUnits(from, to, 1); -+ super._afterTokenTransfer(from, to, tokenId); -+ } -+ -+ /** -+ * @dev Returns the balance of `account`. -+ */ -+ function _getVotingUnits(address account) internal view virtual override returns (uint256) { -+ return balanceOf(account); -+ } -+} diff -ruN utils/Address.sol utils/Address.sol --- utils/Address.sol 2022-05-25 15:36:58.853363841 -0400 -+++ utils/Address.sol 2022-05-25 15:37:51.044083806 -0400 ++++ utils/Address.sol 2022-05-25 16:04:57.733255587 -0400 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage From d01f3ba925cea8d874a83f50a43ded91794fb069 Mon Sep 17 00:00:00 2001 From: Michael George Date: Wed, 25 May 2022 16:07:55 -0400 Subject: [PATCH 191/254] unclobbered gitignore file --- certora/applyHarness.patch | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 833d550dc..1c137a57b 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,6 +1,6 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol --- access/AccessControl.sol 2022-05-25 15:36:58.849363940 -0400 -+++ access/AccessControl.sol 2022-05-25 16:04:57.725255723 -0400 ++++ access/AccessControl.sol 2022-05-25 16:06:40.279512790 -0400 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -10,9 +10,15 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol _checkRole(role, _msgSender()); } +diff -ruN .gitignore .gitignore +--- .gitignore 1969-12-31 19:00:00.000000000 -0500 ++++ .gitignore 2022-05-25 16:07:01.911145854 -0400 +@@ -0,0 +1,2 @@ ++* ++!.gitignore diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol --- governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 16:04:57.725255723 -0400 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 16:06:40.279512790 -0400 @@ -21,8 +21,8 @@ using SafeCast for uint256; using Timers for Timers.BlockNumber; @@ -26,7 +32,7 @@ diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensi event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/Governor.sol governance/Governor.sol --- governance/Governor.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/Governor.sol 2022-05-25 16:04:57.725255723 -0400 ++++ governance/Governor.sol 2022-05-25 16:06:40.279512790 -0400 @@ -44,7 +44,7 @@ string private _name; @@ -38,7 +44,7 @@ diff -ruN governance/Governor.sol governance/Governor.sol // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol --- governance/TimelockController.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/TimelockController.sol 2022-05-25 16:04:57.725255723 -0400 ++++ governance/TimelockController.sol 2022-05-25 16:06:40.279512790 -0400 @@ -28,10 +28,10 @@ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); @@ -54,7 +60,7 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol * @dev Emitted when a call is scheduled as part of operation `id`. diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol --- governance/utils/Votes.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/utils/Votes.sol 2022-05-25 16:04:57.725255723 -0400 ++++ governance/utils/Votes.sol 2022-05-25 16:06:40.279512790 -0400 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -129,7 +135,7 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol } diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol --- token/ERC1155/ERC1155.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC1155/ERC1155.sol 2022-05-25 16:04:57.725255723 -0400 ++++ token/ERC1155/ERC1155.sol 2022-05-25 16:06:40.279512790 -0400 @@ -268,7 +268,7 @@ uint256 id, uint256 amount, @@ -186,7 +192,7 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol --- token/ERC20/ERC20.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/ERC20.sol 2022-05-25 16:04:57.725255723 -0400 ++++ token/ERC20/ERC20.sol 2022-05-25 16:06:40.279512790 -0400 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -207,7 +213,7 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol /** diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol --- token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 16:04:57.725255723 -0400 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 16:06:40.279512790 -0400 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -223,7 +229,7 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 * implementation returns the address(0) which means the fee amount will be burnt. diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol --- token/ERC20/extensions/ERC20Votes.sol 2022-03-02 13:56:14.831535075 -0500 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-05-25 16:04:57.725255723 -0400 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-05-25 16:06:40.279512790 -0400 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -237,7 +243,7 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote /** diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol --- token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 16:04:57.733255587 -0400 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 16:06:40.311512247 -0400 @@ -55,7 +55,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -249,7 +255,7 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr return value; diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol --- token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 16:04:57.733255587 -0400 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 16:06:40.311512247 -0400 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. @@ -261,7 +267,7 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ } diff -ruN utils/Address.sol utils/Address.sol --- utils/Address.sol 2022-05-25 15:36:58.853363841 -0400 -+++ utils/Address.sol 2022-05-25 16:04:57.733255587 -0400 ++++ utils/Address.sol 2022-05-25 16:06:40.311512247 -0400 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage From 04382cd1d30e77528f9206b2ada2778753704c99 Mon Sep 17 00:00:00 2001 From: Michael George Date: Wed, 25 May 2022 16:20:28 -0400 Subject: [PATCH 192/254] fixed a munging merge problem --- certora/applyHarness.patch | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 1c137a57b..6e9067725 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,6 +1,6 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol --- access/AccessControl.sol 2022-05-25 15:36:58.849363940 -0400 -+++ access/AccessControl.sol 2022-05-25 16:06:40.279512790 -0400 ++++ access/AccessControl.sol 2022-05-25 16:07:31.897814961 -0400 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -12,13 +12,13 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol diff -ruN .gitignore .gitignore --- .gitignore 1969-12-31 19:00:00.000000000 -0500 -+++ .gitignore 2022-05-25 16:07:01.911145854 -0400 ++++ .gitignore 2022-05-25 16:07:31.901816357 -0400 @@ -0,0 +1,2 @@ +* +!.gitignore diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol --- governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 16:06:40.279512790 -0400 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 16:07:31.901816357 -0400 @@ -21,8 +21,8 @@ using SafeCast for uint256; using Timers for Timers.BlockNumber; @@ -32,7 +32,7 @@ diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensi event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/Governor.sol governance/Governor.sol --- governance/Governor.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/Governor.sol 2022-05-25 16:06:40.279512790 -0400 ++++ governance/Governor.sol 2022-05-25 16:07:31.901816357 -0400 @@ -44,7 +44,7 @@ string private _name; @@ -44,7 +44,7 @@ diff -ruN governance/Governor.sol governance/Governor.sol // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol --- governance/TimelockController.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/TimelockController.sol 2022-05-25 16:06:40.279512790 -0400 ++++ governance/TimelockController.sol 2022-05-25 16:07:31.901816357 -0400 @@ -28,10 +28,10 @@ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); @@ -60,7 +60,7 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol * @dev Emitted when a call is scheduled as part of operation `id`. diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol --- governance/utils/Votes.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/utils/Votes.sol 2022-05-25 16:06:40.279512790 -0400 ++++ governance/utils/Votes.sol 2022-05-25 16:07:31.901816357 -0400 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -135,7 +135,7 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol } diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol --- token/ERC1155/ERC1155.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC1155/ERC1155.sol 2022-05-25 16:06:40.279512790 -0400 ++++ token/ERC1155/ERC1155.sol 2022-05-25 16:07:31.901816357 -0400 @@ -268,7 +268,7 @@ uint256 id, uint256 amount, @@ -192,28 +192,19 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol --- token/ERC20/ERC20.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/ERC20.sol 2022-05-25 16:06:40.279512790 -0400 ++++ token/ERC20/ERC20.sol 2022-05-25 16:19:02.895813907 -0400 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ - function _burn(address account, uint256 amount) internal virtual { -+ function _burn(address account, uint256 amount) public virtual returns (bool) { // HARNESS: internal -> public ++ function _burn(address account, uint256 amount) public virtual { // HARNESS: internal -> public require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); -@@ -292,6 +292,8 @@ - emit Transfer(account, address(0), amount); - - _afterTokenTransfer(account, address(0), amount); -+ -+ return true; - } - - /** diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol --- token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 16:06:40.279512790 -0400 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 16:07:31.901816357 -0400 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -229,7 +220,7 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 * implementation returns the address(0) which means the fee amount will be burnt. diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol --- token/ERC20/extensions/ERC20Votes.sol 2022-03-02 13:56:14.831535075 -0500 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-05-25 16:06:40.279512790 -0400 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-05-25 16:14:01.137717224 -0400 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -241,9 +232,18 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote Checkpoint[] private _totalSupplyCheckpoints; /** +@@ -169,7 +169,7 @@ + /** + * @dev Snapshots the totalSupply after it has been decreased. + */ +- function _burn(address account, uint256 amount) internal virtual override { ++ function _burn(address account, uint256 amount) public virtual override { + super._burn(account, amount); + + _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol --- token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 16:06:40.311512247 -0400 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 16:07:31.909819153 -0400 @@ -55,7 +55,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -255,7 +255,7 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr return value; diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol --- token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 16:06:40.311512247 -0400 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 16:07:31.909819153 -0400 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. @@ -267,7 +267,7 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ } diff -ruN utils/Address.sol utils/Address.sol --- utils/Address.sol 2022-05-25 15:36:58.853363841 -0400 -+++ utils/Address.sol 2022-05-25 16:06:40.311512247 -0400 ++++ utils/Address.sol 2022-05-25 16:07:31.909819153 -0400 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage From 5e69b54af1c2bef06ff22f6e19f7a86b4660385a Mon Sep 17 00:00:00 2001 From: Michael George Date: Wed, 25 May 2022 16:20:45 -0400 Subject: [PATCH 193/254] added solc version --- certora/scripts/verifyGovernorPreventLateQuorum.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/certora/scripts/verifyGovernorPreventLateQuorum.sh b/certora/scripts/verifyGovernorPreventLateQuorum.sh index 72b7fa651..f15e67219 100644 --- a/certora/scripts/verifyGovernorPreventLateQuorum.sh +++ b/certora/scripts/verifyGovernorPreventLateQuorum.sh @@ -1,13 +1,12 @@ certoraRun \ certora/harnesses/ERC721VotesHarness.sol certora/munged/governance/TimelockController.sol certora/harnesses/GovernorPreventLateQuorumHarness.sol \ --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorPreventLateQuorum.spec \ - --solc solc \ + --solc solc8.13 \ --optimistic_loop \ --disable_auto_cache_key_gen \ --staging \ --send_only \ --loop_iter 1 \ - --rule $1 \ --msg "$1" \ From 428197be69e39bd1169d73541134902253c12b83 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Thu, 26 May 2022 11:20:46 -0700 Subject: [PATCH 194/254] Added tester rule for only burn --- certora/specs/ERC1155Burnable.spec | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 3e67c1919..344d25380 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -1,7 +1,28 @@ +methods { + balanceOf(address, uint256) returns uint256 envfree + isApprovedForAll(address,address) returns bool envfree +} + +/// If a method call reduces account balances, the caller should be either the +/// owner of the account or approved by the owner to act on its behalf. +rule onlyApprovedCanReduceBalance { + address holder; uint256 token; uint256 amount; + uint256 balanceBefore = balanceOf(holder, token); + + env e; + burn(e, holder, token, amount); // TODO Replace burn with appropriate general function + + uint256 balanceAfter = balanceOf(holder, token); + + assert balanceAfter < balanceBefore => e.msg.sender == holder || isApprovedForAll(holder, e.msg.sender); +} + +/// This rule should always fail. rule sanity { method f; env e; calldataarg args; f(e, args); - assert false; + assert false, + "This rule should always fail"; } \ No newline at end of file From 657a051062442556b717be14a138fc3623a7c7c2 Mon Sep 17 00:00:00 2001 From: Michael George Date: Thu, 26 May 2022 14:46:14 -0400 Subject: [PATCH 195/254] replaced burn with generic function --- certora/specs/ERC1155Burnable.spec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 344d25380..3fd4caf7d 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -9,8 +9,8 @@ rule onlyApprovedCanReduceBalance { address holder; uint256 token; uint256 amount; uint256 balanceBefore = balanceOf(holder, token); - env e; - burn(e, holder, token, amount); // TODO Replace burn with appropriate general function + method f; env e; calldataarg args; + f(e, args); uint256 balanceAfter = balanceOf(holder, token); @@ -25,4 +25,4 @@ rule sanity { assert false, "This rule should always fail"; -} \ No newline at end of file +} From 1dd3b7a307b1a0ce9210eccca9e8808f2d886351 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Thu, 26 May 2022 14:08:33 -0700 Subject: [PATCH 196/254] Made comment changes to Burnable and Pausable spec files --- certora/specs/ERC1155Burnable.spec | 2 +- certora/specs/ERC1155Pausable.spec | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 3fd4caf7d..d38731079 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -14,7 +14,7 @@ rule onlyApprovedCanReduceBalance { uint256 balanceAfter = balanceOf(holder, token); - assert balanceAfter < balanceBefore => e.msg.sender == holder || isApprovedForAll(holder, e.msg.sender); + assert balanceAfter < balanceBefore => e.msg.sender == holder || isApprovedForAll(holder, e.msg.sender); // TODO add assert message } /// This rule should always fail. diff --git a/certora/specs/ERC1155Pausable.spec b/certora/specs/ERC1155Pausable.spec index 81fe40029..4271c07df 100644 --- a/certora/specs/ERC1155Pausable.spec +++ b/certora/specs/ERC1155Pausable.spec @@ -36,8 +36,6 @@ filtered { "Transfer methods must revert in a paused contract"; } - -/// Calling pause must pause an unpaused contract. /// When a contract is in an unpaused state, calling pause() must pause. rule pauseMethodPausesContract { require !paused(); From 4dc0ff9fe3aa4ff2672f0de052a9e966afe13f55 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Thu, 26 May 2022 14:35:27 -0700 Subject: [PATCH 197/254] Added assert message --- certora/specs/ERC1155Burnable.spec | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index d38731079..60418bbbf 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -3,9 +3,9 @@ methods { isApprovedForAll(address,address) returns bool envfree } -/// If a method call reduces account balances, the caller should be either the -/// owner of the account or approved by the owner to act on its behalf. -rule onlyApprovedCanReduceBalance { +/// If a method call reduces account balances, the caller must be either the +/// owner of the account or approved by the owner to act on the owner's behalf. +rule onlyHolderOrApprovedCanReduceBalance { address holder; uint256 token; uint256 amount; uint256 balanceBefore = balanceOf(holder, token); @@ -14,7 +14,8 @@ rule onlyApprovedCanReduceBalance { uint256 balanceAfter = balanceOf(holder, token); - assert balanceAfter < balanceBefore => e.msg.sender == holder || isApprovedForAll(holder, e.msg.sender); // TODO add assert message + assert balanceAfter < balanceBefore => e.msg.sender == holder || isApprovedForAll(holder, e.msg.sender), + "An account balance may only be reduced by the holder or a holder-approved agent"; } /// This rule should always fail. From 78263e2a9a9a75b50d70e2e7cff1084bdb68ad30 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Thu, 26 May 2022 14:39:10 -0700 Subject: [PATCH 198/254] Changed rule description to match phrasing of assert comment --- certora/specs/ERC1155Burnable.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 60418bbbf..5f2ef94d2 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -4,7 +4,8 @@ methods { } /// If a method call reduces account balances, the caller must be either the -/// owner of the account or approved by the owner to act on the owner's behalf. +/// holder of the account or approved by the holder to act on the holder's +/// behalf. rule onlyHolderOrApprovedCanReduceBalance { address holder; uint256 token; uint256 amount; uint256 balanceBefore = balanceOf(holder, token); From ca0d3363b8bdcfbeeb67b6b089f616f4443b7ce1 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Fri, 27 May 2022 13:34:24 -0700 Subject: [PATCH 199/254] Revised rule description to be more accurate --- certora/specs/ERC1155Burnable.spec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 5f2ef94d2..059dae2a8 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -4,8 +4,7 @@ methods { } /// If a method call reduces account balances, the caller must be either the -/// holder of the account or approved by the holder to act on the holder's -/// behalf. +/// holder of the account or approved to act on the holder's behalf. rule onlyHolderOrApprovedCanReduceBalance { address holder; uint256 token; uint256 amount; uint256 balanceBefore = balanceOf(holder, token); From b2cdcc38d45d475ff35a5009008a24fa1b5a8ac7 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Sat, 28 May 2022 11:01:25 -0700 Subject: [PATCH 200/254] final govPreventLateQ --- certora/applyHarness.patch | 96 +++++++++------- .../GovernorPreventLateQuorumHarness.sol | 27 ++++- .../harnesses/InitializableBasicHarness.sol | 35 ++++++ .../harnesses/InitializablrComplexHarness.sol | 18 +++ .../verifyGovernorPreventLateQuorum.sh | 5 +- certora/specs/GovernorBase.spec | 4 +- certora/specs/GovernorPreventLateQuorum.spec | 108 ++++++++++-------- certora/specs/Initializable.spec | 3 + 8 files changed, 199 insertions(+), 97 deletions(-) create mode 100644 certora/harnesses/InitializableBasicHarness.sol create mode 100644 certora/harnesses/InitializablrComplexHarness.sol create mode 100644 certora/specs/Initializable.spec diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 6e9067725..a29a34a9a 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,6 +1,12 @@ +diff -ruN .gitignore .gitignore +--- .gitignore 1969-12-31 16:00:00.000000000 -0800 ++++ .gitignore 2022-05-27 01:31:23.000000000 -0700 +@@ -0,0 +1,2 @@ ++* ++!.gitignore diff -ruN access/AccessControl.sol access/AccessControl.sol ---- access/AccessControl.sol 2022-05-25 15:36:58.849363940 -0400 -+++ access/AccessControl.sol 2022-05-25 16:07:31.897814961 -0400 +--- access/AccessControl.sol 2022-05-25 09:38:35.000000000 -0700 ++++ access/AccessControl.sol 2022-05-27 01:31:23.000000000 -0700 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -10,29 +16,9 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol _checkRole(role, _msgSender()); } -diff -ruN .gitignore .gitignore ---- .gitignore 1969-12-31 19:00:00.000000000 -0500 -+++ .gitignore 2022-05-25 16:07:31.901816357 -0400 -@@ -0,0 +1,2 @@ -+* -+!.gitignore -diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol ---- governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 16:07:31.901816357 -0400 -@@ -21,8 +21,8 @@ - using SafeCast for uint256; - using Timers for Timers.BlockNumber; - -- uint64 private _voteExtension; -- mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines; -+ uint64 internal _voteExtension; -+ mapping(uint256 => Timers.BlockNumber) internal _extendedDeadlines; - - /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. - event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/Governor.sol governance/Governor.sol ---- governance/Governor.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/Governor.sol 2022-05-25 16:07:31.901816357 -0400 +--- governance/Governor.sol 2022-05-25 09:38:35.000000000 -0700 ++++ governance/Governor.sol 2022-05-27 01:31:23.000000000 -0700 @@ -44,7 +44,7 @@ string private _name; @@ -43,8 +29,8 @@ diff -ruN governance/Governor.sol governance/Governor.sol // This queue keeps track of the governor operating on itself. Calls to functions protected by the // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol ---- governance/TimelockController.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/TimelockController.sol 2022-05-25 16:07:31.901816357 -0400 +--- governance/TimelockController.sol 2022-05-25 09:38:35.000000000 -0700 ++++ governance/TimelockController.sol 2022-05-27 01:31:23.000000000 -0700 @@ -28,10 +28,10 @@ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); @@ -58,9 +44,35 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol /** * @dev Emitted when a call is scheduled as part of operation `id`. +diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol +--- governance/extensions/GovernorCountingSimple.sol 2022-05-25 09:38:35.000000000 -0700 ++++ governance/extensions/GovernorCountingSimple.sol 2022-05-27 03:15:40.000000000 -0700 +@@ -27,7 +27,7 @@ + mapping(address => bool) hasVoted; + } + +- mapping(uint256 => ProposalVote) private _proposalVotes; ++ mapping(uint256 => ProposalVote) internal _proposalVotes; + + /** + * @dev See {IGovernor-COUNTING_MODE}. +diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol +--- governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 09:38:35.000000000 -0700 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-27 01:31:23.000000000 -0700 +@@ -21,8 +21,8 @@ + using SafeCast for uint256; + using Timers for Timers.BlockNumber; + +- uint64 private _voteExtension; +- mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines; ++ uint64 internal _voteExtension; ++ mapping(uint256 => Timers.BlockNumber) internal _extendedDeadlines; + + /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. + event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol ---- governance/utils/Votes.sol 2022-05-25 15:36:58.849363940 -0400 -+++ governance/utils/Votes.sol 2022-05-25 16:07:31.901816357 -0400 +--- governance/utils/Votes.sol 2022-05-25 09:38:35.000000000 -0700 ++++ governance/utils/Votes.sol 2022-05-27 01:31:23.000000000 -0700 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -134,8 +146,8 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public } diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol ---- token/ERC1155/ERC1155.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC1155/ERC1155.sol 2022-05-25 16:07:31.901816357 -0400 +--- token/ERC1155/ERC1155.sol 2022-05-25 09:38:35.000000000 -0700 ++++ token/ERC1155/ERC1155.sol 2022-05-27 01:31:23.000000000 -0700 @@ -268,7 +268,7 @@ uint256 id, uint256 amount, @@ -191,8 +203,8 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol ---- token/ERC20/ERC20.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/ERC20.sol 2022-05-25 16:19:02.895813907 -0400 +--- token/ERC20/ERC20.sol 2022-05-25 09:38:35.000000000 -0700 ++++ token/ERC20/ERC20.sol 2022-05-27 01:31:23.000000000 -0700 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -203,8 +215,8 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol _beforeTokenTransfer(account, address(0), amount); diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol ---- token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 16:07:31.901816357 -0400 +--- token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 09:38:35.000000000 -0700 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-27 01:31:23.000000000 -0700 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -219,8 +231,8 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 * @dev Returns the receiver address of the flash fee. By default this * implementation returns the address(0) which means the fee amount will be burnt. diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol ---- token/ERC20/extensions/ERC20Votes.sol 2022-03-02 13:56:14.831535075 -0500 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-05-25 16:14:01.137717224 -0400 +--- token/ERC20/extensions/ERC20Votes.sol 2022-05-06 13:43:21.000000000 -0700 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-05-27 01:31:23.000000000 -0700 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -242,8 +254,8 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol ---- token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 16:07:31.909819153 -0400 +--- token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 09:38:35.000000000 -0700 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-27 01:31:23.000000000 -0700 @@ -55,7 +55,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -254,8 +266,8 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr _mint(account, value); return value; diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol ---- token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 15:36:58.853363841 -0400 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 16:07:31.909819153 -0400 +--- token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 09:38:35.000000000 -0700 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-27 01:31:23.000000000 -0700 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. @@ -266,8 +278,8 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ } } diff -ruN utils/Address.sol utils/Address.sol ---- utils/Address.sol 2022-05-25 15:36:58.853363841 -0400 -+++ utils/Address.sol 2022-05-25 16:07:31.909819153 -0400 +--- utils/Address.sol 2022-05-25 09:38:35.000000000 -0700 ++++ utils/Address.sol 2022-05-27 01:31:23.000000000 -0700 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage diff --git a/certora/harnesses/GovernorPreventLateQuorumHarness.sol b/certora/harnesses/GovernorPreventLateQuorumHarness.sol index 773d2ef54..84858c710 100644 --- a/certora/harnesses/GovernorPreventLateQuorumHarness.sol +++ b/certora/harnesses/GovernorPreventLateQuorumHarness.sol @@ -31,19 +31,34 @@ contract GovernorPreventLateQuorumHarness is Governor, GovernorCountingSimple, G return _voteExtension; } - function getExtendedDeadlineIsUnset(uint256 id) public view returns(bool) { - return _extendedDeadlines[id].isUnset(); + function getExtendedDeadlineIsUnset(uint256 proposalId) public view returns(bool) { + return _extendedDeadlines[proposalId].isUnset(); } - function getExtendedDeadlineIsStarted(uint256 id) public view returns(bool) { - return _extendedDeadlines[id].isStarted(); + function getExtendedDeadlineIsStarted(uint256 proposalId) public view returns(bool) { + return _extendedDeadlines[proposalId].isStarted(); } - function getExtendedDeadline(uint256 id) public view returns(uint64) { - return _extendedDeadlines[id].getDeadline(); + function getExtendedDeadline(uint256 proposalId) public view returns(uint64) { + return _extendedDeadlines[proposalId].getDeadline(); } // Harness from GovernorCountingSimple // + + function getAgainstVotes(uint256 proposalId) public view returns(uint256) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + return proposalvote.againstVotes; + } + + function getAbstainVotes(uint256 proposalId) public view returns(uint256) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + return proposalvote.abstainVotes; + } + + function getForVotes(uint256 proposalId) public view returns(uint256) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + return proposalvote.forVotes; + } function quorumReached(uint256 proposalId) public view returns(bool) { return _quorumReached(proposalId); diff --git a/certora/harnesses/InitializableBasicHarness.sol b/certora/harnesses/InitializableBasicHarness.sol new file mode 100644 index 000000000..08ecbe971 --- /dev/null +++ b/certora/harnesses/InitializableBasicHarness.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../munged/proxy/utils/Initializable4.6.sol"; + +contract InitializableBasicHarness is Initializable { + + uint256 public unchangeable; + + modifier version1() { + require(_initialized == 1); + _; + } + + modifier version2() { + require(_initialized == 2); + _; + } + + function initialize(uint256 val) public initializer { + unchangeable = val; + } + + function reinitialize(uint256 val) public reinitializer(2) { + unchangeable = val; + } + + function returnsV1() public view version1 returns(uint256) { + return unchangeable/2; + } + + function returnsV2() public view version2 returns(uint256) { + return unchangeable/3; + } +} diff --git a/certora/harnesses/InitializablrComplexHarness.sol b/certora/harnesses/InitializablrComplexHarness.sol new file mode 100644 index 000000000..7a4de57c2 --- /dev/null +++ b/certora/harnesses/InitializablrComplexHarness.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../munged/proxy/utils/Initializable4.6.sol"; + +contract InitializableBasicHarness is Initializable { + + uint256 public unchangeable; + + function initialize(uint256 _val) public initializer { + unchangeable = _val; + } + + function reinitialize(uint256 _val) public reinitializer(2) { + unchangeable = _val; + } + +} diff --git a/certora/scripts/verifyGovernorPreventLateQuorum.sh b/certora/scripts/verifyGovernorPreventLateQuorum.sh index f15e67219..64952a1d8 100644 --- a/certora/scripts/verifyGovernorPreventLateQuorum.sh +++ b/certora/scripts/verifyGovernorPreventLateQuorum.sh @@ -1,12 +1,13 @@ certoraRun \ certora/harnesses/ERC721VotesHarness.sol certora/munged/governance/TimelockController.sol certora/harnesses/GovernorPreventLateQuorumHarness.sol \ --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorPreventLateQuorum.spec \ - --solc solc8.13 \ + --solc solc \ --optimistic_loop \ - --disable_auto_cache_key_gen \ --staging \ + --settings -optimisticFallback=true \ --send_only \ --loop_iter 1 \ + --rule $1 \ --msg "$1" \ diff --git a/certora/specs/GovernorBase.spec b/certora/specs/GovernorBase.spec index 9f331fb56..c7727a3d3 100644 --- a/certora/specs/GovernorBase.spec +++ b/certora/specs/GovernorBase.spec @@ -38,7 +38,9 @@ methods { // proposal was created - relation proved in noStartBeforeCreation -definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0; +definition proposalCreated(uint256 pId) returns bool = + proposalSnapshot(pId) > 0 + && proposalDeadline(pId) > 0; ////////////////////////////////////////////////////////////////////////////// diff --git a/certora/specs/GovernorPreventLateQuorum.spec b/certora/specs/GovernorPreventLateQuorum.spec index bcadf76da..f6d3e1718 100644 --- a/certora/specs/GovernorPreventLateQuorum.spec +++ b/certora/specs/GovernorPreventLateQuorum.spec @@ -12,6 +12,9 @@ methods { getExtendedDeadlineIsUnset(uint256) returns bool envfree getExtendedDeadlineIsStarted(uint256) returns bool envfree getExtendedDeadline(uint256) returns uint64 envfree + getAgainstVotes(uint256) returns uint256 envfree + getAbstainVotes(uint256) returns uint256 envfree + getForVotes(uint256) returns uint256 envfree // more robust check than f.selector == _castVote(...).selector latestCastVoteCall() returns uint256 envfree @@ -22,6 +25,8 @@ methods { hashOperationBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => CONSTANT scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256) => CONSTANT + // checkpoint length ERC721 + numCheckpoints(address) returns uint32 } @@ -54,27 +59,55 @@ function helperFunctionsWithRevertOnlyCastVote(uint256 proposalId, method f, env // proposal deadline can be extended (but isn't) definition deadlineExtendable(env e, uint256 pId) returns bool = - getExtendedDeadlineIsUnset(pId) + getExtendedDeadlineIsUnset(pId) // deadline == 0 && !quorumReached(e, pId); // proposal deadline has been extended definition deadlineExtended(env e, uint256 pId) returns bool = - getExtendedDeadlineIsStarted(pId) + getExtendedDeadlineIsStarted(pId) // deadline > 0 && quorumReached(e, pId); +definition proposalNotCreated(env e, uint256 pId) returns bool = + proposalSnapshot(pId) == 0 + && proposalDeadline(pId) == 0 + && getExtendedDeadlineIsUnset(pId) + && getAgainstVotes(pId) == 0 + && getAbstainVotes(pId) == 0 + && getForVotes(pId) == 0 + && !quorumReached(e, pId); + ////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// Invariants ///////////////////////////////// ////////////////////////////////////////////////////////////////////////////// /* - * I1: A propsal must be in state deadlineExtendable or deadlineExtended. - * --INVARIANT PASSING // fails for updateQuorumNumerator - * --ADVANCED SANITY PASSING // can't sanity test failing rules, not sure how it works for invariants + * I1: If a proposal has reached quorum then the proposal snapshot (start block.number) must be non-zero + * INVARIANT NOT PASSING // fails for updateQuorumNumerator and in the initial state when voting token total supply is 0 (causes quoromReached to return true) + * ADVANCED SANITY NOT RAN + */ +invariant quorumReachedEffect(env e, uint256 pId) + quorumReached(e, pId) => proposalCreated(pId) // bug: 0 supply 0 votes => quorumReached + // filtered { f -> f.selector != updateQuorumNumerator(uint256).selector } // * fails for this function + +/* + * I2: A non-existant proposal must meet the definition of one. + * INVARIANT NOT PASSING // fails for updateQuorumNumerator and in the initial state when voting token total supply is 0 (causes quoromReached to return true) + * ADVANCED SANITY NOT RAN */ -invariant proposalInOneState(env e, uint256 pId) - deadlineExtendable(e, pId) || deadlineExtended(e, pId) - { preserved { require proposalCreated(pId); } } +invariant proposalNotCreatedEffects(env e, uint256 pId) + !proposalCreated(pId) => proposalNotCreated(e, pId) + // filtered { f -> f.selector != updateQuorumNumerator(uint256).selector } // * fails for this function + +/* + * I3: A created propsal must be in state deadlineExtendable or deadlineExtended. + * INVARIANT NOT PASSING // fails for updateQuorumNumerator and in the initial state when voting token total supply is 0 (causes quoromReached to return true) + * ADVANCED SANITY NOT RAN + */ +invariant proposalInOneState(env e, uint256 pId) + proposalNotCreated(e, pId) || deadlineExtendable(e, pId) || deadlineExtended(e, pId) + // filtered { f -> f.selector != updateQuorumNumerator(uint256).selector } // * fails for this function + { preserved { requireInvariant proposalNotCreatedEffects(e, pId); }} ////////////////////////////////////////////////////////////////////////////// @@ -92,7 +125,7 @@ invariant proposalInOneState(env e, uint256 pId) rule deadlineChangeEffects(method f) filtered {f -> !f.isView} { env e; calldataarg args; uint256 pId; - require (proposalCreated(pId)); + requireInvariant quorumReachedEffect(e, pId); uint256 deadlineBefore = proposalDeadline(pId); f(e, args); @@ -110,12 +143,12 @@ rule deadlineChangeEffects(method f) filtered {f -> !f.isView} { rule deadlineCantBeUnextended(method f) filtered { f -> !f.isView - && f.selector != updateQuorumNumerator(uint256).selector // * fails for this function + // && f.selector != updateQuorumNumerator(uint256).selector // * fails for this function } { env e; calldataarg args; uint256 pId; require(deadlineExtended(e, pId)); - require(proposalCreated(pId)); + requireInvariant quorumReachedEffect(e, pId); f(e, args); @@ -125,14 +158,14 @@ rule deadlineCantBeUnextended(method f) /* * R3: A proposal's deadline can't change in deadlineExtended state. - * RULE PASSING* + * RULE PASSING * ADVANCED SANITY PASSING */ rule canExtendDeadlineOnce(method f) filtered {f -> !f.isView} { env e; calldataarg args; uint256 pId; require(deadlineExtended(e, pId)); - require(proposalCreated(pId)); + requireInvariant quorumReachedEffect(e, pId); uint256 deadlineBefore = proposalDeadline(pId); f(e, args); @@ -145,7 +178,7 @@ rule canExtendDeadlineOnce(method f) filtered {f -> !f.isView} { //////////////////////////// second set of rules //////////////////////////// // HIGH LEVEL RULE R6: deadline can only extended if quorum reached w/ <= timeOfExtension left to vote -// I1, R4 and R5 are assumed in R6 so we prove them first +// I3, R4 and R5 are assumed in R6 so we prove them first /* * R4: A change in hasVoted must be correlated with an increasing of the vote supports, i.e. casting a vote increases the total number of votes. @@ -163,7 +196,7 @@ rule hasVotedCorrelationNonzero(uint256 pId, method f, env e) filtered {f -> !f. bool hasVotedBefore = hasVoted(e, pId, acc); - helperFunctionsWithRevertOnlyCastVote(pId, f, e); + helperFunctionsWithRevertOnlyCastVote(pId, f, e); // should be f(e, args) uint256 againstAfter = votesAgainst(); uint256 forAfter = votesFor(); @@ -175,11 +208,11 @@ rule hasVotedCorrelationNonzero(uint256 pId, method f, env e) filtered {f -> !f. assert (!hasVotedBefore && hasVotedAfter) => (againstBefore <= againstAfter && forBefore <= forAfter && abstainBefore <= abstainAfter), - "no correlation: some category decreased"; // currently vacous but keeping for CI tests + "after a vote is cast, the number of votes for each category must not decrease"; // currently vacous but keeping for CI tests assert (!hasVotedBefore && hasVotedAfter) => (againstBefore < againstAfter || forBefore < forAfter || abstainBefore < abstainAfter), - "no correlation: no category increased"; + "after a vote is cast, the number of votes of at least one category must increase"; } @@ -200,7 +233,7 @@ rule againstVotesDontCount(method f) filtered {f -> !f.isView} { bool quorumAfter = quorumReached(e, pId); uint256 againstAfter = votesAgainst(); - assert (againstBefore < againstAfter) => quorumBefore == quorumAfter, "quorum reached with against vote"; + assert (againstBefore < againstAfter) => quorumBefore == quorumAfter, "quorum must not be reached with an against vote"; } /* @@ -211,9 +244,9 @@ rule againstVotesDontCount(method f) filtered {f -> !f.isView} { rule deadlineExtenededIfQuorumReached(method f) filtered {f -> !f.isView} { env e; calldataarg args; uint256 pId; - // need invariant that proves that a propsal must be in state deadlineExtendable or deadlineExtended - require(deadlineExtended(e, pId) || deadlineExtendable(e, pId)); - require(proposalCreated(pId)); + requireInvariant proposalInOneState(e, pId); + requireInvariant quorumReachedEffect(e, pId); + requireInvariant proposalNotCreatedEffects(e, pId); bool wasDeadlineExtendable = deadlineExtendable(e, pId); uint64 extension = lateQuorumVoteExtension(); @@ -221,8 +254,8 @@ rule deadlineExtenededIfQuorumReached(method f) filtered {f -> !f.isView} { f(e, args); uint256 deadlineAfter = proposalDeadline(pId); - assert(deadlineAfter > deadlineBefore => wasDeadlineExtendable, "deadline was not extendable"); - assert(deadlineAfter > deadlineBefore => deadlineBefore - e.block.number <= extension, "deadline extension should not be used"); + assert deadlineAfter > deadlineBefore => wasDeadlineExtendable, "deadline must have been extendable for the deadline to be extended"; + assert deadlineAfter > deadlineBefore => deadlineBefore - e.block.number <= extension, "deadline extension should not be used"; } /* @@ -232,7 +265,8 @@ rule deadlineExtenededIfQuorumReached(method f) filtered {f -> !f.isView} { */ rule extendedDeadlineValueSetIfQuorumReached(method f) filtered {f -> !f.isView} { env e; calldataarg args; uint256 pId; - require(deadlineExtended(e, pId) || deadlineExtendable(e, pId)); + + requireInvariant proposalInOneState(e, pId); bool extendedBefore = deadlineExtended(e, pId); f(e, args); @@ -247,33 +281,15 @@ rule extendedDeadlineValueSetIfQuorumReached(method f) filtered {f -> !f.isView} } /* -* R8: If the deadline for a proposal has not been reached, users can still vote. -* --RULE PASSING -* --ADVANCED SANITY PASSING -*/ -rule canVote(method f) filtered {f -> !f.isView} { - env e; calldataarg args; uint256 pId; - address acc = e.msg.sender; - uint256 deadline = proposalDeadline(pId); - bool votedBefore = hasVoted(e, pId, acc); - - require(proposalCreated(pId)); - require(deadline >= e.block.number); - // last error? - helperFunctionsWithRevertOnlyCastVote(pId, f, e); - bool votedAfter = hasVoted(e, pId, acc); - - assert !votedBefore && votedAfter => deadline >= e.block.number; -} - -/* - * R9: Deadline can never be reduced. + * R8: Deadline can never be reduced. * RULE PASSING * ADVANCED SANITY PASSING */ rule deadlineNeverReduced(method f) filtered {f -> !f.isView} { env e; calldataarg args; uint256 pId; - require(proposalCreated(pId)); + + requireInvariant quorumReachedEffect(e, pId); + requireInvariant proposalNotCreatedEffects(e, pId); uint256 deadlineBefore = proposalDeadline(pId); f(e, args); diff --git a/certora/specs/Initializable.spec b/certora/specs/Initializable.spec new file mode 100644 index 000000000..b390da45d --- /dev/null +++ b/certora/specs/Initializable.spec @@ -0,0 +1,3 @@ +methods { + +} \ No newline at end of file From 10f5d8d942f5d625e8383c0b7694c2c9ec0b1a99 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 31 May 2022 13:54:54 -0700 Subject: [PATCH 201/254] Updated verifyERC1155Burnable.sh to first run make munged --- certora/scripts/verifyERC1155Burnable.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/certora/scripts/verifyERC1155Burnable.sh b/certora/scripts/verifyERC1155Burnable.sh index 22187e5dd..e5da5470e 100644 --- a/certora/scripts/verifyERC1155Burnable.sh +++ b/certora/scripts/verifyERC1155Burnable.sh @@ -1,3 +1,5 @@ +make -C certora munged + certoraRun \ certora/harnesses/ERC1155/ERC1155BurnableHarness.sol \ --verify ERC1155BurnableHarness:certora/specs/ERC1155Burnable.spec \ From bd3427d5ffd588d5803c09ecb7de1e502cb9a34d Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 31 May 2022 13:56:04 -0700 Subject: [PATCH 202/254] Included rule burnAmountProportionalToBalanceReduction (passing) --- certora/specs/ERC1155Burnable.spec | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 059dae2a8..dcc792361 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -18,6 +18,29 @@ rule onlyHolderOrApprovedCanReduceBalance { "An account balance may only be reduced by the holder or a holder-approved agent"; } +/// Burning a larger amount of a token must reduce that token's balance more +/// than burning a smaller amount. +rule burnAmountProportionalToBalanceReduction { + storage beforeBurn = lastStorage; + env e; + + address holder; uint256 token; + mathint startingBalance = balanceOf(holder, token); // 10 + uint256 smallBurn; uint256 largeBurn; // 4, 7 + require smallBurn < largeBurn; + + // smaller burn amount + burn(e, holder, token, smallBurn) at beforeBurn; + mathint smallBurnBalanceChange = startingBalance - balanceOf(holder, token); // 4 + + // larger burn amount + burn(e, holder, token, largeBurn) at beforeBurn; + mathint largeBurnBalanceChange = startingBalance - balanceOf(holder, token); // 7 + + assert smallBurnBalanceChange < largeBurnBalanceChange, + "A larger burn must lead to a larger decrease in balance"; +} + /// This rule should always fail. rule sanity { method f; env e; calldataarg args; From 018c58219fb25ceb516348d493cb50435ea77ad5 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 31 May 2022 13:59:46 -0700 Subject: [PATCH 203/254] Updated 1155 Pausable and Supply scripts to first run make munged --- certora/scripts/verifyERC1155Pausable.sh | 2 ++ certora/scripts/verifyERC1155Supply.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/certora/scripts/verifyERC1155Pausable.sh b/certora/scripts/verifyERC1155Pausable.sh index fe7d1a024..69a2bc527 100755 --- a/certora/scripts/verifyERC1155Pausable.sh +++ b/certora/scripts/verifyERC1155Pausable.sh @@ -1,3 +1,5 @@ +make -C certora munged + certoraRun \ certora/harnesses/ERC1155/ERC1155PausableHarness.sol \ --verify ERC1155PausableHarness:certora/specs/ERC1155Pausable.spec \ diff --git a/certora/scripts/verifyERC1155Supply.sh b/certora/scripts/verifyERC1155Supply.sh index dba264693..20552f216 100755 --- a/certora/scripts/verifyERC1155Supply.sh +++ b/certora/scripts/verifyERC1155Supply.sh @@ -1,3 +1,5 @@ +make -C certora munged + certoraRun \ certora/harnesses/ERC1155/ERC1155SupplyHarness.sol \ --verify ERC1155SupplyHarness:certora/specs/ERC1155Supply.spec \ From 2a73da9f673e75484c44ca61215b37c487fbc61a Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 31 May 2022 14:13:44 -0700 Subject: [PATCH 204/254] Added rule burnBatchAmountProportionalToBalanceReduction (unimplemented) --- certora/specs/ERC1155Burnable.spec | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index dcc792361..221ca8a8f 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -41,6 +41,12 @@ rule burnAmountProportionalToBalanceReduction { "A larger burn must lead to a larger decrease in balance"; } +/// Unimplemented rule to verify monotonicity of burnBatch. +rule burnBatchAmountProportionalToBalanceReduction { + assert true, + "just a placeholder that should never show up"; +} + /// This rule should always fail. rule sanity { method f; env e; calldataarg args; From f74e316422a6825c75203cbf2b684ca68f91a152 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 31 May 2022 15:12:48 -0700 Subject: [PATCH 205/254] Added rule sequentialBurnsEquivalentToSingleBurnOfSum (passing) --- certora/specs/ERC1155Burnable.spec | 40 ++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 221ca8a8f..9264bd0c6 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -25,24 +25,54 @@ rule burnAmountProportionalToBalanceReduction { env e; address holder; uint256 token; - mathint startingBalance = balanceOf(holder, token); // 10 - uint256 smallBurn; uint256 largeBurn; // 4, 7 + mathint startingBalance = balanceOf(holder, token); + uint256 smallBurn; uint256 largeBurn; require smallBurn < largeBurn; // smaller burn amount burn(e, holder, token, smallBurn) at beforeBurn; - mathint smallBurnBalanceChange = startingBalance - balanceOf(holder, token); // 4 + mathint smallBurnBalanceChange = startingBalance - balanceOf(holder, token); // larger burn amount burn(e, holder, token, largeBurn) at beforeBurn; - mathint largeBurnBalanceChange = startingBalance - balanceOf(holder, token); // 7 + mathint largeBurnBalanceChange = startingBalance - balanceOf(holder, token); assert smallBurnBalanceChange < largeBurnBalanceChange, "A larger burn must lead to a larger decrease in balance"; } /// Unimplemented rule to verify monotonicity of burnBatch. -rule burnBatchAmountProportionalToBalanceReduction { +rule burnBatchAmountProportionalToBalanceReduction { // TODO implement rule or remove + assert true, + "just a placeholder that should never show up"; +} + +/// Two sequential burns must be equivalent to a single burn of the sum of their +/// amounts. +rule sequentialBurnsEquivalentToSingleBurnOfSum { + storage beforeBurns = lastStorage; + env e; + + address holder; uint256 token; + mathint startingBalance = balanceOf(holder, token); + uint256 firstBurn; uint256 secondBurn; uint256 sumBurn; + require sumBurn == firstBurn + secondBurn; + + // sequential burns + burn(e, holder, token, firstBurn) at beforeBurns; + burn(e, holder, token, secondBurn); + mathint sequentialBurnsBalanceChange = startingBalance - balanceOf(holder, token); + + // burn of sum of sequential burns + burn(e, holder, token, sumBurn) at beforeBurns; + mathint sumBurnBalanceChange = startingBalance - balanceOf(holder, token); + + assert sequentialBurnsBalanceChange == sumBurnBalanceChange, + "Sequential burns must be equivalent to a burn of their sum"; +} + +/// Unimplemented rule to verify additivty of burnBatch. +rule sequentialBatchBurnsEquivalentToSingleBurnBatchOfSum { // TODO implement rule or remove assert true, "just a placeholder that should never show up"; } From 38495a50267ddada1872196abc3af0a7e530fbfe Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 31 May 2022 16:02:23 -0700 Subject: [PATCH 206/254] Added rule singleTokenBurnBurnBatchEquivalence (passing) --- certora/specs/ERC1155Burnable.spec | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 9264bd0c6..a920005fa 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -77,6 +77,32 @@ rule sequentialBatchBurnsEquivalentToSingleBurnBatchOfSum { // TODO implement ru "just a placeholder that should never show up"; } +/// The result of burning a single token must be equivalent whether done via +/// burn or burnBatch. +rule singleTokenBurnBurnBatchEquivalence { + storage beforeBurn = lastStorage; + env e; + + address holder; + uint256 token; uint256 burnAmount; + uint256[] tokens; uint256[] burnAmounts; + mathint startingBalance = balanceOf(holder, token); + + require tokens.length == 1; require burnAmounts.length == 1; + require tokens[0] == token; require burnAmounts[0] == burnAmount; + + // burning via burn + burn(e, holder, token, burnAmount) at beforeBurn; + mathint burnBalanceChange = startingBalance - balanceOf(holder, token); + + // burning via burnBatch + burnBatch(e, holder, tokens, burnAmounts) at beforeBurn; + mathint burnBatchBalanceChange = startingBalance - balanceOf(holder, token); + + assert burnBalanceChange == burnBatchBalanceChange, + "Burning a single token via burn or burnBatch must be equivalent"; +} + /// This rule should always fail. rule sanity { method f; env e; calldataarg args; From 8e283704c31c474435310986fa0961ea67cbce2f Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 31 May 2022 16:29:46 -0700 Subject: [PATCH 207/254] Modified 1155 Burnable scripts --- ...RC1155Burnable.sh => verifyERC1155BurnableAll.sh} | 3 ++- certora/scripts/verifyERC1155BurnableSpecific.sh | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) rename certora/scripts/{verifyERC1155Burnable.sh => verifyERC1155BurnableAll.sh} (81%) create mode 100644 certora/scripts/verifyERC1155BurnableSpecific.sh diff --git a/certora/scripts/verifyERC1155Burnable.sh b/certora/scripts/verifyERC1155BurnableAll.sh similarity index 81% rename from certora/scripts/verifyERC1155Burnable.sh rename to certora/scripts/verifyERC1155BurnableAll.sh index e5da5470e..7a812f6ce 100644 --- a/certora/scripts/verifyERC1155Burnable.sh +++ b/certora/scripts/verifyERC1155BurnableAll.sh @@ -7,4 +7,5 @@ certoraRun \ --optimistic_loop \ --loop_iter 3 \ --cloud \ - --msg "ERC1155 Burnable verification" \ No newline at end of file + --msg "ERC1155 Burnable verification all rules" + \ No newline at end of file diff --git a/certora/scripts/verifyERC1155BurnableSpecific.sh b/certora/scripts/verifyERC1155BurnableSpecific.sh new file mode 100644 index 000000000..5f51ba4c5 --- /dev/null +++ b/certora/scripts/verifyERC1155BurnableSpecific.sh @@ -0,0 +1,12 @@ +make -C certora munged + +certoraRun \ + certora/harnesses/ERC1155/ERC1155BurnableHarness.sol \ + --verify ERC1155BurnableHarness:certora/specs/ERC1155Burnable.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --cloud \ + --rule $1 \ + --msg "ERC1155 Burnable verification specific rule $1" + \ No newline at end of file From 15e847c835a0b2735056581fed97b50ef5b6b216 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 31 May 2022 18:34:22 -0700 Subject: [PATCH 208/254] Added invariant balanceOfZeroAddressIsZero (partially passing) --- certora/specs/ERC1155Supply.spec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index b5c84cc3f..a810a06a0 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -4,7 +4,7 @@ methods { balanceOf(address, uint256) returns uint256 envfree exists_wrapper(uint256) returns bool envfree } - + /// given two different token ids, if totalSupply for one changes, then /// totalSupply for other should not rule token_totalSupply_independence(method f) @@ -60,6 +60,10 @@ rule total_supply_is_sum_of_balances_as_rule { /******************************************************************************/ +/// The balance of a token for the zero address must be zero. +invariant balanceOfZeroAddressIsZero(uint256 token) + balanceOf(0, token) == 0 + // if a user has a token, then the token should exist /* From 0119a187c19563ab83673146b8655fca5770e82f Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Wed, 1 Jun 2022 10:34:03 -0700 Subject: [PATCH 209/254] Added rule multipleTokenBurnBurnBatchEquivalence (passing) --- certora/specs/ERC1155Burnable.spec | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index a920005fa..3448e74fe 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -86,6 +86,7 @@ rule singleTokenBurnBurnBatchEquivalence { address holder; uint256 token; uint256 burnAmount; uint256[] tokens; uint256[] burnAmounts; + mathint startingBalance = balanceOf(holder, token); require tokens.length == 1; require burnAmounts.length == 1; @@ -103,6 +104,48 @@ rule singleTokenBurnBurnBatchEquivalence { "Burning a single token via burn or burnBatch must be equivalent"; } +/// The results of burning multiple tokens must be equivalent whether done +/// separately via burn or together via burnBatch. +rule multipleTokenBurnBurnBatchEquivalence { + storage beforeBurns = lastStorage; + env e; + + address holder; + uint256 tokenA; uint256 tokenB; uint256 tokenC; + uint256 burnAmountA; uint256 burnAmountB; uint256 burnAmountC; + uint256[] tokens; uint256[] burnAmounts; + + require tokenA != tokenB; require tokenB != tokenC; require tokenC != tokenA; + + mathint startingBalanceA = balanceOf(holder, tokenA); + mathint startingBalanceB = balanceOf(holder, tokenB); + mathint startingBalanceC = balanceOf(holder, tokenC); + + require tokens.length == 3; require burnAmounts.length == 3; + require tokens[0] == tokenA; require burnAmounts[0] == burnAmountA; + require tokens[1] == tokenB; require burnAmounts[1] == burnAmountB; + require tokens[2] == tokenC; require burnAmounts[2] == burnAmountC; + + // burning via burn + burn(e, holder, tokenA, burnAmountA) at beforeBurns; + mathint burnBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA); + burn(e, holder, tokenB, burnAmountB) at beforeBurns; + mathint burnBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB); + burn(e, holder, tokenC, burnAmountC) at beforeBurns; + mathint burnBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC); + + // burning via burnBatch + burnBatch(e, holder, tokens, burnAmounts) at beforeBurns; + mathint burnBatchBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA); + mathint burnBatchBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB); + mathint burnBatchBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC); + + assert burnBalanceChangeA == burnBatchBalanceChangeA + && burnBalanceChangeB == burnBatchBalanceChangeB + && burnBalanceChangeC == burnBatchBalanceChangeC, + "Burning multiple tokens via burn or burnBatch must be equivalent"; +} + /// This rule should always fail. rule sanity { method f; env e; calldataarg args; From cab8e489b25772b550c56af0c48887d05323a7c0 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Wed, 1 Jun 2022 17:38:28 -0700 Subject: [PATCH 210/254] initializable final draft, ready for review --- certora/applyHarness.patch | 55 ++- .../harnesses/InitializableBasicHarness.sol | 62 +++- .../harnesses/InitializableComplexHarness.sol | 81 +++++ .../harnesses/InitializablrComplexHarness.sol | 18 - certora/harnesses/ReinitializersHarness.sol | 18 + certora/scripts/old/verifyAll.sh | 4 +- .../verifyGovernorPreventLateQuorum.sh | 6 +- certora/scripts/verifyInitializable.sh | 12 + certora/scripts/verifyInitializableComplex.sh | 12 + certora/specs/Initializable.spec | 212 ++++++++++- certora/specs/governor/GovernorBase.spec | 334 ------------------ .../governor/GovernorCountingSimple.spec | 221 ------------ certora/specs/governor/RulesInProgress.spec | 139 -------- 13 files changed, 428 insertions(+), 746 deletions(-) create mode 100644 certora/harnesses/InitializableComplexHarness.sol delete mode 100644 certora/harnesses/InitializablrComplexHarness.sol create mode 100644 certora/harnesses/ReinitializersHarness.sol create mode 100644 certora/scripts/verifyInitializable.sh create mode 100644 certora/scripts/verifyInitializableComplex.sh delete mode 100644 certora/specs/governor/GovernorBase.spec delete mode 100644 certora/specs/governor/GovernorCountingSimple.spec delete mode 100644 certora/specs/governor/RulesInProgress.spec diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index a29a34a9a..240711aff 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,12 +1,12 @@ diff -ruN .gitignore .gitignore --- .gitignore 1969-12-31 16:00:00.000000000 -0800 -+++ .gitignore 2022-05-27 01:31:23.000000000 -0700 ++++ .gitignore 2022-06-01 15:28:29.000000000 -0700 @@ -0,0 +1,2 @@ +* +!.gitignore diff -ruN access/AccessControl.sol access/AccessControl.sol --- access/AccessControl.sol 2022-05-25 09:38:35.000000000 -0700 -+++ access/AccessControl.sol 2022-05-27 01:31:23.000000000 -0700 ++++ access/AccessControl.sol 2022-06-01 15:28:29.000000000 -0700 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -18,7 +18,7 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol diff -ruN governance/Governor.sol governance/Governor.sol --- governance/Governor.sol 2022-05-25 09:38:35.000000000 -0700 -+++ governance/Governor.sol 2022-05-27 01:31:23.000000000 -0700 ++++ governance/Governor.sol 2022-06-01 15:28:29.000000000 -0700 @@ -44,7 +44,7 @@ string private _name; @@ -30,7 +30,7 @@ diff -ruN governance/Governor.sol governance/Governor.sol // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol --- governance/TimelockController.sol 2022-05-25 09:38:35.000000000 -0700 -+++ governance/TimelockController.sol 2022-05-27 01:31:23.000000000 -0700 ++++ governance/TimelockController.sol 2022-06-01 15:28:29.000000000 -0700 @@ -28,10 +28,10 @@ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); @@ -46,7 +46,7 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol * @dev Emitted when a call is scheduled as part of operation `id`. diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol --- governance/extensions/GovernorCountingSimple.sol 2022-05-25 09:38:35.000000000 -0700 -+++ governance/extensions/GovernorCountingSimple.sol 2022-05-27 03:15:40.000000000 -0700 ++++ governance/extensions/GovernorCountingSimple.sol 2022-06-01 15:28:29.000000000 -0700 @@ -27,7 +27,7 @@ mapping(address => bool) hasVoted; } @@ -58,7 +58,7 @@ diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions * @dev See {IGovernor-COUNTING_MODE}. diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol --- governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 09:38:35.000000000 -0700 -+++ governance/extensions/GovernorPreventLateQuorum.sol 2022-05-27 01:31:23.000000000 -0700 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-06-01 15:28:29.000000000 -0700 @@ -21,8 +21,8 @@ using SafeCast for uint256; using Timers for Timers.BlockNumber; @@ -72,7 +72,7 @@ diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensi event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol --- governance/utils/Votes.sol 2022-05-25 09:38:35.000000000 -0700 -+++ governance/utils/Votes.sol 2022-05-27 01:31:23.000000000 -0700 ++++ governance/utils/Votes.sol 2022-06-01 15:28:29.000000000 -0700 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -145,9 +145,36 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol - function _getVotingUnits(address) internal view virtual returns (uint256); + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public } +diff -ruN proxy/utils/Initializable.sol proxy/utils/Initializable.sol +--- proxy/utils/Initializable.sol 2022-05-25 14:01:12.000000000 -0700 ++++ proxy/utils/Initializable.sol 2022-06-01 17:10:12.000000000 -0700 +@@ -59,12 +59,12 @@ + * @dev Indicates that the contract has been initialized. + * @custom:oz-retyped-from bool + */ +- uint8 private _initialized; ++ uint8 internal _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ +- bool private _initializing; ++ bool internal _initializing; + + /** + * @dev Triggered when the contract has been initialized or reinitialized. +@@ -130,7 +130,7 @@ + _setInitializedVersion(type(uint8).max); + } + +- function _setInitializedVersion(uint8 version) private returns (bool) { ++ function _setInitializedVersion(uint8 version) internal returns (bool) { + // If the contract is initializing we ignore whether _initialized is set in order to support multiple + // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level + // of initializers, because in other contexts the contract may have been reentered. diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol --- token/ERC1155/ERC1155.sol 2022-05-25 09:38:35.000000000 -0700 -+++ token/ERC1155/ERC1155.sol 2022-05-27 01:31:23.000000000 -0700 ++++ token/ERC1155/ERC1155.sol 2022-06-01 15:28:29.000000000 -0700 @@ -268,7 +268,7 @@ uint256 id, uint256 amount, @@ -204,7 +231,7 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol --- token/ERC20/ERC20.sol 2022-05-25 09:38:35.000000000 -0700 -+++ token/ERC20/ERC20.sol 2022-05-27 01:31:23.000000000 -0700 ++++ token/ERC20/ERC20.sol 2022-06-01 15:28:29.000000000 -0700 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -216,7 +243,7 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol _beforeTokenTransfer(account, address(0), amount); diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol --- token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 09:38:35.000000000 -0700 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-05-27 01:31:23.000000000 -0700 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-06-01 15:28:29.000000000 -0700 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -232,7 +259,7 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 * implementation returns the address(0) which means the fee amount will be burnt. diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol --- token/ERC20/extensions/ERC20Votes.sol 2022-05-06 13:43:21.000000000 -0700 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-05-27 01:31:23.000000000 -0700 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-06-01 15:28:29.000000000 -0700 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -255,7 +282,7 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol --- token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 09:38:35.000000000 -0700 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-05-27 01:31:23.000000000 -0700 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-06-01 15:28:29.000000000 -0700 @@ -55,7 +55,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -267,7 +294,7 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr return value; diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol --- token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 09:38:35.000000000 -0700 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-27 01:31:23.000000000 -0700 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-06-01 15:28:29.000000000 -0700 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. @@ -279,7 +306,7 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ } diff -ruN utils/Address.sol utils/Address.sol --- utils/Address.sol 2022-05-25 09:38:35.000000000 -0700 -+++ utils/Address.sol 2022-05-27 01:31:23.000000000 -0700 ++++ utils/Address.sol 2022-06-01 15:28:29.000000000 -0700 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage diff --git a/certora/harnesses/InitializableBasicHarness.sol b/certora/harnesses/InitializableBasicHarness.sol index 08ecbe971..b12b26017 100644 --- a/certora/harnesses/InitializableBasicHarness.sol +++ b/certora/harnesses/InitializableBasicHarness.sol @@ -1,35 +1,73 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.2; -import "../munged/proxy/utils/Initializable4.6.sol"; +import "../munged/proxy/utils/Initializable.sol"; contract InitializableBasicHarness is Initializable { - uint256 public unchangeable; - + uint256 public val; + uint256 public a; + uint256 public b; + modifier version1() { require(_initialized == 1); _; } - modifier version2() { - require(_initialized == 2); + modifier versionN(uint8 n) { + require(_initialized == n); _; } - function initialize(uint256 val) public initializer { - unchangeable = val; + function initialize(uint256 _val, uint256 _a, uint256 _b) public initializer { + a = _a; + b = _b; + val = _val; } - function reinitialize(uint256 val) public reinitializer(2) { - unchangeable = val; + function reinitialize(uint256 _val, uint256 _a, uint256 _b, uint8 n) public reinitializer(n) { + a = _a; + b = _b; + val = _val; } + // Versionede return functions for testing + function returnsV1() public view version1 returns(uint256) { - return unchangeable/2; + return val/2; } - function returnsV2() public view version2 returns(uint256) { - return unchangeable/3; + function returnsVN(uint8 n) public view versionN(n) returns(uint256) { + return val/(n+1); } + + function returnsAV1() public view version1 returns(uint256) { + return a/2; + } + + function returnsAVN(uint8 n) public view versionN(n) returns(uint256) { + return a/(n+1); + } + + function returnsBV1() public view version1 returns(uint256) { + return b/2; + } + + function returnsBVN(uint8 n) public view versionN(n) returns(uint256) { + return b/(n+1); + } + + // Harness // + function initialized() public view returns(uint8) { + return _initialized; + } + + function initializing() public view returns(bool) { + return _initializing; + } + + function thisIsContract() public view returns(bool) { + return !Address.isContract(address(this)); + } + } diff --git a/certora/harnesses/InitializableComplexHarness.sol b/certora/harnesses/InitializableComplexHarness.sol new file mode 100644 index 000000000..3e4c0d4b0 --- /dev/null +++ b/certora/harnesses/InitializableComplexHarness.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../munged/proxy/utils/Initializable.sol"; + +contract InitializableA is Initializable { + uint256 public a; + + modifier version1() { + require(_initialized == 1); + _; + } + + modifier versionN(uint8 n) { + require(_initialized == n); + _; + } + function __InitializableA_init(uint256 _a) internal onlyInitializing { + a = _a; + } + + function returnsAV1() public view version1 returns(uint256) { + return a/2; + } + + function returnsAVN(uint8 n) public view versionN(n) returns(uint256) { + return a/(n+1); + } +} + +contract InitializableB is Initializable, InitializableA { + uint256 public b; + function __InitializableB_init(uint256 _b) internal onlyInitializing { + b = _b; + } + + function returnsBV1() public view version1 returns(uint256) { + return b/2; + } + + function returnsBVN(uint8 n) public view versionN(n) returns(uint256) { + return b/(n+1); + } +} + +contract InitializableComplexHarness is Initializable, InitializableB { + uint256 public val; + + function initialize(uint256 _val, uint256 _a, uint256 _b) initializer public { + val = _val; + __InitializableA_init(_a); + __InitializableB_init(_b); + } + + function reinitialize(uint256 _val, uint256 _a, uint256 _b, uint8 n) reinitializer(n) public { + val = _val; + __InitializableA_init(_a); + __InitializableB_init(_b); + } + + function returnsV1() public view version1 returns(uint256) { + return val/2; + } + + function returnsVN(uint8 n) public view versionN(n) returns(uint256) { + return val/(n+1); + } + + // Harness // + function initialized() public view returns(uint8) { + return _initialized; + } + + function initializing() public view returns(bool) { + return _initializing; + } + + function thisIsContract() public view returns(bool) { + return !Address.isContract(address(this)); + } +} diff --git a/certora/harnesses/InitializablrComplexHarness.sol b/certora/harnesses/InitializablrComplexHarness.sol deleted file mode 100644 index 7a4de57c2..000000000 --- a/certora/harnesses/InitializablrComplexHarness.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.2; - -import "../munged/proxy/utils/Initializable4.6.sol"; - -contract InitializableBasicHarness is Initializable { - - uint256 public unchangeable; - - function initialize(uint256 _val) public initializer { - unchangeable = _val; - } - - function reinitialize(uint256 _val) public reinitializer(2) { - unchangeable = _val; - } - -} diff --git a/certora/harnesses/ReinitializersHarness.sol b/certora/harnesses/ReinitializersHarness.sol new file mode 100644 index 000000000..9bf07450b --- /dev/null +++ b/certora/harnesses/ReinitializersHarness.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol"; + +contract MyTokenV1 is Initializable, ERC20Upgradeable { + function initialize() initializer public { + __ERC20_init("MyToken", "MTK"); + } +} + +contract MyTokenV2 is Initializable, ERC20Upgradeable, ERC20PermitUpgradeable { + function initializeV2() reinitializer(2) public { + __ERC20Permit_init("MyToken"); + } +} \ No newline at end of file diff --git a/certora/scripts/old/verifyAll.sh b/certora/scripts/old/verifyAll.sh index 4e04714bb..eb71ead26 100644 --- a/certora/scripts/old/verifyAll.sh +++ b/certora/scripts/old/verifyAll.sh @@ -18,7 +18,7 @@ do certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ --link ${contractFile%.*}:token=ERC20VotesHarness \ --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ - --solc solc8.2 \ + --solc solc \ --staging shelly/forSasha \ --disableLocalTypeChecking \ --optimistic_loop \ @@ -28,7 +28,7 @@ do else certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ - --solc solc8.2 \ + --solc solc \ --staging shelly/forSasha \ --disableLocalTypeChecking \ --optimistic_loop \ diff --git a/certora/scripts/verifyGovernorPreventLateQuorum.sh b/certora/scripts/verifyGovernorPreventLateQuorum.sh index 64952a1d8..4517d161d 100644 --- a/certora/scripts/verifyGovernorPreventLateQuorum.sh +++ b/certora/scripts/verifyGovernorPreventLateQuorum.sh @@ -3,12 +3,10 @@ certoraRun \ --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorPreventLateQuorum.spec \ --solc solc \ --optimistic_loop \ - --staging \ - --settings -optimisticFallback=true \ + --rule_sanity advanced \ --send_only \ --loop_iter 1 \ - --rule $1 \ - --msg "$1" \ + --msg "all sanity" \ diff --git a/certora/scripts/verifyInitializable.sh b/certora/scripts/verifyInitializable.sh new file mode 100644 index 000000000..f302a5c31 --- /dev/null +++ b/certora/scripts/verifyInitializable.sh @@ -0,0 +1,12 @@ +certoraRun \ + certora/harnesses/InitializableComplexHarness.sol \ + --verify InitializableComplexHarness:certora/specs/Initializable.spec \ + --solc solc \ + --optimistic_loop \ + --send_only \ + --rule_sanity advanced \ + --loop_iter 3 \ + --msg "all complex sanity" \ + + + diff --git a/certora/scripts/verifyInitializableComplex.sh b/certora/scripts/verifyInitializableComplex.sh new file mode 100644 index 000000000..2a3da4b2d --- /dev/null +++ b/certora/scripts/verifyInitializableComplex.sh @@ -0,0 +1,12 @@ +certoraRun \ + certora/harnesses/InitializableComplexHarness.sol \ + --verify InitializableComplexHarness:certora/specs/InitializableCompex.spec \ + --solc solc \ + --optimistic_loop \ + --rule_sanity advanced \ + --send_only \ + --loop_iter 1 \ + --msg "all sanity" \ + + + diff --git a/certora/specs/Initializable.spec b/certora/specs/Initializable.spec index b390da45d..d24d62aa0 100644 --- a/certora/specs/Initializable.spec +++ b/certora/specs/Initializable.spec @@ -1,3 +1,211 @@ methods { - -} \ No newline at end of file + initialize(uint256, uint256, uint256) envfree + reinitialize(uint256, uint256, uint256, uint8) envfree + initialized() returns uint8 envfree + initializing() returns bool envfree + thisIsContract() returns bool envfree + + returnsV1() returns uint256 envfree + returnsVN(uint8) returns uint256 envfree + returnsAV1() returns uint256 envfree + returnsAVN(uint8) returns uint256 envfree + returnsBV1() returns uint256 envfree + returnsBVN(uint8) returns uint256 envfree + a() returns uint256 envfree + b() returns uint256 envfree + val() returns uint256 envfree +} + +definition isUninitialized() returns bool = initialized() == 0; + +definition isInitialized() returns bool = initialized() > 0; + +definition isInitializedOnce() returns bool = initialized() == 1; + +definition isReinitialized() returns bool = initialized() > 1; + +definition isDisabled() returns bool = initialized() == 255; + +/* +idea : need extensive harness to test upgrading with reinitialize + +Setup: + +contracts A, B, C + +Scenario where B extends A and both are being initialized + +Potentially need to summarize ‘isContract’ + +Multiple Versioning within one contract + + + +Test Cases: + +Simple + +1 contract with initialize and reinitialize methods (v 1-3) + +Multiple Inheritance + +Contracts A, B, C + +C Inherits from B, which Inherits from A + +Properties + +/// You can only initialize once +/// two rules prove initialize is equivalent to reinitialize(1) property (?) -- what other effects from these calls? +// if reinitialize(1) is called successfully, _initialized is set to 1 +// if initialize is called successfully, _initialized is set to 1 +/// disabled stays disabled +// invariant +// or rule? +/// n increase iff reinitialize succeeds +// n only increases +// reinitialize called => n increases +/// You can reinitialize(n) iff n > _initialized && initialized < 256 (maxuint8) +// <= +// => +/// can only cal v1 function in v1 + +Question: can we handle reentrancy? +*/ + +// You can only initialize once +rule initOnce() { + uint256 val; uint256 a; uint256 b; + + require isInitialized(); + initialize@withrevert(val, a, b); + assert lastReverted; +} + +/// two rules prove initialize is equivalent to reinitialize(1) property (?) -- what other effects from these calls? + +// if reinitialize(1) is called successfully, _initialized is set to 1 +rule basicReinitializeEffects() { + uint256 val; uint256 a; uint256 b; + + reinitialize(val, a, b, 1); + + assert isInitializedOnce(); +} + +// if initialize is called successfully, _initialized is set to 1 +rule initalizeEffects(method f) { + env e; calldataarg args; + + f(e, args); + + assert f.selector == initialize(uint256, uint256, uint256).selector => isInitializedOnce(); +} + +/// disabled stays disabled + +// invariant +invariant disabledStaysDisabledInv() + isDisabled() => isDisabled() + +// or rule? +rule disabledStaysDisabled(method f) { + env e; calldataarg args; + + bool disabledBefore = isDisabled(); + f(e, args); + bool disabledAfter = isDisabled(); + + assert disabledBefore => disabledAfter; +} + +/// n increase iff reinitialize succeeds + +// n only increases +rule increasingInitialized(method f) { + env e; calldataarg args; + + uint8 initBefore = initialized(); + f(e, args); + uint8 initAfter = initialized(); + assert initBefore <= initAfter; +} + +// reinitialize called => n increases +rule reinitializeIncreasesInit() { + uint256 val; uint8 n; uint256 a; uint256 b; + + uint8 initBefore = initialized(); + reinitialize(val, a, b, n); + uint8 initAfter = initialized(); + + assert initAfter > initBefore; +} + +/// You can reinitialize(n) iff n > _initialized && initialized < 256 (maxuint8) + +// <= +rule reinitializeLiveness() { + env e; uint256 val; uint8 n; uint256 a; uint256 b; + + require !initializing(); + uint8 initVal = initialized(); + reinitialize@withrevert(val, a, b, n); + + assert n > initVal && initVal < 255 => !lastReverted; +} + +// => +rule reinitializeRule() { + env e; calldataarg args; uint256 val; uint8 n; uint256 a; uint256 b; + + uint8 initBefore = initialized(); + reinitialize(val, a, b, n); + uint8 initAfter = initialized(); + + assert initAfter > initBefore => n > initBefore; +} + +// can only call v1 functions in v1 +rule initVersionCheck() { + env e; calldataarg args; uint256 val; uint256 a; uint256 b; uint8 n; + + require n != 1; + + initialize(val, a, b); + assert returnsV1() == val / 2; + assert returnsAV1() == a / 2; + assert returnsBV1() == b / 2; + returnsVN@withrevert(n); + assert lastReverted; + returnsAVN@withrevert(n); + assert lastReverted; + returnsBVN@withrevert(n); + assert lastReverted; +} + +// can only call V_n functions in V_n +rule reinitVersionCheck() { + env e; calldataarg args; uint256 val; uint256 a; uint256 b; uint8 n; uint8 m; + + require n != m; + + reinitialize(val, a, b, n); + assert returnsVN(n) == val / (n + 1); + assert returnsAVN(n) == a / (n + 1); + assert returnsBVN(n) == b / (n + 1); + + returnsVN@withrevert(m); + assert lastReverted; + returnsAVN@withrevert(m); + assert lastReverted; + returnsBVN@withrevert(m); + assert lastReverted; +} + +rule inheritanceCheck() { + env e; calldataarg args; uint256 val; uint8 n; uint256 a; uint256 b; + + initialize(val, a, b); + assert val() == val && a() == a && b() == b; +} diff --git a/certora/specs/governor/GovernorBase.spec b/certora/specs/governor/GovernorBase.spec deleted file mode 100644 index 031b2680e..000000000 --- a/certora/specs/governor/GovernorBase.spec +++ /dev/null @@ -1,334 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -///////////////////// Governor.sol base definitions ////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -using ERC20VotesHarness as erc20votes - -methods { - proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart - proposalDeadline(uint256) returns uint256 envfree // matches proposalVoteEnd - hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree - isExecuted(uint256) returns bool envfree - isCanceled(uint256) returns bool envfree - execute(address[], uint256[], bytes[], bytes32) returns uint256 - hasVoted(uint256, address) returns bool - castVote(uint256, uint8) returns uint256 - updateQuorumNumerator(uint256) - queue(address[], uint256[], bytes[], bytes32) returns uint256 - - // internal functions made public in harness: - _quorumReached(uint256) returns bool - _voteSucceeded(uint256) returns bool envfree - - // function summarization - proposalThreshold() returns uint256 envfree - - getVotes(address, uint256) returns uint256 => DISPATCHER(true) - - getPastTotalSupply(uint256 t) returns uint256 => PER_CALLEE_CONSTANT - getPastVotes(address a, uint256 t) returns uint256 => PER_CALLEE_CONSTANT - - //scheduleBatch(address[],uint256[],bytes[],bytes32,bytes32,uint256) => DISPATCHER(true) - //executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) -} - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////////// Definitions ///////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - - -// proposal was created - relation proved in noStartBeforeCreation -definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0; - - -////////////////////////////////////////////////////////////////////////////// -///////////////////////////// Helper Functions /////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -function helperFunctionsWithRevert(uint256 proposalId, method f, env e) { - address[] targets; uint256[] values; bytes[] calldatas; string reason; bytes32 descriptionHash; - uint8 support; uint8 v; bytes32 r; bytes32 s; - if (f.selector == propose(address[], uint256[], bytes[], string).selector) { - uint256 result = propose@withrevert(e, targets, values, calldatas, reason); - require(result == proposalId); - } else if (f.selector == execute(address[], uint256[], bytes[], bytes32).selector) { - uint256 result = execute@withrevert(e, targets, values, calldatas, descriptionHash); - require(result == proposalId); - } else if (f.selector == castVote(uint256, uint8).selector) { - castVote@withrevert(e, proposalId, support); - } else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) { - castVoteWithReason@withrevert(e, proposalId, support, reason); - } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) { - castVoteBySig@withrevert(e, proposalId, support, v, r, s); - } else if (f.selector == queue(address[], uint256[], bytes[], bytes32).selector) { - queue@withrevert(e, targets, values, calldatas, descriptionHash); - } else { - calldataarg args; - f@withrevert(e, args); - } -} - -/* - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////// State Diagram ////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // // - // castVote(s)() // - // ------------- propose() ---------------------- time pass --------------- time passes ----------- // - // | No Proposal | --------> | Before Start (Delay) | --------> | Voting Period | ----------------------> | execute() | // - // ------------- ---------------------- --------------- -> Executed/Canceled ----------- // - // ------------------------------------------------------------|---------------|-------------------------|--------------> // - // t start end timelock // - // // - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -*/ - - -/////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////// Global Valid States ///////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////// - - -/* - * Start and end date are either initialized (non zero) or uninitialized (zero) simultaneously - * This invariant assumes that the block number cannot be 0 at any stage of the contract cycle - * This is very safe assumption as usually the 0 block is genesis block which is uploaded with data - * by the developers and will not be valid to raise proposals (at the current way that block chain is functioning) - */ - // To use env with general preserved block disable type checking [--disableLocalTypeChecking] -invariant startAndEndDatesNonZero(uint256 pId) - proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0 - { preserved with (env e){ - require e.block.number > 0; - }} - - -/* - * If a proposal is canceled it must have a start and an end date - */ - // To use env with general preserved block disable type checking [--disableLocalTypeChecking] -invariant canceledImplyStartAndEndDateNonZero(uint pId) - isCanceled(pId) => proposalSnapshot(pId) != 0 - {preserved with (env e){ - require e.block.number > 0; - }} - - -/* - * If a proposal is executed it must have a start and an end date - */ - // To use env with general preserved block disable type checking [--disableLocalTypeChecking] -invariant executedImplyStartAndEndDateNonZero(uint pId) - isExecuted(pId) => proposalSnapshot(pId) != 0 - { preserved with (env e){ - requireInvariant startAndEndDatesNonZero(pId); - require e.block.number > 0; - }} - - -/* - * A proposal starting block number must be less or equal than the proposal end date - */ -invariant voteStartBeforeVoteEnd(uint256 pId) - // from < to <= because snapshot and deadline can be the same block number if delays are set to 0 - // This is possible before the integration of GovernorSettings.sol to the system. - // After integration of GovernorSettings.sol the invariant expression should be changed from <= to < - (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) - // (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) - { preserved { - requireInvariant startAndEndDatesNonZero(pId); - }} - - -/* - * A proposal cannot be both executed and canceled simultaneously. - */ -invariant noBothExecutedAndCanceled(uint256 pId) - !isExecuted(pId) || !isCanceled(pId) - - -/* - * A proposal could be executed only if quorum was reached and vote succeeded - */ -rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f){ - bool isExecutedBefore = isExecuted(pId); - bool quorumReachedBefore = _quorumReached(e, pId); - bool voteSucceededBefore = _voteSucceeded(pId); - - calldataarg args; - f(e, args); - - bool isExecutedAfter = isExecuted(pId); - assert (!isExecutedBefore && isExecutedAfter) => (quorumReachedBefore && voteSucceededBefore), "quorum was changed"; -} - -/////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////// In-State Rules ///////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////// - -//========================================== -//------------- Voting Period -------------- -//========================================== - -/* - * A user cannot vote twice - */ - // Checked for castVote only. all 3 castVote functions call _castVote, so the completness of the verification is counted on - // the fact that the 3 functions themselves makes no chages, but rather call an internal function to execute. - // That means that we do not check those 3 functions directly, however for castVote & castVoteWithReason it is quite trivial - // to understand why this is ok. For castVoteBySig we basically assume that the signature referendum is correct without checking it. - // We could check each function seperately and pass the rule, but that would have uglyfied the code with no concrete - // benefit, as it is evident that nothing is happening in the first 2 functions (calling a view function), and we do not desire to check the signature verification. -rule doubleVoting(uint256 pId, uint8 sup, method f) { - env e; - address user = e.msg.sender; - bool votedCheck = hasVoted(e, pId, user); - - castVote@withrevert(e, pId, sup); - - assert votedCheck => lastReverted, "double voting accured"; -} - - -/////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////// State Transitions Rules ////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////// - -//=========================================== -//-------- Propose() --> End of Time -------- -//=========================================== - - -/* - * Once a proposal is created, voteStart and voteEnd are immutable - */ -rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { - uint256 _voteStart = proposalSnapshot(pId); - uint256 _voteEnd = proposalDeadline(pId); - - require proposalCreated(pId); // startDate > 0 - - env e; calldataarg arg; - f(e, arg); - - uint256 voteStart_ = proposalSnapshot(pId); - uint256 voteEnd_ = proposalDeadline(pId); - assert _voteStart == voteStart_, "Start date was changed"; - assert _voteEnd == voteEnd_, "End date was changed"; -} - - -/* - * Voting cannot start at a block number prior to proposal’s creation block number - */ -rule noStartBeforeCreation(uint256 pId) { - uint256 previousStart = proposalSnapshot(pId); - // This line makes sure that we see only cases where start date is changed from 0, i.e. creation of proposal - // We proved in immutableFieldsAfterProposalCreation that once dates set for proposal, it cannot be changed - require !proposalCreated(pId); // previousStart == 0; - - env e; calldataarg args; - propose(e, args); - - uint256 newStart = proposalSnapshot(pId); - // if created, start is after current block number (creation block) - assert(newStart != previousStart => newStart >= e.block.number); -} - - -//============================================ -//--- End of Voting Period --> End of Time --- -//============================================ - - -/* - * A proposal can neither be executed nor canceled before it ends - */ - // By induction it cannot be executed nor canceled before it starts, due to voteStartBeforeVoteEnd -rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ - require !isExecuted(pId) && !isCanceled(pId); - - env e; calldataarg args; - f(e, args); - - assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before deadline"; -} - -//////////////////////////////////////////////////////////////////////////////// -////////////////////// Integrity Of Functions (Unit Tests) ///////////////////// -//////////////////////////////////////////////////////////////////////////////// - - -//////////////////////////////////////////////////////////////////////////////// -////////////////////////////// High Level Rules //////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - - -//////////////////////////////////////////////////////////////////////////////// -///////////////////////////// Not Categorized Yet ////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - - -/* - * All proposal specific (non-view) functions should revert if proposal is executed - */ - // In this rule we show that if a function is executed, i.e. execute() was called on the proposal ID, - // non of the proposal specific functions can make changes again. In executedOnlyAfterExecuteFunc - // we connected the executed attribute to the execute() function, showing that only execute() can - // change it, and that it will always change it. -rule allFunctionsRevertIfExecuted(method f) filtered { f -> - !f.isView && !f.isFallback - && f.selector != updateTimelock(address).selector - && f.selector != updateQuorumNumerator(uint256).selector - && f.selector != queue(address[],uint256[],bytes[],bytes32).selector - && f.selector != relay(address,uint256,bytes).selector - && f.selector != 0xb9a61961 // __acceptAdmin() -} { - env e; calldataarg args; - uint256 pId; - require(isExecuted(pId)); - requireInvariant noBothExecutedAndCanceled(pId); - requireInvariant executedImplyStartAndEndDateNonZero(pId); - - helperFunctionsWithRevert(pId, f, e); - - assert(lastReverted, "Function was not reverted"); -} - -/* - * All proposal specific (non-view) functions should revert if proposal is canceled - */ -rule allFunctionsRevertIfCanceled(method f) filtered { - f -> !f.isView && !f.isFallback - && f.selector != updateTimelock(address).selector - && f.selector != updateQuorumNumerator(uint256).selector - && f.selector != queue(address[],uint256[],bytes[],bytes32).selector - && f.selector != relay(address,uint256,bytes).selector - && f.selector != 0xb9a61961 // __acceptAdmin() -} { - env e; calldataarg args; - uint256 pId; - require(isCanceled(pId)); - requireInvariant noBothExecutedAndCanceled(pId); - requireInvariant canceledImplyStartAndEndDateNonZero(pId); - - helperFunctionsWithRevert(pId, f, e); - - assert(lastReverted, "Function was not reverted"); -} - -/* - * Proposal can be switched to executed only via execute() function - */ -rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) { - env e; calldataarg args; - uint256 pId; - bool executedBefore = isExecuted(pId); - require(!executedBefore); - - helperFunctionsWithRevert(pId, f, e); - - bool executedAfter = isExecuted(pId); - assert(executedAfter != executedBefore => f.selector == execute(address[], uint256[], bytes[], bytes32).selector, "isExecuted only changes in the execute method"); -} - diff --git a/certora/specs/governor/GovernorCountingSimple.spec b/certora/specs/governor/GovernorCountingSimple.spec deleted file mode 100644 index 6d2d5fbb7..000000000 --- a/certora/specs/governor/GovernorCountingSimple.spec +++ /dev/null @@ -1,221 +0,0 @@ -import "GovernorBase.spec" - -using ERC20VotesHarness as erc20votes - -methods { - ghost_sum_vote_power_by_id(uint256) returns uint256 envfree - - quorum(uint256) returns uint256 - proposalVotes(uint256) returns (uint256, uint256, uint256) envfree - - quorumNumerator() returns uint256 - _executor() returns address - - erc20votes._getPastVotes(address, uint256) returns uint256 - - getExecutor() returns address - - timelock() returns address -} - - -////////////////////////////////////////////////////////////////////////////// -///////////////////////////////// GHOSTS ///////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - - -//////////// ghosts to keep track of votes counting //////////// - -/* - * the sum of voting power of those who voted - */ -ghost sum_all_votes_power() returns uint256 { - init_state axiom sum_all_votes_power() == 0; -} - -hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE { - havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; -} - -/* - * sum of all votes casted per proposal - */ -ghost tracked_weight(uint256) returns uint256 { - init_state axiom forall uint256 p. tracked_weight(p) == 0; -} - -/* - * sum of all votes casted - */ -ghost sum_tracked_weight() returns uint256 { - init_state axiom sum_tracked_weight() == 0; -} - -/* - * getter for _proposalVotes.againstVotes - */ -ghost votesAgainst() returns uint256 { - init_state axiom votesAgainst() == 0; -} - -/* - * getter for _proposalVotes.forVotes - */ -ghost votesFor() returns uint256 { - init_state axiom votesFor() == 0; -} - -/* - * getter for _proposalVotes.abstainVotes - */ -ghost votesAbstain() returns uint256 { - init_state axiom votesAbstain() == 0; -} - -hook Sstore _proposalVotes [KEY uint256 pId].againstVotes uint256 votes(uint256 old_votes) STORAGE { - havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; - havoc votesAgainst assuming votesAgainst@new() == votesAgainst@old() - old_votes + votes; -} - -hook Sstore _proposalVotes [KEY uint256 pId].forVotes uint256 votes(uint256 old_votes) STORAGE { - havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; - havoc votesFor assuming votesFor@new() == votesFor@old() - old_votes + votes; -} - -hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 old_votes) STORAGE { - havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; - havoc votesAbstain assuming votesAbstain@new() == votesAbstain@old() - old_votes + votes; -} - - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////// INVARIANTS //////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - - -/* - * sum of all votes casted is equal to the sum of voting power of those who voted, per each proposal - */ -invariant SumOfVotesCastEqualSumOfPowerOfVotedPerProposal(uint256 pId) - tracked_weight(pId) == ghost_sum_vote_power_by_id(pId) - - -/* - * sum of all votes casted is equal to the sum of voting power of those who voted - */ -invariant SumOfVotesCastEqualSumOfPowerOfVoted() - sum_tracked_weight() == sum_all_votes_power() - - -/* -* sum of all votes casted is greater or equal to the sum of voting power of those who voted at a specific proposal -*/ -invariant OneIsNotMoreThanAll(uint256 pId) - sum_all_votes_power() >= tracked_weight(pId) - - -////////////////////////////////////////////////////////////////////////////// -///////////////////////////////// RULES ////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - - -/* - * Only sender's voting status can be changed by execution of any cast vote function - */ -// Checked for castVote only. all 3 castVote functions call _castVote, so the completness of the verification is counted on - // the fact that the 3 functions themselves makes no chages, but rather call an internal function to execute. - // That means that we do not check those 3 functions directly, however for castVote & castVoteWithReason it is quite trivial - // to understand why this is ok. For castVoteBySig we basically assume that the signature referendum is correct without checking it. - // We could check each function seperately and pass the rule, but that would have uglyfied the code with no concrete - // benefit, as it is evident that nothing is happening in the first 2 functions (calling a view function), and we do not desire to check the signature verification. -rule noVoteForSomeoneElse(uint256 pId, uint8 sup, method f) { - env e; calldataarg args; - - address voter = e.msg.sender; - address user; - - bool hasVotedBefore_User = hasVoted(e, pId, user); - - castVote@withrevert(e, pId, sup); - require(!lastReverted); - - bool hasVotedAfter_User = hasVoted(e, pId, user); - - assert user != voter => hasVotedBefore_User == hasVotedAfter_User; -} - - -/* -* Total voting tally is monotonically non-decreasing in every operation -*/ -rule votingWeightMonotonicity(method f){ - uint256 votingWeightBefore = sum_tracked_weight(); - - env e; - calldataarg args; - f(e, args); - - uint256 votingWeightAfter = sum_tracked_weight(); - - assert votingWeightBefore <= votingWeightAfter, "Voting weight was decreased somehow"; -} - - -/* -* A change in hasVoted must be correlated with an non-decreasing of the vote supports (nondecrease because user can vote with weight 0) -*/ -rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) { - address acc = e.msg.sender; - - uint256 againstBefore = votesAgainst(); - uint256 forBefore = votesFor(); - uint256 abstainBefore = votesAbstain(); - - bool hasVotedBefore = hasVoted(e, pId, acc); - - helperFunctionsWithRevert(pId, f, e); - require(!lastReverted); - - uint256 againstAfter = votesAgainst(); - uint256 forAfter = votesFor(); - uint256 abstainAfter = votesAbstain(); - - bool hasVotedAfter = hasVoted(e, pId, acc); - - assert (!hasVotedBefore && hasVotedAfter) => againstBefore <= againstAfter || forBefore <= forAfter || abstainBefore <= abstainAfter, "no correlation"; -} - - -/* -* Only privileged users can execute privileged operations, e.g. change _quorumNumerator or _timelock -*/ -rule privilegedOnlyNumerator(method f, uint256 newQuorumNumerator){ - env e; - calldataarg arg; - uint256 quorumNumBefore = quorumNumerator(e); - - f(e, arg); - - uint256 quorumNumAfter = quorumNumerator(e); - address executorCheck = getExecutor(e); - - assert quorumNumBefore != quorumNumAfter => e.msg.sender == executorCheck, "non priveleged user changed quorum numerator"; -} - -rule privilegedOnlyTimelock(method f, uint256 newQuorumNumerator){ - env e; - calldataarg arg; - uint256 timelockBefore = timelock(e); - - f(e, arg); - - uint256 timelockAfter = timelock(e); - - assert timelockBefore != timelockAfter => e.msg.sender == timelockBefore, "non priveleged user changed timelock"; -} diff --git a/certora/specs/governor/RulesInProgress.spec b/certora/specs/governor/RulesInProgress.spec deleted file mode 100644 index cbad3336e..000000000 --- a/certora/specs/governor/RulesInProgress.spec +++ /dev/null @@ -1,139 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -////////////// THIS SPEC IS A RESERVE FOR NOT IN PROGRESS ////////////// -////////////////////////////////////////////////////////////////////////////// - -import "GovernorBase.spec" - -using ERC20VotesHarness as erc20votes - -methods { - ghost_sum_vote_power_by_id(uint256) returns uint256 envfree - - quorum(uint256) returns uint256 - proposalVotes(uint256) returns (uint256, uint256, uint256) envfree - - quorumNumerator() returns uint256 - _executor() returns address - - erc20votes._getPastVotes(address, uint256) returns uint256 - - getExecutor() returns address - - timelock() returns address -} - - -////////////////////////////////////////////////////////////////////////////// -///////////////////////////////// GHOSTS ///////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - - -//////////// ghosts to keep track of votes counting //////////// - -/* - * the sum of voting power of those who voted - */ -ghost sum_all_votes_power() returns uint256 { - init_state axiom sum_all_votes_power() == 0; -} - -hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE { - havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; -} - -/* - * sum of all votes casted per proposal - */ -ghost tracked_weight(uint256) returns uint256 { - init_state axiom forall uint256 p. tracked_weight(p) == 0; -} - -/* - * sum of all votes casted - */ -ghost sum_tracked_weight() returns uint256 { - init_state axiom sum_tracked_weight() == 0; -} - -/* - * getter for _proposalVotes.againstVotes - */ -ghost votesAgainst() returns uint256 { - init_state axiom votesAgainst() == 0; -} - -/* - * getter for _proposalVotes.forVotes - */ -ghost votesFor() returns uint256 { - init_state axiom votesFor() == 0; -} - -/* - * getter for _proposalVotes.abstainVotes - */ -ghost votesAbstain() returns uint256 { - init_state axiom votesAbstain() == 0; -} - -hook Sstore _proposalVotes [KEY uint256 pId].againstVotes uint256 votes(uint256 old_votes) STORAGE { - havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; - havoc votesAgainst assuming votesAgainst@new() == votesAgainst@old() - old_votes + votes; -} - -hook Sstore _proposalVotes [KEY uint256 pId].forVotes uint256 votes(uint256 old_votes) STORAGE { - havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; - havoc votesFor assuming votesFor@new() == votesFor@old() - old_votes + votes; -} - -hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 old_votes) STORAGE { - havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && - (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); - havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; - havoc votesAbstain assuming votesAbstain@new() == votesAbstain@old() - old_votes + votes; -} - - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////// INVARIANTS //////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - - - -////////////////////////////////////////////////////////////////////////////// -///////////////////////////////// RULES ////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - - -//NOT FINISHED -/* -* the sum of voting power of those who voted is less or equal to the maximum possible votes, per each proposal -*/ -rule possibleTotalVotes(uint256 pId, uint8 sup, env e, method f) { - - // add requireinvariant for all i, j. i = i - 1 && i < j => checkpointlookup[i] < checkpointlookup[j]; - require tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)); - - uint256 againstB; - uint256 forB; - uint256 absatinB; - againstB, forB, absatinB = proposalVotes(pId); - - calldataarg args; - //f(e, args); - - castVote(e, pId, sup); - - uint256 against; - uint256 for; - uint256 absatin; - against, for, absatin = proposalVotes(pId); - - uint256 ps = proposalSnapshot(pId); - - assert tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)), "bla bla bla"; -} \ No newline at end of file From 669a22e0ff7eba0b09aeca2ae216d7093ba24025 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Wed, 1 Jun 2022 17:39:39 -0700 Subject: [PATCH 211/254] remove duplicate initalize script --- certora/scripts/verifyInitializableComplex.sh | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 certora/scripts/verifyInitializableComplex.sh diff --git a/certora/scripts/verifyInitializableComplex.sh b/certora/scripts/verifyInitializableComplex.sh deleted file mode 100644 index 2a3da4b2d..000000000 --- a/certora/scripts/verifyInitializableComplex.sh +++ /dev/null @@ -1,12 +0,0 @@ -certoraRun \ - certora/harnesses/InitializableComplexHarness.sol \ - --verify InitializableComplexHarness:certora/specs/InitializableCompex.spec \ - --solc solc \ - --optimistic_loop \ - --rule_sanity advanced \ - --send_only \ - --loop_iter 1 \ - --msg "all sanity" \ - - - From a6863a059c53773a77abc3a356f01036c9c5ef7f Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Thu, 2 Jun 2022 11:29:28 -0700 Subject: [PATCH 212/254] Changed invariant total_supply_is_sum_of_balances (partially passing) --- certora/specs/ERC1155Supply.spec | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index a810a06a0..48bacb3ac 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -41,8 +41,13 @@ hook Sstore _balances[KEY uint256 token][KEY address user] uint256 newValue (uin // status: not passing, because mint and burn are the same as transferring to/from // the 0 address. invariant total_supply_is_sum_of_balances(uint256 token) - sumOfBalances[token] == totalSupply(token) + balanceOf(0, token) - + sumOfBalances[token] == totalSupply(token) + { + preserved { + requireInvariant balanceOfZeroAddressIsZero(token); + } + } +/* rule total_supply_is_sum_of_balances_as_rule { uint256 token; @@ -57,7 +62,7 @@ rule total_supply_is_sum_of_balances_as_rule { assert sumOfBalances[token] == totalSupply(token) + balanceOf(0, token); } - +*/ /******************************************************************************/ /// The balance of a token for the zero address must be zero. @@ -75,6 +80,8 @@ hook Sload _balances[...] { rule held_tokens_should_exist { address user; uint256 token; + requireInvariant balanceOfZeroAddressIsZero(token); + // This assumption is safe because of total_supply_is_sum_of_balances require balanceOf(user, token) <= totalSupply(token); From b90d195c6cff5de165382e4757725403ffacbea7 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Thu, 2 Jun 2022 11:30:35 -0700 Subject: [PATCH 213/254] Added rule re burnBatch (not implemented) --- certora/specs/ERC1155Burnable.spec | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 3448e74fe..5ee2652e0 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -42,7 +42,26 @@ rule burnAmountProportionalToBalanceReduction { } /// Unimplemented rule to verify monotonicity of burnBatch. +/// Using only burnBatch, possible approach: +/// Token with smaller and larger burn amounts +/// Round one smaller burn +/// Round larger burn rule burnBatchAmountProportionalToBalanceReduction { // TODO implement rule or remove + storage beforeBurn = lastStorage; + env e; + + address holder; uint256 token; + mathint startingBalance = balanceOf(holder, token); + uint256 smallBurn; uint256 largeBurn; + require smallBurn < largeBurn; + uint256[] tokens; uint256[] smallBurnAmounts; uint256[] largeBurnAmounts; + require tokens.length == 1; require smallBurnAmounts.length == 1; require largeBurnAmounts.length == 1; + require tokens[0] == token; require smallBurnAmounts[0] == smallBurn; require largeBurnAmounts[0] == largeBurn; + + // smaller burn amount + burnBatch(e, holder, tokens, smallBurnAmounts) at beforeBurn; + mathint smallBurnBalanceChange = + assert true, "just a placeholder that should never show up"; } @@ -72,6 +91,11 @@ rule sequentialBurnsEquivalentToSingleBurnOfSum { } /// Unimplemented rule to verify additivty of burnBatch. +/// Using only burnBatch, possible approach: +/// Token with first and second burn amounts +/// Round one two sequential burns in separate transactions +/// Round two two sequential burns in the same transaction +/// Round three one burn of sum rule sequentialBatchBurnsEquivalentToSingleBurnBatchOfSum { // TODO implement rule or remove assert true, "just a placeholder that should never show up"; @@ -102,7 +126,7 @@ rule singleTokenBurnBurnBatchEquivalence { assert burnBalanceChange == burnBatchBalanceChange, "Burning a single token via burn or burnBatch must be equivalent"; -} +} /// The results of burning multiple tokens must be equivalent whether done /// separately via burn or together via burnBatch. From 27fa53bba9f16543e04b8632cef8d2e8f93cfb4b Mon Sep 17 00:00:00 2001 From: Michael George Date: Thu, 2 Jun 2022 14:37:40 -0400 Subject: [PATCH 214/254] added init_state axiom for sum of balances --- certora/specs/ERC1155Supply.spec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index 48bacb3ac..01f392e88 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -32,7 +32,9 @@ filtered { /******************************************************************************/ -ghost mapping(uint256 => mathint) sumOfBalances; +ghost mapping(uint256 => mathint) sumOfBalances { + init_state axiom forall uint256 token . sumOfBalances[token] == 0; +} hook Sstore _balances[KEY uint256 token][KEY address user] uint256 newValue (uint256 oldValue) STORAGE { sumOfBalances[token] = sumOfBalances[token] + newValue - oldValue; From d02c2ccab3bbf7df81c31e2f429a9f3ec94d1fc9 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Thu, 2 Jun 2022 14:08:29 -0700 Subject: [PATCH 215/254] Added comments throughout regarding reasoning --- certora/specs/ERC1155Burnable.spec | 32 +++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 5ee2652e0..6bf6b6062 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -41,11 +41,12 @@ rule burnAmountProportionalToBalanceReduction { "A larger burn must lead to a larger decrease in balance"; } +/// This rule not needed (because multipleTokenBurnBurnBatchEquivalence) /// Unimplemented rule to verify monotonicity of burnBatch. /// Using only burnBatch, possible approach: /// Token with smaller and larger burn amounts /// Round one smaller burn -/// Round larger burn +/// Round two larger burn rule burnBatchAmountProportionalToBalanceReduction { // TODO implement rule or remove storage beforeBurn = lastStorage; env e; @@ -60,10 +61,14 @@ rule burnBatchAmountProportionalToBalanceReduction { // TODO implement rule or r // smaller burn amount burnBatch(e, holder, tokens, smallBurnAmounts) at beforeBurn; - mathint smallBurnBalanceChange = + mathint smallBurnBalanceChange = startingBalance - balanceOf(holder, token); - assert true, - "just a placeholder that should never show up"; + // larger burn amount + burnBatch(e, holder, tokens, largeBurnAmounts) at beforeBurn; + mathint largeBurnBalanceChange = startingBalance - balanceOf(holder, token); + + assert smallBurnBalanceChange < largeBurnBalanceChange, + "A larger burn must lead to a larger decrease in balance"; } /// Two sequential burns must be equivalent to a single burn of the sum of their @@ -90,6 +95,7 @@ rule sequentialBurnsEquivalentToSingleBurnOfSum { "Sequential burns must be equivalent to a burn of their sum"; } +/// This rule not needed (because multipleTokenBurnBurnBatchEquivalence) /// Unimplemented rule to verify additivty of burnBatch. /// Using only burnBatch, possible approach: /// Token with first and second burn amounts @@ -97,8 +103,8 @@ rule sequentialBurnsEquivalentToSingleBurnOfSum { /// Round two two sequential burns in the same transaction /// Round three one burn of sum rule sequentialBatchBurnsEquivalentToSingleBurnBatchOfSum { // TODO implement rule or remove - assert true, - "just a placeholder that should never show up"; + assert false, + "TODO just a placeholder that should always show up until rule is implemented"; } /// The result of burning a single token must be equivalent whether done via @@ -139,7 +145,7 @@ rule multipleTokenBurnBurnBatchEquivalence { uint256 burnAmountA; uint256 burnAmountB; uint256 burnAmountC; uint256[] tokens; uint256[] burnAmounts; - require tokenA != tokenB; require tokenB != tokenC; require tokenC != tokenA; +// require tokenA != tokenB; require tokenB != tokenC; require tokenC != tokenA; mathint startingBalanceA = balanceOf(holder, tokenA); mathint startingBalanceB = balanceOf(holder, tokenB); @@ -152,10 +158,10 @@ rule multipleTokenBurnBurnBatchEquivalence { // burning via burn burn(e, holder, tokenA, burnAmountA) at beforeBurns; + burn(e, holder, tokenB, burnAmountB); + burn(e, holder, tokenC, burnAmountC); mathint burnBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA); - burn(e, holder, tokenB, burnAmountB) at beforeBurns; mathint burnBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB); - burn(e, holder, tokenC, burnAmountC) at beforeBurns; mathint burnBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC); // burning via burnBatch @@ -170,6 +176,14 @@ rule multipleTokenBurnBurnBatchEquivalence { "Burning multiple tokens via burn or burnBatch must be equivalent"; } +/// possible rule: +/// like singleTokenBurnBurnBatchEquivalence but for no operation +/// i.e. burnBatch on empty arrays does nothing + +/// skip frontrunning because +/// (1) needing to filter a ton of rules for f +/// (2) frontrunning before burning isn't a likely issue + /// This rule should always fail. rule sanity { method f; env e; calldataarg args; From 3eb67081f322d71ce9dc7ae632477f7fd8b8e72e Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Thu, 2 Jun 2022 14:15:20 -0700 Subject: [PATCH 216/254] Added TODO show equivalence between batch and non-batch methods --- certora/specs/ERC1155Supply.spec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index 01f392e88..867dac46f 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -30,6 +30,9 @@ filtered { "methods must not change the total supply of more than one token"; } +/// TODO possibly show equivalence between batch and non-batch methods +/// in order to leverage non-batch rules wrt batch rules + /******************************************************************************/ ghost mapping(uint256 => mathint) sumOfBalances { From f3f26e3ff350accb5fab5e0f92b1132c2ba4e9e4 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Thu, 2 Jun 2022 14:25:08 -0700 Subject: [PATCH 217/254] Modified verification scripts to include --send_only flag --- certora/scripts/verifyERC1155BurnableAll.sh | 1 + certora/scripts/verifyERC1155BurnableSpecific.sh | 1 + certora/scripts/verifyERC1155Pausable.sh | 1 + certora/scripts/verifyERC1155Supply.sh | 1 + 4 files changed, 4 insertions(+) diff --git a/certora/scripts/verifyERC1155BurnableAll.sh b/certora/scripts/verifyERC1155BurnableAll.sh index 7a812f6ce..da19cc9d7 100644 --- a/certora/scripts/verifyERC1155BurnableAll.sh +++ b/certora/scripts/verifyERC1155BurnableAll.sh @@ -6,6 +6,7 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --loop_iter 3 \ + --send_only \ --cloud \ --msg "ERC1155 Burnable verification all rules" \ No newline at end of file diff --git a/certora/scripts/verifyERC1155BurnableSpecific.sh b/certora/scripts/verifyERC1155BurnableSpecific.sh index 5f51ba4c5..bc98a86d7 100644 --- a/certora/scripts/verifyERC1155BurnableSpecific.sh +++ b/certora/scripts/verifyERC1155BurnableSpecific.sh @@ -7,6 +7,7 @@ certoraRun \ --optimistic_loop \ --loop_iter 3 \ --cloud \ + --send_only \ --rule $1 \ --msg "ERC1155 Burnable verification specific rule $1" \ No newline at end of file diff --git a/certora/scripts/verifyERC1155Pausable.sh b/certora/scripts/verifyERC1155Pausable.sh index 69a2bc527..9af2aa8c4 100755 --- a/certora/scripts/verifyERC1155Pausable.sh +++ b/certora/scripts/verifyERC1155Pausable.sh @@ -6,5 +6,6 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --loop_iter 3 \ + --send_only \ --cloud \ --msg "ERC1155 Pausable verification" diff --git a/certora/scripts/verifyERC1155Supply.sh b/certora/scripts/verifyERC1155Supply.sh index 20552f216..43f5b3797 100755 --- a/certora/scripts/verifyERC1155Supply.sh +++ b/certora/scripts/verifyERC1155Supply.sh @@ -7,4 +7,5 @@ certoraRun \ --optimistic_loop \ --loop_iter 3 \ --cloud \ + --send_only \ --msg "ERC1155 Supply verification" From 93928e3e19ea3a28cd222321ae145c3b952d57e1 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Fri, 3 Jun 2022 12:13:10 -0700 Subject: [PATCH 218/254] Added rule burnBatchOnEmptyArraysChangesNothing (passing) --- certora/specs/ERC1155Burnable.spec | 43 ++++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 6bf6b6062..0339c321a 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -95,18 +95,6 @@ rule sequentialBurnsEquivalentToSingleBurnOfSum { "Sequential burns must be equivalent to a burn of their sum"; } -/// This rule not needed (because multipleTokenBurnBurnBatchEquivalence) -/// Unimplemented rule to verify additivty of burnBatch. -/// Using only burnBatch, possible approach: -/// Token with first and second burn amounts -/// Round one two sequential burns in separate transactions -/// Round two two sequential burns in the same transaction -/// Round three one burn of sum -rule sequentialBatchBurnsEquivalentToSingleBurnBatchOfSum { // TODO implement rule or remove - assert false, - "TODO just a placeholder that should always show up until rule is implemented"; -} - /// The result of burning a single token must be equivalent whether done via /// burn or burnBatch. rule singleTokenBurnBurnBatchEquivalence { @@ -145,8 +133,6 @@ rule multipleTokenBurnBurnBatchEquivalence { uint256 burnAmountA; uint256 burnAmountB; uint256 burnAmountC; uint256[] tokens; uint256[] burnAmounts; -// require tokenA != tokenB; require tokenB != tokenC; require tokenC != tokenA; - mathint startingBalanceA = balanceOf(holder, tokenA); mathint startingBalanceB = balanceOf(holder, tokenB); mathint startingBalanceC = balanceOf(holder, tokenC); @@ -176,13 +162,30 @@ rule multipleTokenBurnBurnBatchEquivalence { "Burning multiple tokens via burn or burnBatch must be equivalent"; } -/// possible rule: -/// like singleTokenBurnBurnBatchEquivalence but for no operation -/// i.e. burnBatch on empty arrays does nothing +/// If passed empty token and burn amount arrays, burnBatch must not change +/// token balances or address permissions. +rule burnBatchOnEmptyArraysChangesNothing { + env e; -/// skip frontrunning because -/// (1) needing to filter a ton of rules for f -/// (2) frontrunning before burning isn't a likely issue + address holder; uint256 token; + address nonHolderA; address nonHolderB; + uint256 startingBalance = balanceOf(holder, token); + bool startingPermissionNonHolderA = isApprovedForAll(holder, nonHolderA); + bool startingPermissionNonHolderB = isApprovedForAll(holder, nonHolderB); + uint256[] noTokens; uint256[] noBurnAmounts; + require noTokens.length == 0; require noBurnAmounts.length == 0; + + burnBatch(e, holder, noTokens, noBurnAmounts); + uint256 endingBalance = balanceOf(holder, token); + bool endingPermissionNonHolderA = isApprovedForAll(holder, nonHolderA); + bool endingPermissionNonHolderB = isApprovedForAll(holder, nonHolderB); + + assert startingBalance == endingBalance, + "burnBatch must not change token balances if passed empty arrays"; + assert startingPermissionNonHolderA == endingPermissionNonHolderA + && startingPermissionNonHolderB == endingPermissionNonHolderB, + "burnBatch must not change account permissions if passed empty arrays"; +} /// This rule should always fail. rule sanity { From bab9528dc11b005c41fa953013e3aab57c6e6be3 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Fri, 3 Jun 2022 12:24:03 -0700 Subject: [PATCH 219/254] Added rule comments re burn method rules holding for burnBatch method --- certora/specs/ERC1155Burnable.spec | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 0339c321a..9e54d60c9 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -5,6 +5,9 @@ methods { /// If a method call reduces account balances, the caller must be either the /// holder of the account or approved to act on the holder's behalf. +/// n.b. This rule is passing for all methods except `_burn` and `_burnBatch`, +/// ordinarily internal methods that are callable by our tool only because they +/// were changed to public for the purposes of verification. rule onlyHolderOrApprovedCanReduceBalance { address holder; uint256 token; uint256 amount; uint256 balanceBefore = balanceOf(holder, token); @@ -20,6 +23,8 @@ rule onlyHolderOrApprovedCanReduceBalance { /// Burning a larger amount of a token must reduce that token's balance more /// than burning a smaller amount. +/// n.b. This rule holds for `burnBatch` as well due to rules establishing +/// appropriate equivance between `burn` and `burnBatch` methods. rule burnAmountProportionalToBalanceReduction { storage beforeBurn = lastStorage; env e; @@ -73,6 +78,8 @@ rule burnBatchAmountProportionalToBalanceReduction { // TODO implement rule or r /// Two sequential burns must be equivalent to a single burn of the sum of their /// amounts. +/// n.b. This rule holds for `burnBatch` as well due to rules establishing +/// appropriate equivance between `burn` and `burnBatch` methods. rule sequentialBurnsEquivalentToSingleBurnOfSum { storage beforeBurns = lastStorage; env e; From bdb49654c5720966ec3272588f5794efa43a71ed Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Fri, 3 Jun 2022 12:27:08 -0700 Subject: [PATCH 220/254] Deleted redundant rule burnBatchAmountProportionalToBalanceReduction --- certora/specs/ERC1155Burnable.spec | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 9e54d60c9..0408cf00f 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -46,36 +46,6 @@ rule burnAmountProportionalToBalanceReduction { "A larger burn must lead to a larger decrease in balance"; } -/// This rule not needed (because multipleTokenBurnBurnBatchEquivalence) -/// Unimplemented rule to verify monotonicity of burnBatch. -/// Using only burnBatch, possible approach: -/// Token with smaller and larger burn amounts -/// Round one smaller burn -/// Round two larger burn -rule burnBatchAmountProportionalToBalanceReduction { // TODO implement rule or remove - storage beforeBurn = lastStorage; - env e; - - address holder; uint256 token; - mathint startingBalance = balanceOf(holder, token); - uint256 smallBurn; uint256 largeBurn; - require smallBurn < largeBurn; - uint256[] tokens; uint256[] smallBurnAmounts; uint256[] largeBurnAmounts; - require tokens.length == 1; require smallBurnAmounts.length == 1; require largeBurnAmounts.length == 1; - require tokens[0] == token; require smallBurnAmounts[0] == smallBurn; require largeBurnAmounts[0] == largeBurn; - - // smaller burn amount - burnBatch(e, holder, tokens, smallBurnAmounts) at beforeBurn; - mathint smallBurnBalanceChange = startingBalance - balanceOf(holder, token); - - // larger burn amount - burnBatch(e, holder, tokens, largeBurnAmounts) at beforeBurn; - mathint largeBurnBalanceChange = startingBalance - balanceOf(holder, token); - - assert smallBurnBalanceChange < largeBurnBalanceChange, - "A larger burn must lead to a larger decrease in balance"; -} - /// Two sequential burns must be equivalent to a single burn of the sum of their /// amounts. /// n.b. This rule holds for `burnBatch` as well due to rules establishing From b10a2b8cd3d077e1bd9b47a1d53d8e46214db97d Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Fri, 3 Jun 2022 13:56:46 -0700 Subject: [PATCH 221/254] Added burn and burnBatch to filtered block of unexpectedBalanceChange --- certora/specs/ERC1155.spec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/certora/specs/ERC1155.spec b/certora/specs/ERC1155.spec index 9b5839031..bb158117d 100644 --- a/certora/specs/ERC1155.spec +++ b/certora/specs/ERC1155.spec @@ -90,7 +90,9 @@ rule unexpectedBalanceChange(method f, env e) && f.selector != _mint(address, uint256, uint256, bytes).selector && f.selector != _mintBatch(address, uint256[], uint256[], bytes).selector && f.selector != _burn(address, uint256, uint256).selector - && f.selector != _burnBatch(address, uint256[], uint256[]).selector } { + && f.selector != _burnBatch(address, uint256[], uint256[]).selector + && f.selector != burn(address, uint256, uint256).selector + && f.selector != burnBatch(address, uint256[], uint256[]).selector } { address from; uint256 id; uint256 balanceBefore = balanceOf(from, id); From 6363deaedd534485c1675177805c9c17a63587dd Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Fri, 3 Jun 2022 16:12:12 -0700 Subject: [PATCH 222/254] Changed rule onlyHolderOrApprovedCanReduceBalance to include filtered block --- certora/specs/ERC1155Burnable.spec | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 0408cf00f..23c036e81 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -5,14 +5,20 @@ methods { /// If a method call reduces account balances, the caller must be either the /// holder of the account or approved to act on the holder's behalf. -/// n.b. This rule is passing for all methods except `_burn` and `_burnBatch`, +/// n.b. This rule was passing for all methods except `_burn` and `_burnBatch`, /// ordinarily internal methods that are callable by our tool only because they -/// were changed to public for the purposes of verification. -rule onlyHolderOrApprovedCanReduceBalance { +/// were changed to public for the purposes of prior verification. Filtered here +/// since they are not generally callable. +rule onlyHolderOrApprovedCanReduceBalance(method f) +filtered { + f -> f.selector != _burn(address,uint256,uint256).selector + && f.selector != _burnBatch(address,uint256[],uint256[]).selector +} +{ address holder; uint256 token; uint256 amount; uint256 balanceBefore = balanceOf(holder, token); - method f; env e; calldataarg args; + env e; calldataarg args; f(e, args); uint256 balanceAfter = balanceOf(holder, token); @@ -46,9 +52,10 @@ rule burnAmountProportionalToBalanceReduction { "A larger burn must lead to a larger decrease in balance"; } -/// Two sequential burns must be equivalent to a single burn of the sum of their +/// @decription Two sequential burns must be equivalent to a single burn of the sum of their /// amounts. -/// n.b. This rule holds for `burnBatch` as well due to rules establishing +/// @formula Date: Fri, 3 Jun 2022 16:18:24 -0700 Subject: [PATCH 223/254] final initializable spec modulo extra natspec style comments --- .../harnesses/InitializableBasicHarness.sol | 14 +- .../harnesses/InitializableComplexHarness.sol | 12 +- certora/harnesses/ReinitializersHarness.sol | 18 -- certora/specs/Initializable.spec | 182 +++++++----------- 4 files changed, 78 insertions(+), 148 deletions(-) delete mode 100644 certora/harnesses/ReinitializersHarness.sol diff --git a/certora/harnesses/InitializableBasicHarness.sol b/certora/harnesses/InitializableBasicHarness.sol index b12b26017..94495ea54 100644 --- a/certora/harnesses/InitializableBasicHarness.sol +++ b/certora/harnesses/InitializableBasicHarness.sol @@ -31,30 +31,30 @@ contract InitializableBasicHarness is Initializable { val = _val; } - // Versionede return functions for testing + // Versioned return functions for testing function returnsV1() public view version1 returns(uint256) { - return val/2; + return val; } function returnsVN(uint8 n) public view versionN(n) returns(uint256) { - return val/(n+1); + return val; } function returnsAV1() public view version1 returns(uint256) { - return a/2; + return a; } function returnsAVN(uint8 n) public view versionN(n) returns(uint256) { - return a/(n+1); + return a; } function returnsBV1() public view version1 returns(uint256) { - return b/2; + return b; } function returnsBVN(uint8 n) public view versionN(n) returns(uint256) { - return b/(n+1); + return b; } // Harness // diff --git a/certora/harnesses/InitializableComplexHarness.sol b/certora/harnesses/InitializableComplexHarness.sol index 3e4c0d4b0..e76af78bc 100644 --- a/certora/harnesses/InitializableComplexHarness.sol +++ b/certora/harnesses/InitializableComplexHarness.sol @@ -20,11 +20,11 @@ contract InitializableA is Initializable { } function returnsAV1() public view version1 returns(uint256) { - return a/2; + return a; } function returnsAVN(uint8 n) public view versionN(n) returns(uint256) { - return a/(n+1); + return a; } } @@ -35,11 +35,11 @@ contract InitializableB is Initializable, InitializableA { } function returnsBV1() public view version1 returns(uint256) { - return b/2; + return b; } function returnsBVN(uint8 n) public view versionN(n) returns(uint256) { - return b/(n+1); + return b; } } @@ -59,11 +59,11 @@ contract InitializableComplexHarness is Initializable, InitializableB { } function returnsV1() public view version1 returns(uint256) { - return val/2; + return val; } function returnsVN(uint8 n) public view versionN(n) returns(uint256) { - return val/(n+1); + return val; } // Harness // diff --git a/certora/harnesses/ReinitializersHarness.sol b/certora/harnesses/ReinitializersHarness.sol deleted file mode 100644 index 9bf07450b..000000000 --- a/certora/harnesses/ReinitializersHarness.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol"; - -contract MyTokenV1 is Initializable, ERC20Upgradeable { - function initialize() initializer public { - __ERC20_init("MyToken", "MTK"); - } -} - -contract MyTokenV2 is Initializable, ERC20Upgradeable, ERC20PermitUpgradeable { - function initializeV2() reinitializer(2) public { - __ERC20Permit_init("MyToken"); - } -} \ No newline at end of file diff --git a/certora/specs/Initializable.spec b/certora/specs/Initializable.spec index d24d62aa0..063baee82 100644 --- a/certora/specs/Initializable.spec +++ b/certora/specs/Initializable.spec @@ -16,6 +16,11 @@ methods { val() returns uint256 envfree } + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// Definitions ///////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + definition isUninitialized() returns bool = initialized() == 0; definition isInitialized() returns bool = initialized() > 0; @@ -26,89 +31,49 @@ definition isReinitialized() returns bool = initialized() > 1; definition isDisabled() returns bool = initialized() == 255; -/* -idea : need extensive harness to test upgrading with reinitialize -Setup: +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// Invariants ///////////////////////////////// +////////////////////////////////////////////////////////////////////////////// -contracts A, B, C +/// @description A contract must only ever be in an initializing state while in the middle of a transaction execution. +invariant notInitializing() + !initializing(), "contract must not be initializing" -Scenario where B extends A and both are being initialized -Potentially need to summarize ‘isContract’ +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// Rules ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// -Multiple Versioning within one contract - - - -Test Cases: - -Simple - -1 contract with initialize and reinitialize methods (v 1-3) - -Multiple Inheritance - -Contracts A, B, C - -C Inherits from B, which Inherits from A - -Properties - -/// You can only initialize once -/// two rules prove initialize is equivalent to reinitialize(1) property (?) -- what other effects from these calls? -// if reinitialize(1) is called successfully, _initialized is set to 1 -// if initialize is called successfully, _initialized is set to 1 -/// disabled stays disabled -// invariant -// or rule? -/// n increase iff reinitialize succeeds -// n only increases -// reinitialize called => n increases -/// You can reinitialize(n) iff n > _initialized && initialized < 256 (maxuint8) -// <= -// => -/// can only cal v1 function in v1 - -Question: can we handle reentrancy? -*/ - -// You can only initialize once +/// @description An initializeable contract with a function that inherits the initializer modifier must be initializable only once" rule initOnce() { uint256 val; uint256 a; uint256 b; require isInitialized(); initialize@withrevert(val, a, b); - assert lastReverted; + assert lastReverted, "contract must only be initialized once"; } -/// two rules prove initialize is equivalent to reinitialize(1) property (?) -- what other effects from these calls? - -// if reinitialize(1) is called successfully, _initialized is set to 1 -rule basicReinitializeEffects() { +/// @description Successfully calling reinitialize() with a version value of 1 must result in _initialized being set to 1. +rule reinitializeEffects { uint256 val; uint256 a; uint256 b; reinitialize(val, a, b, 1); - assert isInitializedOnce(); + assert isInitializedOnce(), "reinitialize(1) must set _initialized to 1"; } -// if initialize is called successfully, _initialized is set to 1 -rule initalizeEffects(method f) { - env e; calldataarg args; +/// @description Successfully calling initalize() must result in _initialized being set to 1. +/// @note We assume initialize() and reinitialize(1) are equivalent if this rule and the above rule, reinitalizeEffects, both pass. +rule initalizeEffects { + uint256 val; uint256 a; uint256 b; - f(e, args); + initialize(val, a, b); - assert f.selector == initialize(uint256, uint256, uint256).selector => isInitializedOnce(); + assert isInitializedOnce(), "initialize() must set _initialized to 1"; } -/// disabled stays disabled - -// invariant -invariant disabledStaysDisabledInv() - isDisabled() => isDisabled() - -// or rule? +/// @description A disabled initializable contract must always stay disabled. rule disabledStaysDisabled(method f) { env e; calldataarg args; @@ -116,96 +81,79 @@ rule disabledStaysDisabled(method f) { f(e, args); bool disabledAfter = isDisabled(); - assert disabledBefore => disabledAfter; + assert disabledBefore => disabledAfter, "a disabled initializer must stay disabled"; } -/// n increase iff reinitialize succeeds - -// n only increases +/// @description The variable _initialized must not decrease. rule increasingInitialized(method f) { env e; calldataarg args; uint8 initBefore = initialized(); f(e, args); uint8 initAfter = initialized(); - assert initBefore <= initAfter; + assert initBefore <= initAfter, "_initialized must only increase"; } -// reinitialize called => n increases -rule reinitializeIncreasesInit() { +/// @description If reinitialize(...) was called successfuly, then the variable _initialized must increases. +rule reinitializeIncreasesInit { uint256 val; uint8 n; uint256 a; uint256 b; uint8 initBefore = initialized(); reinitialize(val, a, b, n); uint8 initAfter = initialized(); - assert initAfter > initBefore; + assert initAfter > initBefore, "calling reinitialize must increase _initialized"; } -/// You can reinitialize(n) iff n > _initialized && initialized < 256 (maxuint8) +/// @description Reinitialize(n) must be callable if the contract is not in an _initializing state and n is greater than _initialized and less than 255 +rule reinitializeLiveness { + uint256 val; uint8 n; uint256 a; uint256 b; -// <= -rule reinitializeLiveness() { - env e; uint256 val; uint8 n; uint256 a; uint256 b; - - require !initializing(); + requireInvariant notInitializing(); uint8 initVal = initialized(); reinitialize@withrevert(val, a, b, n); - assert n > initVal && initVal < 255 => !lastReverted; + assert n > initVal => !lastReverted, "reinitialize(n) call must succeed if n was greater than _initialized"; } -// => -rule reinitializeRule() { - env e; calldataarg args; uint256 val; uint8 n; uint256 a; uint256 b; +/// @description If reinitialize(n) was called successfully then n was greater than _initialized. +rule reinitializeRule { + uint256 val; uint8 n; uint256 a; uint256 b; uint8 initBefore = initialized(); reinitialize(val, a, b, n); - uint8 initAfter = initialized(); - assert initAfter > initBefore => n > initBefore; + assert n > initBefore; } -// can only call v1 functions in v1 -rule initVersionCheck() { - env e; calldataarg args; uint256 val; uint256 a; uint256 b; uint8 n; +/// @description Functions implemented in the parent contract that require _initialized to be a certain value are only callable when it is that value. +rule reinitVersionCheckParent { + uint8 n; - require n != 1; - - initialize(val, a, b); - assert returnsV1() == val / 2; - assert returnsAV1() == a / 2; - assert returnsBV1() == b / 2; - returnsVN@withrevert(n); - assert lastReverted; - returnsAVN@withrevert(n); - assert lastReverted; - returnsBVN@withrevert(n); - assert lastReverted; + returnsVN(n); + assert initialized() == n, "parent contract's version n functions must only be callable in version n"; } -// can only call V_n functions in V_n -rule reinitVersionCheck() { - env e; calldataarg args; uint256 val; uint256 a; uint256 b; uint8 n; uint8 m; +/// @description Functions implemented in the child contract that require _initialized to be a certain value are only callable when it is that value. +rule reinitVersionCheckChild { + uint8 n; - require n != m; + returnsAVN(n); + assert initialized() == n, "child contract's version n functions must only be callable in version n"; +} + +/// @description Functions implemented in the grandchild contract that require _initialized to be a certain value are only callable when it is that value. +rule reinitVersionCheckGrandchild { + uint8 n; + + returnsBVN(n); + assert initialized() == n, "gransdchild contract's version n functions must only be callable in version n"; +} + +// @description Calling parent initalizer function must initialize all child contracts. +rule inheritanceCheck { + uint256 val; uint8 n; uint256 a; uint256 b; reinitialize(val, a, b, n); - assert returnsVN(n) == val / (n + 1); - assert returnsAVN(n) == a / (n + 1); - assert returnsBVN(n) == b / (n + 1); - - returnsVN@withrevert(m); - assert lastReverted; - returnsAVN@withrevert(m); - assert lastReverted; - returnsBVN@withrevert(m); - assert lastReverted; -} - -rule inheritanceCheck() { - env e; calldataarg args; uint256 val; uint8 n; uint256 a; uint256 b; - - initialize(val, a, b); - assert val() == val && a() == a && b() == b; + assert val() == val && a() == a && b() == b, "all child contract values must be initialized"; } From 69d9ebfcdf5a14f02c1469d5ee7f6e04f58b4655 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 6 Jun 2022 10:36:26 -0700 Subject: [PATCH 224/254] Added modified scripts to run previous ERC1155 spec --- certora/scripts/verifyERC1155All.sh | 11 +++++++++++ certora/scripts/verifyERC1155Specific.sh | 13 +++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 certora/scripts/verifyERC1155All.sh create mode 100644 certora/scripts/verifyERC1155Specific.sh diff --git a/certora/scripts/verifyERC1155All.sh b/certora/scripts/verifyERC1155All.sh new file mode 100644 index 000000000..fbde87f24 --- /dev/null +++ b/certora/scripts/verifyERC1155All.sh @@ -0,0 +1,11 @@ +make -C certora munged + +certoraRun \ + certora/munged/token/ERC1155/ERC1155.sol \ + --verify ERC1155:certora/specs/ERC1155.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --cloud \ + --send_only \ + --msg "ERC1155 Burnable verification all rules" \ No newline at end of file diff --git a/certora/scripts/verifyERC1155Specific.sh b/certora/scripts/verifyERC1155Specific.sh new file mode 100644 index 000000000..438d527ca --- /dev/null +++ b/certora/scripts/verifyERC1155Specific.sh @@ -0,0 +1,13 @@ +make -C certora munged + +certoraRun \ + certora/munged/token/ERC1155/ERC1155.sol \ + --verify ERC1155:certora/specs/ERC1155.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --cloud \ + --send_only \ + --rule $1 \ + --msg "ERC1155 Burnable verification specific rule $1" + \ No newline at end of file From e3341255b284af34225a9d7b4921dc5853b0e81d Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 6 Jun 2022 11:24:16 -0700 Subject: [PATCH 225/254] Added rule skeletons for equivalence rules --- certora/specs/ERC1155Supply.spec | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index 867dac46f..8956a537a 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -33,6 +33,16 @@ filtered { /// TODO possibly show equivalence between batch and non-batch methods /// in order to leverage non-batch rules wrt batch rules +rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { + assert false, + "TODO implement this rule using burn version as structural model" +} + +rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence { + assert false, + "TODO implement this rule using burn version as structural model" +} + /******************************************************************************/ ghost mapping(uint256 => mathint) sumOfBalances { From 5a7cc50974540d9f24c9adb64092233b10dd216c Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 6 Jun 2022 11:25:48 -0700 Subject: [PATCH 226/254] Modified burnable verification script to follow script naming convention --- .../{verifyERC1155BurnableAll.sh => verifyERC1155Burnable.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename certora/scripts/{verifyERC1155BurnableAll.sh => verifyERC1155Burnable.sh} (100%) diff --git a/certora/scripts/verifyERC1155BurnableAll.sh b/certora/scripts/verifyERC1155Burnable.sh similarity index 100% rename from certora/scripts/verifyERC1155BurnableAll.sh rename to certora/scripts/verifyERC1155Burnable.sh From 990fd18c21d5b68195ed2043ce1345f851ac5218 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Mon, 6 Jun 2022 11:25:54 -0700 Subject: [PATCH 227/254] make ERC1155Harness, set up script, and undo munging for ERC1155 --- certora/applyHarness.patch | 266 +++++++++++++------ certora/harnesses/ERC1155/ERC1155Harness.sol | 39 +++ certora/scripts/old/verifyERC1155.sh | 10 +- certora/specs/ERC1155.spec | 70 +++-- 4 files changed, 270 insertions(+), 115 deletions(-) create mode 100644 certora/harnesses/ERC1155/ERC1155Harness.sol diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 240711aff..99fb5d3e7 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,12 +1,12 @@ diff -ruN .gitignore .gitignore --- .gitignore 1969-12-31 16:00:00.000000000 -0800 -+++ .gitignore 2022-06-01 15:28:29.000000000 -0700 ++++ .gitignore 2022-06-06 11:21:40.000000000 -0700 @@ -0,0 +1,2 @@ +* +!.gitignore diff -ruN access/AccessControl.sol access/AccessControl.sol ---- access/AccessControl.sol 2022-05-25 09:38:35.000000000 -0700 -+++ access/AccessControl.sol 2022-06-01 15:28:29.000000000 -0700 +--- access/AccessControl.sol 2022-06-06 10:42:37.000000000 -0700 ++++ access/AccessControl.sol 2022-06-06 11:21:40.000000000 -0700 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -17,8 +17,8 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol } diff -ruN governance/Governor.sol governance/Governor.sol ---- governance/Governor.sol 2022-05-25 09:38:35.000000000 -0700 -+++ governance/Governor.sol 2022-06-01 15:28:29.000000000 -0700 +--- governance/Governor.sol 2022-06-06 10:42:37.000000000 -0700 ++++ governance/Governor.sol 2022-06-06 11:21:40.000000000 -0700 @@ -44,7 +44,7 @@ string private _name; @@ -29,8 +29,8 @@ diff -ruN governance/Governor.sol governance/Governor.sol // This queue keeps track of the governor operating on itself. Calls to functions protected by the // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol ---- governance/TimelockController.sol 2022-05-25 09:38:35.000000000 -0700 -+++ governance/TimelockController.sol 2022-06-01 15:28:29.000000000 -0700 +--- governance/TimelockController.sol 2022-06-06 10:42:37.000000000 -0700 ++++ governance/TimelockController.sol 2022-06-06 11:21:40.000000000 -0700 @@ -28,10 +28,10 @@ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); @@ -45,8 +45,8 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol /** * @dev Emitted when a call is scheduled as part of operation `id`. diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol ---- governance/extensions/GovernorCountingSimple.sol 2022-05-25 09:38:35.000000000 -0700 -+++ governance/extensions/GovernorCountingSimple.sol 2022-06-01 15:28:29.000000000 -0700 +--- governance/extensions/GovernorCountingSimple.sol 2022-06-06 10:42:37.000000000 -0700 ++++ governance/extensions/GovernorCountingSimple.sol 2022-06-06 11:21:40.000000000 -0700 @@ -27,7 +27,7 @@ mapping(address => bool) hasVoted; } @@ -57,8 +57,8 @@ diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions /** * @dev See {IGovernor-COUNTING_MODE}. diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol ---- governance/extensions/GovernorPreventLateQuorum.sol 2022-05-25 09:38:35.000000000 -0700 -+++ governance/extensions/GovernorPreventLateQuorum.sol 2022-06-01 15:28:29.000000000 -0700 +--- governance/extensions/GovernorPreventLateQuorum.sol 2022-06-06 10:42:37.000000000 -0700 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-06-06 11:21:40.000000000 -0700 @@ -21,8 +21,8 @@ using SafeCast for uint256; using Timers for Timers.BlockNumber; @@ -71,8 +71,8 @@ diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensi /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol ---- governance/utils/Votes.sol 2022-05-25 09:38:35.000000000 -0700 -+++ governance/utils/Votes.sol 2022-06-01 15:28:29.000000000 -0700 +--- governance/utils/Votes.sol 2022-06-06 10:42:37.000000000 -0700 ++++ governance/utils/Votes.sol 2022-06-06 11:21:40.000000000 -0700 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -146,8 +146,8 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public } diff -ruN proxy/utils/Initializable.sol proxy/utils/Initializable.sol ---- proxy/utils/Initializable.sol 2022-05-25 14:01:12.000000000 -0700 -+++ proxy/utils/Initializable.sol 2022-06-01 17:10:12.000000000 -0700 +--- proxy/utils/Initializable.sol 2022-06-06 10:42:37.000000000 -0700 ++++ proxy/utils/Initializable.sol 2022-06-06 11:21:40.000000000 -0700 @@ -59,12 +59,12 @@ * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool @@ -163,54 +163,172 @@ diff -ruN proxy/utils/Initializable.sol proxy/utils/Initializable.sol /** * @dev Triggered when the contract has been initialized or reinitialized. -@@ -130,7 +130,7 @@ - _setInitializedVersion(type(uint8).max); - } - -- function _setInitializedVersion(uint8 version) private returns (bool) { -+ function _setInitializedVersion(uint8 version) internal returns (bool) { - // If the contract is initializing we ignore whether _initialized is set in order to support multiple - // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level - // of initializers, because in other contexts the contract may have been reentered. +diff -ruN proxy/utils/Initializable.sol.orig proxy/utils/Initializable.sol.orig +--- proxy/utils/Initializable.sol.orig 1969-12-31 16:00:00.000000000 -0800 ++++ proxy/utils/Initializable.sol.orig 2022-06-06 11:21:40.000000000 -0700 +@@ -0,0 +1,138 @@ ++// SPDX-License-Identifier: MIT ++// OpenZeppelin Contracts (last updated v4.6.0) (proxy/utils/Initializable.sol) ++ ++pragma solidity ^0.8.2; ++ ++import "../../utils/Address.sol"; ++ ++/** ++ * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed ++ * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an ++ * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer ++ * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. ++ * ++ * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be ++ * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in ++ * case an upgrade adds a module that needs to be initialized. ++ * ++ * For example: ++ * ++ * [.hljs-theme-light.nopadding] ++ * ``` ++ * contract MyToken is ERC20Upgradeable { ++ * function initialize() initializer public { ++ * __ERC20_init("MyToken", "MTK"); ++ * } ++ * } ++ * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { ++ * function initializeV2() reinitializer(2) public { ++ * __ERC20Permit_init("MyToken"); ++ * } ++ * } ++ * ``` ++ * ++ * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as ++ * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. ++ * ++ * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure ++ * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. ++ * ++ * [CAUTION] ++ * ==== ++ * Avoid leaving a contract uninitialized. ++ * ++ * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation ++ * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke ++ * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: ++ * ++ * [.hljs-theme-light.nopadding] ++ * ``` ++ * /// @custom:oz-upgrades-unsafe-allow constructor ++ * constructor() { ++ * _disableInitializers(); ++ * } ++ * ``` ++ * ==== ++ */ ++abstract contract Initializable { ++ /** ++ * @dev Indicates that the contract has been initialized. ++ * @custom:oz-retyped-from bool ++ */ ++ uint8 private _initialized; ++ ++ /** ++ * @dev Indicates that the contract is in the process of being initialized. ++ */ ++ bool private _initializing; ++ ++ /** ++ * @dev Triggered when the contract has been initialized or reinitialized. ++ */ ++ event Initialized(uint8 version); ++ ++ /** ++ * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, ++ * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. ++ */ ++ modifier initializer() { ++ bool isTopLevelCall = !_initializing; ++ require( ++ (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), ++ "Initializable: contract is already initialized" ++ ); ++ _initialized = 1; ++ if (isTopLevelCall) { ++ _initializing = true; ++ } ++ _; ++ if (isTopLevelCall) { ++ _initializing = false; ++ emit Initialized(1); ++ } ++ } ++ ++ /** ++ * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the ++ * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be ++ * used to initialize parent contracts. ++ * ++ * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original ++ * initialization step. This is essential to configure modules that are added through upgrades and that require ++ * initialization. ++ * ++ * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in ++ * a contract, executing them in the right order is up to the developer or operator. ++ */ ++ modifier reinitializer(uint8 version) { ++ require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); ++ _initialized = version; ++ _initializing = true; ++ _; ++ _initializing = false; ++ emit Initialized(version); ++ } ++ ++ /** ++ * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the ++ * {initializer} and {reinitializer} modifiers, directly or indirectly. ++ */ ++ modifier onlyInitializing() { ++ require(_initializing, "Initializable: contract is not initializing"); ++ _; ++ } ++ ++ /** ++ * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. ++ * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized ++ * to any version. It is recommended to use this to lock implementation contracts that are designed to be called ++ * through proxies. ++ */ ++ function _disableInitializers() internal virtual { ++ require(!_initializing, "Initializable: contract is initializing"); ++ if (_initialized < type(uint8).max) { ++ _initialized = type(uint8).max; ++ emit Initialized(type(uint8).max); ++ } ++ } ++} +diff -ruN proxy/utils/Initializable.sol.rej proxy/utils/Initializable.sol.rej +--- proxy/utils/Initializable.sol.rej 1969-12-31 16:00:00.000000000 -0800 ++++ proxy/utils/Initializable.sol.rej 2022-06-06 11:21:40.000000000 -0700 +@@ -0,0 +1,17 @@ ++*************** ++*** 130,136 **** ++ _setInitializedVersion(type(uint8).max); ++ } ++ ++- function _setInitializedVersion(uint8 version) private returns (bool) { ++ // If the contract is initializing we ignore whether _initialized is set in order to support multiple ++ // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level ++ // of initializers, because in other contexts the contract may have been reentered. ++--- 130,136 ---- ++ _setInitializedVersion(type(uint8).max); ++ } ++ +++ function _setInitializedVersion(uint8 version) internal returns (bool) { ++ // If the contract is initializing we ignore whether _initialized is set in order to support multiple ++ // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level ++ // of initializers, because in other contexts the contract may have been reentered. diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol ---- token/ERC1155/ERC1155.sol 2022-05-25 09:38:35.000000000 -0700 -+++ token/ERC1155/ERC1155.sol 2022-06-01 15:28:29.000000000 -0700 -@@ -268,7 +268,7 @@ - uint256 id, - uint256 amount, - bytes memory data -- ) internal virtual { -+ ) public virtual { // HARNESS: internal -> public - require(to != address(0), "ERC1155: mint to the zero address"); - - address operator = _msgSender(); -@@ -301,7 +301,7 @@ - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data -- ) internal virtual { -+ ) public virtual { // HARNESS: internal -> public - require(to != address(0), "ERC1155: mint to the zero address"); - require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); - -@@ -334,7 +334,7 @@ - address from, - uint256 id, - uint256 amount -- ) internal virtual { -+ ) public virtual { // HARNESS: internal -> public - require(from != address(0), "ERC1155: burn from the zero address"); - - address operator = _msgSender(); -@@ -367,7 +367,7 @@ - address from, - uint256[] memory ids, - uint256[] memory amounts -- ) internal virtual { -+ ) public virtual { // HARNESS: internal -> public - require(from != address(0), "ERC1155: burn from the zero address"); - require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); - +--- token/ERC1155/ERC1155.sol 2022-06-06 10:42:37.000000000 -0700 ++++ token/ERC1155/ERC1155.sol 2022-06-06 11:23:46.000000000 -0700 @@ -471,7 +471,7 @@ uint256 id, uint256 amount, @@ -230,8 +348,8 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol ---- token/ERC20/ERC20.sol 2022-05-25 09:38:35.000000000 -0700 -+++ token/ERC20/ERC20.sol 2022-06-01 15:28:29.000000000 -0700 +--- token/ERC20/ERC20.sol 2022-06-06 10:42:37.000000000 -0700 ++++ token/ERC20/ERC20.sol 2022-06-06 11:21:40.000000000 -0700 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -242,8 +360,8 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol _beforeTokenTransfer(account, address(0), amount); diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol ---- token/ERC20/extensions/ERC20FlashMint.sol 2022-05-25 09:38:35.000000000 -0700 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-06-01 15:28:29.000000000 -0700 +--- token/ERC20/extensions/ERC20FlashMint.sol 2022-06-06 10:42:37.000000000 -0700 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-06-06 11:21:40.000000000 -0700 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -258,8 +376,8 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 * @dev Returns the receiver address of the flash fee. By default this * implementation returns the address(0) which means the fee amount will be burnt. diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol ---- token/ERC20/extensions/ERC20Votes.sol 2022-05-06 13:43:21.000000000 -0700 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-06-01 15:28:29.000000000 -0700 +--- token/ERC20/extensions/ERC20Votes.sol 2022-06-06 10:42:37.000000000 -0700 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-06-06 11:21:40.000000000 -0700 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -281,8 +399,8 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol ---- token/ERC20/extensions/ERC20Wrapper.sol 2022-05-25 09:38:35.000000000 -0700 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-06-01 15:28:29.000000000 -0700 +--- token/ERC20/extensions/ERC20Wrapper.sol 2022-06-06 10:42:37.000000000 -0700 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-06-06 11:21:40.000000000 -0700 @@ -55,7 +55,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -293,8 +411,8 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr _mint(account, value); return value; diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol ---- token/ERC721/extensions/draft-ERC721Votes.sol 2022-05-25 09:38:35.000000000 -0700 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-06-01 15:28:29.000000000 -0700 +--- token/ERC721/extensions/draft-ERC721Votes.sol 2022-06-06 10:42:37.000000000 -0700 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-06-06 11:21:40.000000000 -0700 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. @@ -305,8 +423,8 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ } } diff -ruN utils/Address.sol utils/Address.sol ---- utils/Address.sol 2022-05-25 09:38:35.000000000 -0700 -+++ utils/Address.sol 2022-06-01 15:28:29.000000000 -0700 +--- utils/Address.sol 2022-06-06 10:42:37.000000000 -0700 ++++ utils/Address.sol 2022-06-06 11:21:40.000000000 -0700 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage diff --git a/certora/harnesses/ERC1155/ERC1155Harness.sol b/certora/harnesses/ERC1155/ERC1155Harness.sol new file mode 100644 index 000000000..94581511c --- /dev/null +++ b/certora/harnesses/ERC1155/ERC1155Harness.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; +import "../../munged/token/ERC1155/ERC1155.sol"; + +contract ERC1155Harness is ERC1155 { + + constructor(string memory uri_) + ERC1155(uri_) + {} + + function burn( address from, uint256 id, uint256 amount) public virtual { + _burn(from, id, amount); + } + function burnBatch( + address from, + uint256[] memory ids, + uint256[] memory amounts + ) public virtual { + _burnBatch(from, ids, amounts); + } + + function mint( + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual { + _mint(to, id, amount, data); + } + + function mintBatch( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual { + _mintBatch(to, ids, amounts, data); + } +} \ No newline at end of file diff --git a/certora/scripts/old/verifyERC1155.sh b/certora/scripts/old/verifyERC1155.sh index 84c59fc47..d60ac9cd5 100644 --- a/certora/scripts/old/verifyERC1155.sh +++ b/certora/scripts/old/verifyERC1155.sh @@ -1,9 +1,9 @@ certoraRun \ - certora/munged/token/ERC1155/ERC1155.sol \ - --verify ERC1155:certora/specs/ERC1155.spec \ - --solc solc8.2 \ + certora/harnesses/ERC1155/ERC1155Harness.sol \ + --verify ERC1155Harness:certora/specs/ERC1155.spec \ + --solc solc \ --optimistic_loop \ --loop_iter 3 \ - --cloud \ - --msg "ERC1155 verification" + --send_only \ + --msg "ERC1155" \ No newline at end of file diff --git a/certora/specs/ERC1155.spec b/certora/specs/ERC1155.spec index bb158117d..df8461f98 100644 --- a/certora/specs/ERC1155.spec +++ b/certora/specs/ERC1155.spec @@ -8,10 +8,10 @@ methods { setApprovalForAll(address, bool) safeTransferFrom(address, address, uint256, uint256, bytes) safeBatchTransferFrom(address, address, uint256[], uint256[], bytes) - _mint(address, uint256, uint256, bytes) - _mintBatch(address, uint256[], uint256[], bytes) - _burn(address, uint256, uint256) - _burnBatch(address, uint256[], uint256[]) + mint(address, uint256, uint256, bytes) + mintBatch(address, uint256[], uint256[], bytes) + burn(address, uint256, uint256) + burnBatch(address, uint256[], uint256[]) } @@ -87,10 +87,8 @@ rule onlyOneAllowanceChange(method f, env e) { rule unexpectedBalanceChange(method f, env e) filtered { f -> f.selector != safeTransferFrom(address, address, uint256, uint256, bytes).selector && f.selector != safeBatchTransferFrom(address, address, uint256[], uint256[], bytes).selector - && f.selector != _mint(address, uint256, uint256, bytes).selector - && f.selector != _mintBatch(address, uint256[], uint256[], bytes).selector - && f.selector != _burn(address, uint256, uint256).selector - && f.selector != _burnBatch(address, uint256[], uint256[]).selector + && f.selector != mint(address, uint256, uint256, bytes).selector + && f.selector != mintBatch(address, uint256[], uint256[], bytes).selector && f.selector != burn(address, uint256, uint256).selector && f.selector != burnBatch(address, uint256[], uint256[]).selector } { address from; uint256 id; @@ -425,19 +423,19 @@ rule noTransferBatchEffectOnApproval(env e){ // STATUS - verified -// Additivity of _mint: _mint(a); _mint(b) has same effect as _mint(a+b) +// Additivity of mint: mint(a); mint(b) has same effect as mint(a+b) rule mintAdditivity(env e){ address to; uint256 id; uint256 amount; uint256 amount1; uint256 amount2; bytes data; require amount == amount1 + amount2; storage initialStorage = lastStorage; - _mint(e, to, id, amount, data); + mint(e, to, id, amount, data); uint256 balanceAfterSingleTransaction = balanceOf(to, id); - _mint(e, to, id, amount1, data) at initialStorage; - _mint(e, to, id, amount2, data); + mint(e, to, id, amount1, data) at initialStorage; + mint(e, to, id, amount2, data); uint256 balanceAfterDoubleTransaction = balanceOf(to, id); @@ -446,25 +444,25 @@ rule mintAdditivity(env e){ // STATUS - verified -// Chech that `_mint()` revertes in planned scenario(s) (only if `to` is 0) +// Chech that `mint()` revertes in planned scenario(s) (only if `to` is 0) rule mintRevertCases(env e){ address to; uint256 id; uint256 amount; bytes data; - _mint@withrevert(e, to, id, amount, data); + mint@withrevert(e, to, id, amount, data); assert to == 0 => lastReverted, "Should revert"; } // STATUS - verified -// Chech that `_mintBatch()` revertes in planned scenario(s) (only if `to` is 0 or arrays have different length) +// Chech that `mintBatch()` revertes in planned scenario(s) (only if `to` is 0 or arrays have different length) rule mintBatchRevertCases(env e){ address to; uint256[] ids; uint256[] amounts; bytes data; require ids.length < 1000000000; require amounts.length < 1000000000; - _mintBatch@withrevert(e, to, ids, amounts, data); + mintBatch@withrevert(e, to, ids, amounts, data); assert (ids.length != amounts.length || to == 0) => lastReverted, "Should revert"; } @@ -477,7 +475,7 @@ rule mintCorrectWork(env e){ uint256 otherBalanceBefore = balanceOf(to, id); - _mint(e, to, id, amount, data); + mint(e, to, id, amount, data); uint256 otherBalanceAfter = balanceOf(to, id); @@ -505,7 +503,7 @@ rule mintBatchCorrectWork(env e){ uint256 otherBalanceBefore2 = balanceOf(to, id2); uint256 otherBalanceBefore3 = balanceOf(to, id3); - _mintBatch(e, to, ids, amounts, data); + mintBatch(e, to, ids, amounts, data); uint256 otherBalanceAfter1 = balanceOf(to, id1); uint256 otherBalanceAfter2 = balanceOf(to, id2); @@ -525,7 +523,7 @@ rule cantMintMoreSingle(env e){ require to_mathint(balanceOf(to, id) + amount) > max_uint256; - _mint@withrevert(e, to, id, amount, data); + mint@withrevert(e, to, id, amount, data); assert lastReverted, "Don't be too greedy!"; } @@ -549,21 +547,21 @@ rule cantMintMoreBatch(env e){ || to_mathint(balanceOf(to, id2) + amount2) > max_uint256 || to_mathint(balanceOf(to, id3) + amount3) > max_uint256; - _mintBatch@withrevert(e, to, ids, amounts, data); + mintBatch@withrevert(e, to, ids, amounts, data); assert lastReverted, "Don't be too greedy!"; } // STATUS - verified -// `_mint()` changes only `to` balance +// `mint()` changes only `to` balance rule cantMintOtherBalances(env e){ address to; uint256 id; uint256 amount; bytes data; address other; uint256 otherBalanceBefore = balanceOf(other, id); - _mint(e, to, id, amount, data); + mint(e, to, id, amount, data); uint256 otherBalanceAfter = balanceOf(other, id); @@ -584,7 +582,7 @@ rule cantMintBatchOtherBalances(env e){ uint256 otherBalanceBefore2 = balanceOf(other, id2); uint256 otherBalanceBefore3 = balanceOf(other, id3); - _mintBatch(e, to, ids, amounts, data); + mintBatch(e, to, ids, amounts, data); uint256 otherBalanceAfter1 = balanceOf(other, id1); uint256 otherBalanceAfter2 = balanceOf(other, id2); @@ -603,19 +601,19 @@ rule cantMintBatchOtherBalances(env e){ // STATUS - verified -// Additivity of _burn: _burn(a); _burn(b) has same effect as _burn(a+b) +// Additivity of burn: burn(a); burn(b) has same effect as burn(a+b) rule burnAdditivity(env e){ address from; uint256 id; uint256 amount; uint256 amount1; uint256 amount2; require amount == amount1 + amount2; storage initialStorage = lastStorage; - _burn(e, from, id, amount); + burn(e, from, id, amount); uint256 balanceAfterSingleTransaction = balanceOf(from, id); - _burn(e, from, id, amount1) at initialStorage; - _burn(e, from, id, amount2); + burn(e, from, id, amount1) at initialStorage; + burn(e, from, id, amount2); uint256 balanceAfterDoubleTransaction = balanceOf(from, id); @@ -624,11 +622,11 @@ rule burnAdditivity(env e){ // STATUS - verified -// Chech that `_burn()` revertes in planned scenario(s) (if `from` is 0) +// Chech that `burn()` revertes in planned scenario(s) (if `from` is 0) rule burnRevertCases(env e){ address from; uint256 id; uint256 amount; - _burn@withrevert(e, from, id, amount); + burn@withrevert(e, from, id, amount); assert from == 0 => lastReverted, "Should revert"; } @@ -642,7 +640,7 @@ rule burnBatchRevertCases(env e){ require ids.length < 1000000000; require amounts.length < 1000000000; - _burnBatch@withrevert(e, from, ids, amounts); + burnBatch@withrevert(e, from, ids, amounts); assert (from == 0 || ids.length != amounts.length) => lastReverted, "Should revert"; } @@ -655,7 +653,7 @@ rule burnCorrectWork(env e){ uint256 otherBalanceBefore = balanceOf(from, id); - _burn(e, from, id, amount); + burn(e, from, id, amount); uint256 otherBalanceAfter = balanceOf(from, id); @@ -681,7 +679,7 @@ rule burnBatchCorrectWork(env e){ uint256 otherBalanceBefore2 = balanceOf(from, id2); uint256 otherBalanceBefore3 = balanceOf(from, id3); - _burnBatch(e, from, ids, amounts); + burnBatch(e, from, ids, amounts); uint256 otherBalanceAfter1 = balanceOf(from, id1); uint256 otherBalanceAfter2 = balanceOf(from, id2); @@ -701,7 +699,7 @@ rule cantBurnMoreSingle(env e){ require to_mathint(balanceOf(from, id) - amount) < 0; - _burn@withrevert(e, from, id, amount); + burn@withrevert(e, from, id, amount); assert lastReverted, "Don't be too greedy!"; } @@ -724,7 +722,7 @@ rule cantBurnMoreBatch(env e){ || to_mathint(balanceOf(from, id2) - amount2) < 0 || to_mathint(balanceOf(from, id3) - amount3) < 0 ; - _burnBatch@withrevert(e, from, ids, amounts); + burnBatch@withrevert(e, from, ids, amounts); assert lastReverted, "Don't be too greedy!"; } @@ -738,7 +736,7 @@ rule cantBurnOtherBalances(env e){ uint256 otherBalanceBefore = balanceOf(other, id); - _burn(e, from, id, amount); + burn(e, from, id, amount); uint256 otherBalanceAfter = balanceOf(other, id); @@ -759,7 +757,7 @@ rule cantBurnBatchOtherBalances(env e){ uint256 otherBalanceBefore2 = balanceOf(other, id2); uint256 otherBalanceBefore3 = balanceOf(other, id3); - _burnBatch(e, from, ids, amounts); + burnBatch(e, from, ids, amounts); uint256 otherBalanceAfter1 = balanceOf(other, id1); uint256 otherBalanceAfter2 = balanceOf(other, id2); From ffa3daa5d9593dd51a2e3400bad5654a5da49328 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 6 Jun 2022 11:42:13 -0700 Subject: [PATCH 228/254] Modified verification scripts and Supply spec syntax --- certora/scripts/verifyERC1155Pausable.sh | 2 +- certora/scripts/verifyERC1155Supply.sh | 2 +- certora/specs/ERC1155Supply.spec | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/certora/scripts/verifyERC1155Pausable.sh b/certora/scripts/verifyERC1155Pausable.sh index 9af2aa8c4..2c41d9f54 100755 --- a/certora/scripts/verifyERC1155Pausable.sh +++ b/certora/scripts/verifyERC1155Pausable.sh @@ -8,4 +8,4 @@ certoraRun \ --loop_iter 3 \ --send_only \ --cloud \ - --msg "ERC1155 Pausable verification" + --msg "ERC1155 Pausable verification all rules" diff --git a/certora/scripts/verifyERC1155Supply.sh b/certora/scripts/verifyERC1155Supply.sh index 43f5b3797..09db4a55a 100755 --- a/certora/scripts/verifyERC1155Supply.sh +++ b/certora/scripts/verifyERC1155Supply.sh @@ -8,4 +8,4 @@ certoraRun \ --loop_iter 3 \ --cloud \ --send_only \ - --msg "ERC1155 Supply verification" + --msg "ERC1155 Supply verification all rules" diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index 8956a537a..cd894f6f7 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -35,12 +35,12 @@ filtered { rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { assert false, - "TODO implement this rule using burn version as structural model" + "TODO implement this rule using burn version as structural model"; } rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence { assert false, - "TODO implement this rule using burn version as structural model" + "TODO implement this rule using burn version as structural model"; } /******************************************************************************/ From 3ccaf4f6d1ddda33c5424c4ba51b6e6de56a68b0 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 6 Jun 2022 11:50:05 -0700 Subject: [PATCH 229/254] Updated and cleaned up rule descriptions --- certora/specs/ERC1155Burnable.spec | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index 23c036e81..e673507e3 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -5,15 +5,7 @@ methods { /// If a method call reduces account balances, the caller must be either the /// holder of the account or approved to act on the holder's behalf. -/// n.b. This rule was passing for all methods except `_burn` and `_burnBatch`, -/// ordinarily internal methods that are callable by our tool only because they -/// were changed to public for the purposes of prior verification. Filtered here -/// since they are not generally callable. rule onlyHolderOrApprovedCanReduceBalance(method f) -filtered { - f -> f.selector != _burn(address,uint256,uint256).selector - && f.selector != _burnBatch(address,uint256[],uint256[]).selector -} { address holder; uint256 token; uint256 amount; uint256 balanceBefore = balanceOf(holder, token); @@ -52,10 +44,9 @@ rule burnAmountProportionalToBalanceReduction { "A larger burn must lead to a larger decrease in balance"; } -/// @decription Two sequential burns must be equivalent to a single burn of the sum of their +/// Two sequential burns must be equivalent to a single burn of the sum of their /// amounts. -/// @formula Date: Mon, 6 Jun 2022 12:26:41 -0700 Subject: [PATCH 230/254] Removed reference to _burn and _mint --- certora/specs/ERC1155Supply.spec | 107 +++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 7 deletions(-) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index cd894f6f7..00a22887a 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -9,9 +9,7 @@ methods { /// totalSupply for other should not rule token_totalSupply_independence(method f) filtered { - f -> f.selector != _burnBatch(address,uint256[],uint256[]).selector - && f.selector != _mintBatch(address,uint256[],uint256[],bytes).selector - && f.selector != safeBatchTransferFrom(address,address,uint256[],uint256[],bytes).selector + f -> f.selector != safeBatchTransferFrom(address,address,uint256[],uint256[],bytes).selector } { uint256 token1; uint256 token2; @@ -32,17 +30,112 @@ filtered { /// TODO possibly show equivalence between batch and non-batch methods /// in order to leverage non-batch rules wrt batch rules - +/* +/// The result of transferring a single token must be equivalent whether done +/// via safeTransferFrom or safeBatchTransferFrom. rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { - assert false, - "TODO implement this rule using burn version as structural model"; -} + storage beforeTransfer = lastStorage; + env e; + + address holder; address recipient; + uint256 token; uint256 transferAmount; bytes data; + uint256[] tokens; uint256[] transferAmounts; + +/// safeTransferFrom(address,address,uint256,uint256,bytes) +/// safeBatchTransferFrom(address,address,uint256[],uint256[],bytes) + + mathint holderStartingBalance = balanceOf(holder, token); + mathint recipientStartingBalance = balanceOf(recipient, token); + + require tokens.length == 1; require transferAmounts.length == 1; + require tokens[0] == token; require transferAmounts[0] == transferAmount; + + // transferring via safeTransferFrom + safeTransferFrom(e, holder, recipient, token, transferAmount, data) at beforeTransfer; + mathint safeTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); + + // transferring via safeBatchTransferFrom + safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfer; + mathint safeBatchTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); + + assert safeTransferFromBalanceChange == safeBatchTransferFromBalanceChange, + "Transferring a single token via safeTransferFrom or safeBatchTransferFrom must be equivalent"; +} rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence { assert false, "TODO implement this rule using burn version as structural model"; } +/* + +/// The results of burning multiple tokens must be equivalent whether done +/// separately via burn or together via burnBatch. +rule multipleTokenBurnBurnBatchEquivalence { + storage beforeBurns = lastStorage; + env e; + + address holder; + uint256 tokenA; uint256 tokenB; uint256 tokenC; + uint256 burnAmountA; uint256 burnAmountB; uint256 burnAmountC; + uint256[] tokens; uint256[] burnAmounts; + + mathint startingBalanceA = balanceOf(holder, tokenA); + mathint startingBalanceB = balanceOf(holder, tokenB); + mathint startingBalanceC = balanceOf(holder, tokenC); + + require tokens.length == 3; require burnAmounts.length == 3; + require tokens[0] == tokenA; require burnAmounts[0] == burnAmountA; + require tokens[1] == tokenB; require burnAmounts[1] == burnAmountB; + require tokens[2] == tokenC; require burnAmounts[2] == burnAmountC; + + // burning via burn + burn(e, holder, tokenA, burnAmountA) at beforeBurns; + burn(e, holder, tokenB, burnAmountB); + burn(e, holder, tokenC, burnAmountC); + mathint burnBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA); + mathint burnBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB); + mathint burnBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC); + + // burning via burnBatch + burnBatch(e, holder, tokens, burnAmounts) at beforeBurns; + mathint burnBatchBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA); + mathint burnBatchBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB); + mathint burnBatchBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC); + + assert burnBalanceChangeA == burnBatchBalanceChangeA + && burnBalanceChangeB == burnBatchBalanceChangeB + && burnBalanceChangeC == burnBatchBalanceChangeC, + "Burning multiple tokens via burn or burnBatch must be equivalent"; +} + +/// If passed empty token and burn amount arrays, burnBatch must not change +/// token balances or address permissions. +rule burnBatchOnEmptyArraysChangesNothing { + env e; + + address holder; uint256 token; + address nonHolderA; address nonHolderB; + uint256 startingBalance = balanceOf(holder, token); + bool startingPermissionNonHolderA = isApprovedForAll(holder, nonHolderA); + bool startingPermissionNonHolderB = isApprovedForAll(holder, nonHolderB); + uint256[] noTokens; uint256[] noBurnAmounts; + require noTokens.length == 0; require noBurnAmounts.length == 0; + + burnBatch(e, holder, noTokens, noBurnAmounts); + uint256 endingBalance = balanceOf(holder, token); + bool endingPermissionNonHolderA = isApprovedForAll(holder, nonHolderA); + bool endingPermissionNonHolderB = isApprovedForAll(holder, nonHolderB); + + assert startingBalance == endingBalance, + "burnBatch must not change token balances if passed empty arrays"; + assert startingPermissionNonHolderA == endingPermissionNonHolderA + && startingPermissionNonHolderB == endingPermissionNonHolderB, + "burnBatch must not change account permissions if passed empty arrays"; +} + +*/ + /******************************************************************************/ ghost mapping(uint256 => mathint) sumOfBalances { From f4b2aff79eda22d87dbcee0a390ae0ad5b95fc80 Mon Sep 17 00:00:00 2001 From: Michael George Date: Mon, 6 Jun 2022 15:33:25 -0400 Subject: [PATCH 231/254] added erc1155ext branch to CI --- .github/workflows/verify.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 6919609d0..2bf53082e 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -5,6 +5,7 @@ on: branches: - main - certora/erc20 + - certora/erc1155ext jobs: verify: From a373d25b01e376acba53f43955246bca9d7d760c Mon Sep 17 00:00:00 2001 From: Michael George Date: Mon, 6 Jun 2022 15:35:08 -0400 Subject: [PATCH 232/254] updated CI to match moving scripts to old --- .github/workflows/verify.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 2bf53082e..0844e8ee9 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -46,13 +46,13 @@ jobs: matrix: script: - - verifyTimelock.sh - - verifyERC1155.sh - - verifyERC20FlashMint.sh - - verifyERC20Wrapper.sh - - verifyAccessControl.sh - - verifyERC20Votes.sh "checking ERC20Votes.spec on ERC20Votes.sol" - - verifyERC721Votes.sh "checking ERC721Votes.spec on draft-ERC721Votes.sol and Votes.sol" + - old/verifyTimelock.sh + - old/verifyERC1155.sh + - old/verifyERC20FlashMint.sh + - old/verifyERC20Wrapper.sh + - old/verifyAccessControl.sh + - old/verifyERC20Votes.sh "checking ERC20Votes.spec on ERC20Votes.sol" + - old/verifyERC721Votes.sh "checking ERC721Votes.spec on draft-ERC721Votes.sol and Votes.sol" From 234b843c36aee4058b6c9a4e854870f9ec308996 Mon Sep 17 00:00:00 2001 From: Michael George Date: Mon, 6 Jun 2022 15:44:07 -0400 Subject: [PATCH 233/254] commented out failing rules and added solc version to ERC1155 --- .github/workflows/verify.yml | 4 ++-- certora/scripts/old/verifyERC1155.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 0844e8ee9..4fde4b0a5 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -48,8 +48,8 @@ jobs: script: - old/verifyTimelock.sh - old/verifyERC1155.sh - - old/verifyERC20FlashMint.sh - - old/verifyERC20Wrapper.sh +# - old/verifyERC20FlashMint.sh +# - old/verifyERC20Wrapper.sh - old/verifyAccessControl.sh - old/verifyERC20Votes.sh "checking ERC20Votes.spec on ERC20Votes.sol" - old/verifyERC721Votes.sh "checking ERC721Votes.spec on draft-ERC721Votes.sol and Votes.sol" diff --git a/certora/scripts/old/verifyERC1155.sh b/certora/scripts/old/verifyERC1155.sh index d60ac9cd5..d2c00df78 100644 --- a/certora/scripts/old/verifyERC1155.sh +++ b/certora/scripts/old/verifyERC1155.sh @@ -1,9 +1,9 @@ certoraRun \ certora/harnesses/ERC1155/ERC1155Harness.sol \ --verify ERC1155Harness:certora/specs/ERC1155.spec \ - --solc solc \ + --solc solc8.2 \ --optimistic_loop \ --loop_iter 3 \ --send_only \ --msg "ERC1155" - \ No newline at end of file + From 866042d6fc3f611ecf2ad4ba9ab152d6b6ed83fd Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 6 Jun 2022 12:52:34 -0700 Subject: [PATCH 234/254] Added two transfer batch transfer equivalence rules --- certora/specs/ERC1155Supply.spec | 94 +++++++++++++++----------------- 1 file changed, 45 insertions(+), 49 deletions(-) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index 00a22887a..15c9d1a55 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -30,7 +30,7 @@ filtered { /// TODO possibly show equivalence between batch and non-batch methods /// in order to leverage non-batch rules wrt batch rules -/* + /// The result of transferring a single token must be equivalent whether done /// via safeTransferFrom or safeBatchTransferFrom. rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { @@ -41,9 +41,6 @@ rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { uint256 token; uint256 transferAmount; bytes data; uint256[] tokens; uint256[] transferAmounts; -/// safeTransferFrom(address,address,uint256,uint256,bytes) -/// safeBatchTransferFrom(address,address,uint256[],uint256[],bytes) - mathint holderStartingBalance = balanceOf(holder, token); mathint recipientStartingBalance = balanceOf(recipient, token); @@ -52,63 +49,62 @@ rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { // transferring via safeTransferFrom safeTransferFrom(e, holder, recipient, token, transferAmount, data) at beforeTransfer; - mathint safeTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); + mathint holderSafeTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); + mathint recipientSafeTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance; + // transferring via safeBatchTransferFrom safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfer; - mathint safeBatchTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); + mathint holderSafeBatchTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); + mathint recipientSafeBatchTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance; - assert safeTransferFromBalanceChange == safeBatchTransferFromBalanceChange, + assert holderSafeTransferFromBalanceChange == holderSafeBatchTransferFromBalanceChange + && recipientSafeTransferFromBalanceChange == recipientSafeBatchTransferFromBalanceChange, "Transferring a single token via safeTransferFrom or safeBatchTransferFrom must be equivalent"; } +/// The results of transferring multiple tokens must be equivalent whether done +/// separately via safeTransferFrom or together via safeBatchTransferFrom. rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence { - assert false, - "TODO implement this rule using burn version as structural model"; + storage beforeTransfers = lastStorage; + env e; + + address holder; address recipient; bytes data; + uint256 tokenA; uint256 tokenB; uint256 tokenC; + uint256 transferAmountA; uint256 transferAmountB; uint256 transferAmountC; + uint256[] tokens; uint256[] transferAmounts; + + mathint holderStartingBalanceA = balanceOf(holder, tokenA); + mathint holderStartingBalanceB = balanceOf(holder, tokenB); + mathint holderStartingBalanceC = balanceOf(holder, tokenC); + + require tokens.length == 3; require transferAmounts.length == 3; + require tokens[0] == tokenA; require transferAmounts[0] == transferAmountA; + require tokens[1] == tokenB; require transferAmounts[1] == transferAmountB; + require tokens[2] == tokenC; require transferAmounts[2] == transferAmountC; + + // transferring via safeTransferFrom + safeTransferFrom(e, holder, recipient, tokenA, transferAmountA, data) at beforeTransfers; + safeTransferFrom(e, holder, recipient, tokenB, transferAmountB, data); + safeTransferFrom(e, holder, recipient, tokenC, transferAmountC, data); + mathint holderSafeTransferFromChangeA = holderStartingBalanceA - balanceOf(holder, tokenA); + mathint holderSafeTransferFromChangeB = holderStartingBalanceB - balanceOf(holder, tokenB); + mathint holderSafeTransferFromChangeC = holderStartingBalanceC - balanceOf(holder, tokenC); + + // transferring via safeBatchTransferFrom + safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfers; + mathint holderSafeBatchTransferFromBalanceChangeA = holderStartingBalanceA - balanceOf(holder, tokenA); + mathint holderSafeBatchTransferFromBalanceChangeB = holderStartingBalanceB - balanceOf(holder, tokenB); + mathint holderSafeBatchTransferFromBalanceChangeC = holderStartingBalanceC - balanceOf(holder, tokenC); + + assert holderSafeTransferFromChangeA == holderSafeBatchTransferFromBalanceChangeA + && holderSafeTransferFromChangeB == holderSafeBatchTransferFromBalanceChangeB + && holderSafeTransferFromChangeC == holderSafeBatchTransferFromBalanceChangeC, + "Transferring multiple tokens via safeTransferFrom or safeBatchTransferFrom must be equivalent"; } /* -/// The results of burning multiple tokens must be equivalent whether done -/// separately via burn or together via burnBatch. -rule multipleTokenBurnBurnBatchEquivalence { - storage beforeBurns = lastStorage; - env e; - - address holder; - uint256 tokenA; uint256 tokenB; uint256 tokenC; - uint256 burnAmountA; uint256 burnAmountB; uint256 burnAmountC; - uint256[] tokens; uint256[] burnAmounts; - - mathint startingBalanceA = balanceOf(holder, tokenA); - mathint startingBalanceB = balanceOf(holder, tokenB); - mathint startingBalanceC = balanceOf(holder, tokenC); - - require tokens.length == 3; require burnAmounts.length == 3; - require tokens[0] == tokenA; require burnAmounts[0] == burnAmountA; - require tokens[1] == tokenB; require burnAmounts[1] == burnAmountB; - require tokens[2] == tokenC; require burnAmounts[2] == burnAmountC; - - // burning via burn - burn(e, holder, tokenA, burnAmountA) at beforeBurns; - burn(e, holder, tokenB, burnAmountB); - burn(e, holder, tokenC, burnAmountC); - mathint burnBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA); - mathint burnBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB); - mathint burnBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC); - - // burning via burnBatch - burnBatch(e, holder, tokens, burnAmounts) at beforeBurns; - mathint burnBatchBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA); - mathint burnBatchBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB); - mathint burnBatchBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC); - - assert burnBalanceChangeA == burnBatchBalanceChangeA - && burnBalanceChangeB == burnBatchBalanceChangeB - && burnBalanceChangeC == burnBatchBalanceChangeC, - "Burning multiple tokens via burn or burnBatch must be equivalent"; -} - /// If passed empty token and burn amount arrays, burnBatch must not change /// token balances or address permissions. rule burnBatchOnEmptyArraysChangesNothing { From 1aa8141b149bab0a09567b3cf847ae728cf6716b Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Mon, 6 Jun 2022 12:55:54 -0700 Subject: [PATCH 235/254] removed a assert failure message from an invariant in Initializable spec --- certora/specs/Initializable.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/Initializable.spec b/certora/specs/Initializable.spec index 063baee82..0adbad08c 100644 --- a/certora/specs/Initializable.spec +++ b/certora/specs/Initializable.spec @@ -38,7 +38,7 @@ definition isDisabled() returns bool = initialized() == 255; /// @description A contract must only ever be in an initializing state while in the middle of a transaction execution. invariant notInitializing() - !initializing(), "contract must not be initializing" + !initializing() ////////////////////////////////////////////////////////////////////////////// From 6a4fc6acb88a447a6881c040c97b2fac2e02b8c5 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Mon, 6 Jun 2022 12:57:50 -0700 Subject: [PATCH 236/254] set up CI for round3 scripts --- .github/workflows/verify.yml | 6 ++++++ certora/scripts/Round3/round3.sh | 4 ++++ certora/scripts/Round3/verifyERC1155Burnable.sh | 8 ++++++++ certora/scripts/Round3/verifyERC1155Pausable.sh | 7 +++++++ certora/scripts/Round3/verifyERC1155Supply.sh | 7 +++++++ .../scripts/Round3/verifyGovernorPreventLateQuorum.sh | 10 ++++++++++ certora/scripts/Round3/verifyInitializable.sh | 10 ++++++++++ 7 files changed, 52 insertions(+) create mode 100644 certora/scripts/Round3/round3.sh create mode 100644 certora/scripts/Round3/verifyERC1155Burnable.sh create mode 100755 certora/scripts/Round3/verifyERC1155Pausable.sh create mode 100755 certora/scripts/Round3/verifyERC1155Supply.sh create mode 100644 certora/scripts/Round3/verifyGovernorPreventLateQuorum.sh create mode 100644 certora/scripts/Round3/verifyInitializable.sh diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 4fde4b0a5..dafed0e22 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -53,6 +53,12 @@ jobs: - old/verifyAccessControl.sh - old/verifyERC20Votes.sh "checking ERC20Votes.spec on ERC20Votes.sol" - old/verifyERC721Votes.sh "checking ERC721Votes.spec on draft-ERC721Votes.sol and Votes.sol" + - Round3/verifyERC1155Burnable.sh + - Round3/verifyERC1155Supply.sh + - Round3/verifyERC1155Pausable.sh + - Round3/verifyERC1155Initializable.sh + - Round3/verifyERC1155GovernorPreventLateQuorum.sh + diff --git a/certora/scripts/Round3/round3.sh b/certora/scripts/Round3/round3.sh new file mode 100644 index 000000000..f976e610c --- /dev/null +++ b/certora/scripts/Round3/round3.sh @@ -0,0 +1,4 @@ +for script in ./certora/scripts/Round3/verify*.sh +do + sh $script +done \ No newline at end of file diff --git a/certora/scripts/Round3/verifyERC1155Burnable.sh b/certora/scripts/Round3/verifyERC1155Burnable.sh new file mode 100644 index 000000000..0231296c7 --- /dev/null +++ b/certora/scripts/Round3/verifyERC1155Burnable.sh @@ -0,0 +1,8 @@ +certoraRun \ + certora/harnesses/ERC1155/ERC1155BurnableHarness.sol \ + --verify ERC1155BurnableHarness:certora/specs/ERC1155Burnable.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --msg "ERC1155 Burnable verification all rules" + \ No newline at end of file diff --git a/certora/scripts/Round3/verifyERC1155Pausable.sh b/certora/scripts/Round3/verifyERC1155Pausable.sh new file mode 100755 index 000000000..0292037b6 --- /dev/null +++ b/certora/scripts/Round3/verifyERC1155Pausable.sh @@ -0,0 +1,7 @@ +certoraRun \ + certora/harnesses/ERC1155/ERC1155PausableHarness.sol \ + --verify ERC1155PausableHarness:certora/specs/ERC1155Pausable.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --msg "ERC1155 Pausable verification all rules" diff --git a/certora/scripts/Round3/verifyERC1155Supply.sh b/certora/scripts/Round3/verifyERC1155Supply.sh new file mode 100755 index 000000000..4677e0694 --- /dev/null +++ b/certora/scripts/Round3/verifyERC1155Supply.sh @@ -0,0 +1,7 @@ +certoraRun \ + certora/harnesses/ERC1155/ERC1155SupplyHarness.sol \ + --verify ERC1155SupplyHarness:certora/specs/ERC1155Supply.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --msg "ERC1155 Supply verification all rules" diff --git a/certora/scripts/Round3/verifyGovernorPreventLateQuorum.sh b/certora/scripts/Round3/verifyGovernorPreventLateQuorum.sh new file mode 100644 index 000000000..b99f0b8aa --- /dev/null +++ b/certora/scripts/Round3/verifyGovernorPreventLateQuorum.sh @@ -0,0 +1,10 @@ +certoraRun \ + certora/harnesses/ERC721VotesHarness.sol certora/munged/governance/TimelockController.sol certora/harnesses/GovernorPreventLateQuorumHarness.sol \ + --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorPreventLateQuorum.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 1 \ + --msg "GovernorPreventLateQuorum verification all rules" \ + + + diff --git a/certora/scripts/Round3/verifyInitializable.sh b/certora/scripts/Round3/verifyInitializable.sh new file mode 100644 index 000000000..291f1757e --- /dev/null +++ b/certora/scripts/Round3/verifyInitializable.sh @@ -0,0 +1,10 @@ +certoraRun \ + certora/harnesses/InitializableComplexHarness.sol \ + --verify InitializableComplexHarness:certora/specs/Initializable.spec \ + --solc solc8.2 \ + --optimistic_loop \ + --loop_iter 3 \ + --msg "Initializable verificaiton all rules on complex harness" \ + + + From ee2f0ecb687b49224f4e03c79787b815a7eae42c Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 6 Jun 2022 13:02:12 -0700 Subject: [PATCH 237/254] Improved multiple token transfer batch transfer equivalence rule --- certora/specs/ERC1155Supply.spec | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index 15c9d1a55..4ca61f4a2 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -77,6 +77,9 @@ rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence { mathint holderStartingBalanceA = balanceOf(holder, tokenA); mathint holderStartingBalanceB = balanceOf(holder, tokenB); mathint holderStartingBalanceC = balanceOf(holder, tokenC); + mathint recipientStartingBalanceA = balanceOf(recipient, tokenA); + mathint recipientStartingBalanceB = balanceOf(recipient, tokenB); + mathint recipientStartingBalanceC = balanceOf(recipient, tokenC); require tokens.length == 3; require transferAmounts.length == 3; require tokens[0] == tokenA; require transferAmounts[0] == transferAmountA; @@ -87,19 +90,28 @@ rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence { safeTransferFrom(e, holder, recipient, tokenA, transferAmountA, data) at beforeTransfers; safeTransferFrom(e, holder, recipient, tokenB, transferAmountB, data); safeTransferFrom(e, holder, recipient, tokenC, transferAmountC, data); - mathint holderSafeTransferFromChangeA = holderStartingBalanceA - balanceOf(holder, tokenA); - mathint holderSafeTransferFromChangeB = holderStartingBalanceB - balanceOf(holder, tokenB); - mathint holderSafeTransferFromChangeC = holderStartingBalanceC - balanceOf(holder, tokenC); + mathint holderSafeTransferFromBalanceChangeA = holderStartingBalanceA - balanceOf(holder, tokenA); + mathint holderSafeTransferFromBalanceChangeB = holderStartingBalanceB - balanceOf(holder, tokenB); + mathint holderSafeTransferFromBalanceChangeC = holderStartingBalanceC - balanceOf(holder, tokenC); + mathint recipientSafeTransferFromBalanceChangeA = balanceOf(recipient, tokenA) - recipientStartingBalanceA; + mathint recipientSafeTransferFromBalanceChangeB = balanceOf(recipient, tokenB) - recipientStartingBalanceB; + mathint recipientSafeTransferFromBalanceChangeC = balanceOf(recipient, tokenC) - recipientStartingBalanceC; // transferring via safeBatchTransferFrom safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfers; mathint holderSafeBatchTransferFromBalanceChangeA = holderStartingBalanceA - balanceOf(holder, tokenA); mathint holderSafeBatchTransferFromBalanceChangeB = holderStartingBalanceB - balanceOf(holder, tokenB); mathint holderSafeBatchTransferFromBalanceChangeC = holderStartingBalanceC - balanceOf(holder, tokenC); + mathint recipientSafeBatchTransferFromBalanceChangeA = balanceOf(recipient, tokenA) - recipientStartingBalanceA; + mathint recipientSafeBatchTransferFromBalanceChangeB = balanceOf(recipient, tokenB) - recipientStartingBalanceB; + mathint recipientSafeBatchTransferFromBalanceChangeC = balanceOf(recipient, tokenC) - recipientStartingBalanceC; - assert holderSafeTransferFromChangeA == holderSafeBatchTransferFromBalanceChangeA - && holderSafeTransferFromChangeB == holderSafeBatchTransferFromBalanceChangeB - && holderSafeTransferFromChangeC == holderSafeBatchTransferFromBalanceChangeC, + assert holderSafeTransferFromBalanceChangeA == holderSafeBatchTransferFromBalanceChangeA + && holderSafeTransferFromBalanceChangeB == holderSafeBatchTransferFromBalanceChangeB + && holderSafeTransferFromBalanceChangeC == holderSafeBatchTransferFromBalanceChangeC + && recipientSafeTransferFromBalanceChangeA == recipientSafeBatchTransferFromBalanceChangeA + && recipientSafeTransferFromBalanceChangeB == recipientSafeBatchTransferFromBalanceChangeB + && recipientSafeTransferFromBalanceChangeC == recipientSafeBatchTransferFromBalanceChangeC, "Transferring multiple tokens via safeTransferFrom or safeBatchTransferFrom must be equivalent"; } From cccd90ec83f92596e325cc511690a09a083e7add Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Mon, 6 Jun 2022 13:06:08 -0700 Subject: [PATCH 238/254] fix typo in CI --- .github/workflows/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index dafed0e22..1071a3816 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -56,7 +56,7 @@ jobs: - Round3/verifyERC1155Burnable.sh - Round3/verifyERC1155Supply.sh - Round3/verifyERC1155Pausable.sh - - Round3/verifyERC1155Initializable.sh + - Round3/verifyInitializable.sh - Round3/verifyERC1155GovernorPreventLateQuorum.sh From 84b371f92c50bba915b4aa7943696b8488ab148a Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 6 Jun 2022 13:54:22 -0700 Subject: [PATCH 239/254] Added rule transfersHaveSameLengthInputArrays (partially passing) --- certora/specs/ERC1155Supply.spec | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index 4ca61f4a2..d624201a0 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -115,6 +115,22 @@ rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence { "Transferring multiple tokens via safeTransferFrom or safeBatchTransferFrom must be equivalent"; } +/// If transfer methods do not revert, the input arrays must be the same length. +rule transfersHaveSameLengthInputArrays { + env e; + + address holder; address recipient; bytes data; + uint256[] tokens; uint256[] transferAmounts; + + safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data); + + uint256 tokensLength = tokens.length; + uint256 transferAmountsLength = transferAmounts.length; + + assert tokens.length == transferAmounts.length, + "If transfer methods do not revert, the input arrays must be the same length"; +} + /* /// If passed empty token and burn amount arrays, burnBatch must not change @@ -144,6 +160,8 @@ rule burnBatchOnEmptyArraysChangesNothing { */ +/// TODO + /******************************************************************************/ ghost mapping(uint256 => mathint) sumOfBalances { From 7946806fb32d22e5f3b1571a2e1841df0d75c159 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Mon, 6 Jun 2022 17:34:11 -0700 Subject: [PATCH 240/254] Commented out sanity rules for the purposes of CI --- certora/specs/ERC1155Burnable.spec | 23 +++++----- certora/specs/ERC1155Pausable.spec | 3 +- certora/specs/ERC1155Supply.spec | 69 +++--------------------------- 3 files changed, 20 insertions(+), 75 deletions(-) diff --git a/certora/specs/ERC1155Burnable.spec b/certora/specs/ERC1155Burnable.spec index e673507e3..a5507bfce 100644 --- a/certora/specs/ERC1155Burnable.spec +++ b/certora/specs/ERC1155Burnable.spec @@ -140,28 +140,26 @@ rule multipleTokenBurnBurnBatchEquivalence { /// If passed empty token and burn amount arrays, burnBatch must not change /// token balances or address permissions. rule burnBatchOnEmptyArraysChangesNothing { - env e; + uint256 token; address nonHolderA; address nonHolderB; - address holder; uint256 token; - address nonHolderA; address nonHolderB; - uint256 startingBalance = balanceOf(holder, token); - bool startingPermissionNonHolderA = isApprovedForAll(holder, nonHolderA); - bool startingPermissionNonHolderB = isApprovedForAll(holder, nonHolderB); - uint256[] noTokens; uint256[] noBurnAmounts; + uint256 startingBalance = balanceOf(nonHolderA, token); + bool startingPermission = isApprovedForAll(nonHolderA, nonHolderB); + + env e; address holder; uint256[] noTokens; uint256[] noBurnAmounts; require noTokens.length == 0; require noBurnAmounts.length == 0; burnBatch(e, holder, noTokens, noBurnAmounts); - uint256 endingBalance = balanceOf(holder, token); - bool endingPermissionNonHolderA = isApprovedForAll(holder, nonHolderA); - bool endingPermissionNonHolderB = isApprovedForAll(holder, nonHolderB); + + uint256 endingBalance = balanceOf(nonHolderA, token); + bool endingPermission = isApprovedForAll(nonHolderA, nonHolderB); assert startingBalance == endingBalance, "burnBatch must not change token balances if passed empty arrays"; - assert startingPermissionNonHolderA == endingPermissionNonHolderA - && startingPermissionNonHolderB == endingPermissionNonHolderB, + assert startingPermission == endingPermission, "burnBatch must not change account permissions if passed empty arrays"; } +/* /// This rule should always fail. rule sanity { method f; env e; calldataarg args; @@ -171,3 +169,4 @@ rule sanity { assert false, "This rule should always fail"; } +*/ \ No newline at end of file diff --git a/certora/specs/ERC1155Pausable.spec b/certora/specs/ERC1155Pausable.spec index 4271c07df..53a66e4b6 100644 --- a/certora/specs/ERC1155Pausable.spec +++ b/certora/specs/ERC1155Pausable.spec @@ -103,7 +103,7 @@ rule whenPausedModifierCausesRevertIfUnpaused { assert lastReverted, "Functions with the whenPaused modifier must revert if the contract is not paused"; } - +/* /// This rule should always fail. rule sanity { method f; env e; calldataarg args; @@ -113,3 +113,4 @@ rule sanity { assert false, "This rule should always fail"; } +*/ \ No newline at end of file diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index d624201a0..d8e617b5d 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -6,7 +6,7 @@ methods { } /// given two different token ids, if totalSupply for one changes, then -/// totalSupply for other should not +/// totalSupply for other must not rule token_totalSupply_independence(method f) filtered { f -> f.selector != safeBatchTransferFrom(address,address,uint256[],uint256[],bytes).selector @@ -28,9 +28,6 @@ filtered { "methods must not change the total supply of more than one token"; } -/// TODO possibly show equivalence between batch and non-batch methods -/// in order to leverage non-batch rules wrt batch rules - /// The result of transferring a single token must be equivalent whether done /// via safeTransferFrom or safeBatchTransferFrom. rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { @@ -52,7 +49,6 @@ rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { mathint holderSafeTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); mathint recipientSafeTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance; - // transferring via safeBatchTransferFrom safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfer; mathint holderSafeBatchTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); @@ -131,37 +127,6 @@ rule transfersHaveSameLengthInputArrays { "If transfer methods do not revert, the input arrays must be the same length"; } -/* - -/// If passed empty token and burn amount arrays, burnBatch must not change -/// token balances or address permissions. -rule burnBatchOnEmptyArraysChangesNothing { - env e; - - address holder; uint256 token; - address nonHolderA; address nonHolderB; - uint256 startingBalance = balanceOf(holder, token); - bool startingPermissionNonHolderA = isApprovedForAll(holder, nonHolderA); - bool startingPermissionNonHolderB = isApprovedForAll(holder, nonHolderB); - uint256[] noTokens; uint256[] noBurnAmounts; - require noTokens.length == 0; require noBurnAmounts.length == 0; - - burnBatch(e, holder, noTokens, noBurnAmounts); - uint256 endingBalance = balanceOf(holder, token); - bool endingPermissionNonHolderA = isApprovedForAll(holder, nonHolderA); - bool endingPermissionNonHolderB = isApprovedForAll(holder, nonHolderB); - - assert startingBalance == endingBalance, - "burnBatch must not change token balances if passed empty arrays"; - assert startingPermissionNonHolderA == endingPermissionNonHolderA - && startingPermissionNonHolderB == endingPermissionNonHolderB, - "burnBatch must not change account permissions if passed empty arrays"; -} - -*/ - -/// TODO - /******************************************************************************/ ghost mapping(uint256 => mathint) sumOfBalances { @@ -172,8 +137,8 @@ hook Sstore _balances[KEY uint256 token][KEY address user] uint256 newValue (uin sumOfBalances[token] = sumOfBalances[token] + newValue - oldValue; } -// status: not passing, because mint and burn are the same as transferring to/from -// the 0 address. +/// The sum of the balances over all users must equal the total supply for a +/// given token. invariant total_supply_is_sum_of_balances(uint256 token) sumOfBalances[token] == totalSupply(token) { @@ -181,36 +146,14 @@ invariant total_supply_is_sum_of_balances(uint256 token) requireInvariant balanceOfZeroAddressIsZero(token); } } -/* -rule total_supply_is_sum_of_balances_as_rule { - uint256 token; - require sumOfBalances[token] == totalSupply(token) + balanceOf(0, token); - - mathint sum_before = sumOfBalances[token]; - - method f; calldataarg arg; env e; - f(e, arg); - - mathint sum_after = sumOfBalances[token]; - - assert sumOfBalances[token] == totalSupply(token) + balanceOf(0, token); -} -*/ /******************************************************************************/ /// The balance of a token for the zero address must be zero. invariant balanceOfZeroAddressIsZero(uint256 token) balanceOf(0, token) == 0 -// if a user has a token, then the token should exist - -/* -hook Sload _balances[...] { - require balance <= totalSupply -} -*/ - +/// If a user has a token, then the token should exist. rule held_tokens_should_exist { address user; uint256 token; @@ -219,12 +162,13 @@ rule held_tokens_should_exist { // This assumption is safe because of total_supply_is_sum_of_balances require balanceOf(user, token) <= totalSupply(token); + // note: `exists_wrapper` just calls `exists` assert balanceOf(user, token) > 0 => exists_wrapper(token), "if a user's balance for a token is positive, the token must exist"; } /******************************************************************************/ - +/* rule sanity { method f; env e; calldataarg args; @@ -232,3 +176,4 @@ rule sanity { assert false; } +*/ From 75a3602ba6762bad2f5de3f8637319b5ea82adf1 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 7 Jun 2022 17:16:45 -0700 Subject: [PATCH 241/254] Modified rule transfersHaveSameLengthInputArrays (passing) to limit array size --- certora/specs/ERC1155Supply.spec | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index d8e617b5d..caabff78e 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -117,6 +117,11 @@ rule transfersHaveSameLengthInputArrays { address holder; address recipient; bytes data; uint256[] tokens; uint256[] transferAmounts; +// uint max_int = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint max_int = 0xffffffffffffffffffffffffffffffff; +// require tokens.length >= 0 && tokens.length <= type(uint128).max + require tokens.length >= 0 && tokens.length <= max_int; + require transferAmounts.length >= 0 && transferAmounts.length <= max_int; safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data); From ca034ab3df35c5d215c31a6e62cb44b7571f6f9d Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Tue, 7 Jun 2022 17:20:50 -0700 Subject: [PATCH 242/254] Deleted unused method from GovPreventLateQuorum --- certora/specs/GovernorPreventLateQuorum.spec | 2 -- 1 file changed, 2 deletions(-) diff --git a/certora/specs/GovernorPreventLateQuorum.spec b/certora/specs/GovernorPreventLateQuorum.spec index f6d3e1718..861e57ae8 100644 --- a/certora/specs/GovernorPreventLateQuorum.spec +++ b/certora/specs/GovernorPreventLateQuorum.spec @@ -25,8 +25,6 @@ methods { hashOperationBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => CONSTANT scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256) => CONSTANT - // checkpoint length ERC721 - numCheckpoints(address) returns uint32 } From 9c45c52c4bd9b6f8862cf8d5f94aaec2f43c1f55 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Tue, 7 Jun 2022 17:26:11 -0700 Subject: [PATCH 243/254] Fix CI script name for GovernorPreventLateQuorum --- .github/workflows/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 1071a3816..ce58c95d4 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -57,7 +57,7 @@ jobs: - Round3/verifyERC1155Supply.sh - Round3/verifyERC1155Pausable.sh - Round3/verifyInitializable.sh - - Round3/verifyERC1155GovernorPreventLateQuorum.sh + - Round3/verifyGovernorPreventLateQuorum.sh From e4492aed8a7904ee932a6af241f201a9aeee5b9f Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 7 Jun 2022 17:25:35 -0700 Subject: [PATCH 244/254] Cleaned up code for rule transfersHaveSameLengthInputArrays (passing) --- certora/specs/ERC1155Supply.spec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index caabff78e..a843ff378 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -117,9 +117,8 @@ rule transfersHaveSameLengthInputArrays { address holder; address recipient; bytes data; uint256[] tokens; uint256[] transferAmounts; -// uint max_int = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; uint max_int = 0xffffffffffffffffffffffffffffffff; -// require tokens.length >= 0 && tokens.length <= type(uint128).max + require tokens.length >= 0 && tokens.length <= max_int; require transferAmounts.length >= 0 && transferAmounts.length <= max_int; From 9bbc7b7eb369d55cfe303d5b8abf529e7755cfc9 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 7 Jun 2022 17:32:45 -0700 Subject: [PATCH 245/254] Moved three transfer rules from 1155Supply spec to base 1155 spec --- certora/specs/ERC1155.spec | 108 +++++++++++++++++++++++++++++++ certora/specs/ERC1155Supply.spec | 103 ----------------------------- 2 files changed, 108 insertions(+), 103 deletions(-) diff --git a/certora/specs/ERC1155.spec b/certora/specs/ERC1155.spec index df8461f98..ee899b9d4 100644 --- a/certora/specs/ERC1155.spec +++ b/certora/specs/ERC1155.spec @@ -768,3 +768,111 @@ rule cantBurnBatchOtherBalances(env e){ && otherBalanceBefore3 == otherBalanceAfter3) , "I like to see your money disappearing"; } + +///////////////////////////////////////////////// +// The rules below were added to this base ERC1155 spec as part of a later +// project with OpenZeppelin covering various ERC1155 extensions. +///////////////////////////////////////////////// + +/// The result of transferring a single token must be equivalent whether done +/// via safeTransferFrom or safeBatchTransferFrom. +rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { + storage beforeTransfer = lastStorage; + env e; + + address holder; address recipient; + uint256 token; uint256 transferAmount; bytes data; + uint256[] tokens; uint256[] transferAmounts; + + mathint holderStartingBalance = balanceOf(holder, token); + mathint recipientStartingBalance = balanceOf(recipient, token); + + require tokens.length == 1; require transferAmounts.length == 1; + require tokens[0] == token; require transferAmounts[0] == transferAmount; + + // transferring via safeTransferFrom + safeTransferFrom(e, holder, recipient, token, transferAmount, data) at beforeTransfer; + mathint holderSafeTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); + mathint recipientSafeTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance; + + // transferring via safeBatchTransferFrom + safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfer; + mathint holderSafeBatchTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); + mathint recipientSafeBatchTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance; + + assert holderSafeTransferFromBalanceChange == holderSafeBatchTransferFromBalanceChange + && recipientSafeTransferFromBalanceChange == recipientSafeBatchTransferFromBalanceChange, + "Transferring a single token via safeTransferFrom or safeBatchTransferFrom must be equivalent"; +} + +/// The results of transferring multiple tokens must be equivalent whether done +/// separately via safeTransferFrom or together via safeBatchTransferFrom. +rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence { + storage beforeTransfers = lastStorage; + env e; + + address holder; address recipient; bytes data; + uint256 tokenA; uint256 tokenB; uint256 tokenC; + uint256 transferAmountA; uint256 transferAmountB; uint256 transferAmountC; + uint256[] tokens; uint256[] transferAmounts; + + mathint holderStartingBalanceA = balanceOf(holder, tokenA); + mathint holderStartingBalanceB = balanceOf(holder, tokenB); + mathint holderStartingBalanceC = balanceOf(holder, tokenC); + mathint recipientStartingBalanceA = balanceOf(recipient, tokenA); + mathint recipientStartingBalanceB = balanceOf(recipient, tokenB); + mathint recipientStartingBalanceC = balanceOf(recipient, tokenC); + + require tokens.length == 3; require transferAmounts.length == 3; + require tokens[0] == tokenA; require transferAmounts[0] == transferAmountA; + require tokens[1] == tokenB; require transferAmounts[1] == transferAmountB; + require tokens[2] == tokenC; require transferAmounts[2] == transferAmountC; + + // transferring via safeTransferFrom + safeTransferFrom(e, holder, recipient, tokenA, transferAmountA, data) at beforeTransfers; + safeTransferFrom(e, holder, recipient, tokenB, transferAmountB, data); + safeTransferFrom(e, holder, recipient, tokenC, transferAmountC, data); + mathint holderSafeTransferFromBalanceChangeA = holderStartingBalanceA - balanceOf(holder, tokenA); + mathint holderSafeTransferFromBalanceChangeB = holderStartingBalanceB - balanceOf(holder, tokenB); + mathint holderSafeTransferFromBalanceChangeC = holderStartingBalanceC - balanceOf(holder, tokenC); + mathint recipientSafeTransferFromBalanceChangeA = balanceOf(recipient, tokenA) - recipientStartingBalanceA; + mathint recipientSafeTransferFromBalanceChangeB = balanceOf(recipient, tokenB) - recipientStartingBalanceB; + mathint recipientSafeTransferFromBalanceChangeC = balanceOf(recipient, tokenC) - recipientStartingBalanceC; + + // transferring via safeBatchTransferFrom + safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfers; + mathint holderSafeBatchTransferFromBalanceChangeA = holderStartingBalanceA - balanceOf(holder, tokenA); + mathint holderSafeBatchTransferFromBalanceChangeB = holderStartingBalanceB - balanceOf(holder, tokenB); + mathint holderSafeBatchTransferFromBalanceChangeC = holderStartingBalanceC - balanceOf(holder, tokenC); + mathint recipientSafeBatchTransferFromBalanceChangeA = balanceOf(recipient, tokenA) - recipientStartingBalanceA; + mathint recipientSafeBatchTransferFromBalanceChangeB = balanceOf(recipient, tokenB) - recipientStartingBalanceB; + mathint recipientSafeBatchTransferFromBalanceChangeC = balanceOf(recipient, tokenC) - recipientStartingBalanceC; + + assert holderSafeTransferFromBalanceChangeA == holderSafeBatchTransferFromBalanceChangeA + && holderSafeTransferFromBalanceChangeB == holderSafeBatchTransferFromBalanceChangeB + && holderSafeTransferFromBalanceChangeC == holderSafeBatchTransferFromBalanceChangeC + && recipientSafeTransferFromBalanceChangeA == recipientSafeBatchTransferFromBalanceChangeA + && recipientSafeTransferFromBalanceChangeB == recipientSafeBatchTransferFromBalanceChangeB + && recipientSafeTransferFromBalanceChangeC == recipientSafeBatchTransferFromBalanceChangeC, + "Transferring multiple tokens via safeTransferFrom or safeBatchTransferFrom must be equivalent"; +} + +/// If transfer methods do not revert, the input arrays must be the same length. +rule transfersHaveSameLengthInputArrays { + env e; + + address holder; address recipient; bytes data; + uint256[] tokens; uint256[] transferAmounts; + uint max_int = 0xffffffffffffffffffffffffffffffff; + + require tokens.length >= 0 && tokens.length <= max_int; + require transferAmounts.length >= 0 && transferAmounts.length <= max_int; + + safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data); + + uint256 tokensLength = tokens.length; + uint256 transferAmountsLength = transferAmounts.length; + + assert tokens.length == transferAmounts.length, + "If transfer methods do not revert, the input arrays must be the same length"; +} diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index a843ff378..a33aaade6 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -28,109 +28,6 @@ filtered { "methods must not change the total supply of more than one token"; } -/// The result of transferring a single token must be equivalent whether done -/// via safeTransferFrom or safeBatchTransferFrom. -rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { - storage beforeTransfer = lastStorage; - env e; - - address holder; address recipient; - uint256 token; uint256 transferAmount; bytes data; - uint256[] tokens; uint256[] transferAmounts; - - mathint holderStartingBalance = balanceOf(holder, token); - mathint recipientStartingBalance = balanceOf(recipient, token); - - require tokens.length == 1; require transferAmounts.length == 1; - require tokens[0] == token; require transferAmounts[0] == transferAmount; - - // transferring via safeTransferFrom - safeTransferFrom(e, holder, recipient, token, transferAmount, data) at beforeTransfer; - mathint holderSafeTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); - mathint recipientSafeTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance; - - // transferring via safeBatchTransferFrom - safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfer; - mathint holderSafeBatchTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); - mathint recipientSafeBatchTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance; - - assert holderSafeTransferFromBalanceChange == holderSafeBatchTransferFromBalanceChange - && recipientSafeTransferFromBalanceChange == recipientSafeBatchTransferFromBalanceChange, - "Transferring a single token via safeTransferFrom or safeBatchTransferFrom must be equivalent"; -} - -/// The results of transferring multiple tokens must be equivalent whether done -/// separately via safeTransferFrom or together via safeBatchTransferFrom. -rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence { - storage beforeTransfers = lastStorage; - env e; - - address holder; address recipient; bytes data; - uint256 tokenA; uint256 tokenB; uint256 tokenC; - uint256 transferAmountA; uint256 transferAmountB; uint256 transferAmountC; - uint256[] tokens; uint256[] transferAmounts; - - mathint holderStartingBalanceA = balanceOf(holder, tokenA); - mathint holderStartingBalanceB = balanceOf(holder, tokenB); - mathint holderStartingBalanceC = balanceOf(holder, tokenC); - mathint recipientStartingBalanceA = balanceOf(recipient, tokenA); - mathint recipientStartingBalanceB = balanceOf(recipient, tokenB); - mathint recipientStartingBalanceC = balanceOf(recipient, tokenC); - - require tokens.length == 3; require transferAmounts.length == 3; - require tokens[0] == tokenA; require transferAmounts[0] == transferAmountA; - require tokens[1] == tokenB; require transferAmounts[1] == transferAmountB; - require tokens[2] == tokenC; require transferAmounts[2] == transferAmountC; - - // transferring via safeTransferFrom - safeTransferFrom(e, holder, recipient, tokenA, transferAmountA, data) at beforeTransfers; - safeTransferFrom(e, holder, recipient, tokenB, transferAmountB, data); - safeTransferFrom(e, holder, recipient, tokenC, transferAmountC, data); - mathint holderSafeTransferFromBalanceChangeA = holderStartingBalanceA - balanceOf(holder, tokenA); - mathint holderSafeTransferFromBalanceChangeB = holderStartingBalanceB - balanceOf(holder, tokenB); - mathint holderSafeTransferFromBalanceChangeC = holderStartingBalanceC - balanceOf(holder, tokenC); - mathint recipientSafeTransferFromBalanceChangeA = balanceOf(recipient, tokenA) - recipientStartingBalanceA; - mathint recipientSafeTransferFromBalanceChangeB = balanceOf(recipient, tokenB) - recipientStartingBalanceB; - mathint recipientSafeTransferFromBalanceChangeC = balanceOf(recipient, tokenC) - recipientStartingBalanceC; - - // transferring via safeBatchTransferFrom - safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfers; - mathint holderSafeBatchTransferFromBalanceChangeA = holderStartingBalanceA - balanceOf(holder, tokenA); - mathint holderSafeBatchTransferFromBalanceChangeB = holderStartingBalanceB - balanceOf(holder, tokenB); - mathint holderSafeBatchTransferFromBalanceChangeC = holderStartingBalanceC - balanceOf(holder, tokenC); - mathint recipientSafeBatchTransferFromBalanceChangeA = balanceOf(recipient, tokenA) - recipientStartingBalanceA; - mathint recipientSafeBatchTransferFromBalanceChangeB = balanceOf(recipient, tokenB) - recipientStartingBalanceB; - mathint recipientSafeBatchTransferFromBalanceChangeC = balanceOf(recipient, tokenC) - recipientStartingBalanceC; - - assert holderSafeTransferFromBalanceChangeA == holderSafeBatchTransferFromBalanceChangeA - && holderSafeTransferFromBalanceChangeB == holderSafeBatchTransferFromBalanceChangeB - && holderSafeTransferFromBalanceChangeC == holderSafeBatchTransferFromBalanceChangeC - && recipientSafeTransferFromBalanceChangeA == recipientSafeBatchTransferFromBalanceChangeA - && recipientSafeTransferFromBalanceChangeB == recipientSafeBatchTransferFromBalanceChangeB - && recipientSafeTransferFromBalanceChangeC == recipientSafeBatchTransferFromBalanceChangeC, - "Transferring multiple tokens via safeTransferFrom or safeBatchTransferFrom must be equivalent"; -} - -/// If transfer methods do not revert, the input arrays must be the same length. -rule transfersHaveSameLengthInputArrays { - env e; - - address holder; address recipient; bytes data; - uint256[] tokens; uint256[] transferAmounts; - uint max_int = 0xffffffffffffffffffffffffffffffff; - - require tokens.length >= 0 && tokens.length <= max_int; - require transferAmounts.length >= 0 && transferAmounts.length <= max_int; - - safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data); - - uint256 tokensLength = tokens.length; - uint256 transferAmountsLength = transferAmounts.length; - - assert tokens.length == transferAmounts.length, - "If transfer methods do not revert, the input arrays must be the same length"; -} - /******************************************************************************/ ghost mapping(uint256 => mathint) sumOfBalances { From 65ab8e9ac4dbec443bce037eb07b58f4c2538670 Mon Sep 17 00:00:00 2001 From: Thomas Adams Date: Tue, 7 Jun 2022 17:51:27 -0700 Subject: [PATCH 246/254] Changed rule transfersHaveSameLengthInputArrays (passing) to use _ instead of holder --- certora/specs/ERC1155.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/specs/ERC1155.spec b/certora/specs/ERC1155.spec index ee899b9d4..bd88e8c09 100644 --- a/certora/specs/ERC1155.spec +++ b/certora/specs/ERC1155.spec @@ -861,14 +861,14 @@ rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence { rule transfersHaveSameLengthInputArrays { env e; - address holder; address recipient; bytes data; + address recipient; bytes data; uint256[] tokens; uint256[] transferAmounts; uint max_int = 0xffffffffffffffffffffffffffffffff; require tokens.length >= 0 && tokens.length <= max_int; require transferAmounts.length >= 0 && transferAmounts.length <= max_int; - safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data); + safeBatchTransferFrom(e, _, recipient, tokens, transferAmounts, data); uint256 tokensLength = tokens.length; uint256 transferAmountsLength = transferAmounts.length; From 962a5023e63525a3c8291be878bd3d402e524492 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Tue, 14 Jun 2022 16:52:57 -0700 Subject: [PATCH 247/254] remove commented code, fix some old scripts --- .../harnesses/GovernorPreventLateQuorumHarness.sol | 11 ----------- certora/harnesses/WizardControlFirstPriority.sol | 1 + certora/harnesses/WizardFirstTry.sol | 3 ++- .../scripts/old/GovernorCountingSimple-counting.sh | 2 -- certora/scripts/verifyERC1155All.sh | 2 +- 5 files changed, 4 insertions(+), 15 deletions(-) diff --git a/certora/harnesses/GovernorPreventLateQuorumHarness.sol b/certora/harnesses/GovernorPreventLateQuorumHarness.sol index 84858c710..7cc9f5b97 100644 --- a/certora/harnesses/GovernorPreventLateQuorumHarness.sol +++ b/certora/harnesses/GovernorPreventLateQuorumHarness.sol @@ -104,17 +104,6 @@ contract GovernorPreventLateQuorumHarness is Governor, GovernorCountingSimple, G return deltaWeight; } - /* - function castVote( - uint256 proposalId, - address account, - uint8 support, - string memory reason, - bytes memory params - ) public returns(uint256) { - return _castVote(proposalId, account, support, reason, params); - } - */ function lateQuorumVoteExtension() public view virtual override returns (uint64) { return super.lateQuorumVoteExtension(); diff --git a/certora/harnesses/WizardControlFirstPriority.sol b/certora/harnesses/WizardControlFirstPriority.sol index 5ae7fe066..da9fe8581 100644 --- a/certora/harnesses/WizardControlFirstPriority.sol +++ b/certora/harnesses/WizardControlFirstPriority.sol @@ -7,6 +7,7 @@ import "../munged/governance/extensions/GovernorVotes.sol"; import "../munged/governance/extensions/GovernorVotesQuorumFraction.sol"; import "../munged/governance/extensions/GovernorTimelockControl.sol"; import "../munged/governance/extensions/GovernorProposalThreshold.sol"; +import "../munged/token/ERC20/extensions/ERC20Votes.sol"; /* Wizard options: diff --git a/certora/harnesses/WizardFirstTry.sol b/certora/harnesses/WizardFirstTry.sol index 83fece04f..4e986ed32 100644 --- a/certora/harnesses/WizardFirstTry.sol +++ b/certora/harnesses/WizardFirstTry.sol @@ -6,6 +6,7 @@ import "../munged/governance/extensions/GovernorCountingSimple.sol"; import "../munged/governance/extensions/GovernorVotes.sol"; import "../munged/governance/extensions/GovernorVotesQuorumFraction.sol"; import "../munged/governance/extensions/GovernorTimelockCompound.sol"; +import "../munged/token/ERC20/extensions/ERC20Votes.sol"; /* Wizard options: @@ -83,7 +84,7 @@ contract WizardFirstTry is Governor, GovernorCountingSimple, GovernorVotes, Gove function getVotes(address account, uint256 blockNumber) public view - override(IGovernor, GovernorVotes) + override(IGovernor, Governor) returns (uint256) { return super.getVotes(account, blockNumber); diff --git a/certora/scripts/old/GovernorCountingSimple-counting.sh b/certora/scripts/old/GovernorCountingSimple-counting.sh index 9ed8fe34c..e3b86b555 100644 --- a/certora/scripts/old/GovernorCountingSimple-counting.sh +++ b/certora/scripts/old/GovernorCountingSimple-counting.sh @@ -3,8 +3,6 @@ make -C certora munged certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorBasicHarness.sol \ --verify GovernorBasicHarness:certora/specs/GovernorCountingSimple.spec \ --solc solc8.2 \ - --staging shelly/forSasha \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ - --rule hasVotedCorrelation \ --msg "$1" diff --git a/certora/scripts/verifyERC1155All.sh b/certora/scripts/verifyERC1155All.sh index fbde87f24..865374543 100644 --- a/certora/scripts/verifyERC1155All.sh +++ b/certora/scripts/verifyERC1155All.sh @@ -8,4 +8,4 @@ certoraRun \ --loop_iter 3 \ --cloud \ --send_only \ - --msg "ERC1155 Burnable verification all rules" \ No newline at end of file + --msg "ERC1155 verification all rules " \ No newline at end of file From 9708bc03974f139a0648df69bfe114a68a92fa5c Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Wed, 15 Jun 2022 14:12:04 -0700 Subject: [PATCH 248/254] undo script changes --- .github/workflows/verify.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index ce58c95d4..6cd064c64 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -46,13 +46,13 @@ jobs: matrix: script: - - old/verifyTimelock.sh - - old/verifyERC1155.sh +# - old/verifyTimelock.sh +# - old/verifyERC1155.sh # - old/verifyERC20FlashMint.sh # - old/verifyERC20Wrapper.sh - - old/verifyAccessControl.sh - - old/verifyERC20Votes.sh "checking ERC20Votes.spec on ERC20Votes.sol" - - old/verifyERC721Votes.sh "checking ERC721Votes.spec on draft-ERC721Votes.sol and Votes.sol" +# - old/verifyAccessControl.sh +# - old/verifyERC20Votes.sh "checking ERC20Votes.spec on ERC20Votes.sol" +# - old/verifyERC721Votes.sh "checking ERC721Votes.spec on draft-ERC721Votes.sol and Votes.sol" - Round3/verifyERC1155Burnable.sh - Round3/verifyERC1155Supply.sh - Round3/verifyERC1155Pausable.sh From 154a1515188423ebf6683509eee8827f669a9269 Mon Sep 17 00:00:00 2001 From: teryanarmen <61996358+teryanarmen@users.noreply.github.com> Date: Wed, 15 Jun 2022 14:31:28 -0700 Subject: [PATCH 249/254] Update verify.yml, adding master branch --- .github/workflows/verify.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 6cd064c64..2abea07e5 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -3,6 +3,7 @@ name: Certora on: push: branches: + - master - main - certora/erc20 - certora/erc1155ext From 1701b0c7fdb5dd16c1a8865fc828c349e64657a5 Mon Sep 17 00:00:00 2001 From: teryanarmen Date: Wed, 15 Jun 2022 17:05:12 -0700 Subject: [PATCH 250/254] fix erc1155supply vacuity, change CI solc version to 8.4 --- .github/workflows/verify.yml | 5 +- certora/applyHarness.patch | 957 +++++++++++++++++- .../ERC1155/ERC1155SupplyHarness.sol | 53 +- .../scripts/Round3/verifyERC1155Burnable.sh | 2 +- .../scripts/Round3/verifyERC1155Pausable.sh | 2 +- certora/scripts/Round3/verifyERC1155Supply.sh | 2 +- .../Round3/verifyGovernorPreventLateQuorum.sh | 2 +- certora/scripts/Round3/verifyInitializable.sh | 2 +- certora/specs/ERC1155Supply.spec | 2 + 9 files changed, 997 insertions(+), 30 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 6cd064c64..0bc89572a 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -3,6 +3,7 @@ name: Certora on: push: branches: + - master - main - certora/erc20 - certora/erc1155ext @@ -27,9 +28,9 @@ jobs: - name: Install solc run: | - wget https://github.com/ethereum/solidity/releases/download/v0.8.2/solc-static-linux + wget https://github.com/ethereum/solidity/releases/download/v0.8.4/solc-static-linux chmod +x solc-static-linux - sudo mv solc-static-linux /usr/local/bin/solc8.2 + sudo mv solc-static-linux /usr/local/bin/solc8.4 - name: Verify rule ${{ matrix.script }} run: | diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 99fb5d3e7..c1391565e 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,12 +1,12 @@ diff -ruN .gitignore .gitignore --- .gitignore 1969-12-31 16:00:00.000000000 -0800 -+++ .gitignore 2022-06-06 11:21:40.000000000 -0700 ++++ .gitignore 2022-06-06 12:25:10.000000000 -0700 @@ -0,0 +1,2 @@ +* +!.gitignore diff -ruN access/AccessControl.sol access/AccessControl.sol --- access/AccessControl.sol 2022-06-06 10:42:37.000000000 -0700 -+++ access/AccessControl.sol 2022-06-06 11:21:40.000000000 -0700 ++++ access/AccessControl.sol 2022-06-06 12:25:10.000000000 -0700 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -16,9 +16,41 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol _checkRole(role, _msgSender()); } +diff -ruN access/Ownable.sol access/Ownable.sol +--- access/Ownable.sol 2022-06-14 06:30:55.000000000 -0700 ++++ access/Ownable.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -30,14 +30,6 @@ + } + + /** +- * @dev Throws if called by any account other than the owner. +- */ +- modifier onlyOwner() { +- _checkOwner(); +- _; +- } +- +- /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { +@@ -45,10 +37,11 @@ + } + + /** +- * @dev Throws if the sender is not the owner. ++ * @dev Throws if called by any account other than the owner. + */ +- function _checkOwner() internal view virtual { ++ modifier onlyOwner() { + require(owner() == _msgSender(), "Ownable: caller is not the owner"); ++ _; + } + + /** diff -ruN governance/Governor.sol governance/Governor.sol --- governance/Governor.sol 2022-06-06 10:42:37.000000000 -0700 -+++ governance/Governor.sol 2022-06-06 11:21:40.000000000 -0700 ++++ governance/Governor.sol 2022-06-06 12:25:10.000000000 -0700 @@ -44,7 +44,7 @@ string private _name; @@ -30,7 +62,7 @@ diff -ruN governance/Governor.sol governance/Governor.sol // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol --- governance/TimelockController.sol 2022-06-06 10:42:37.000000000 -0700 -+++ governance/TimelockController.sol 2022-06-06 11:21:40.000000000 -0700 ++++ governance/TimelockController.sol 2022-06-06 12:25:10.000000000 -0700 @@ -28,10 +28,10 @@ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); @@ -45,8 +77,8 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol /** * @dev Emitted when a call is scheduled as part of operation `id`. diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol ---- governance/extensions/GovernorCountingSimple.sol 2022-06-06 10:42:37.000000000 -0700 -+++ governance/extensions/GovernorCountingSimple.sol 2022-06-06 11:21:40.000000000 -0700 +--- governance/extensions/GovernorCountingSimple.sol 2022-06-14 16:49:50.000000000 -0700 ++++ governance/extensions/GovernorCountingSimple.sol 2022-06-06 12:25:10.000000000 -0700 @@ -27,7 +27,7 @@ mapping(address => bool) hasVoted; } @@ -58,21 +90,21 @@ diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions * @dev See {IGovernor-COUNTING_MODE}. diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol --- governance/extensions/GovernorPreventLateQuorum.sol 2022-06-06 10:42:37.000000000 -0700 -+++ governance/extensions/GovernorPreventLateQuorum.sol 2022-06-06 11:21:40.000000000 -0700 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-06-15 17:00:30.000000000 -0700 @@ -21,8 +21,8 @@ using SafeCast for uint256; using Timers for Timers.BlockNumber; - uint64 private _voteExtension; - mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines; -+ uint64 internal _voteExtension; -+ mapping(uint256 => Timers.BlockNumber) internal _extendedDeadlines; ++ uint64 internal _voteExtension; // PRIVATE => INTERNAL ++ mapping(uint256 => Timers.BlockNumber) internal _extendedDeadlines; // PRIVATE => INTERNAL /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol --- governance/utils/Votes.sol 2022-06-06 10:42:37.000000000 -0700 -+++ governance/utils/Votes.sol 2022-06-06 11:21:40.000000000 -0700 ++++ governance/utils/Votes.sol 2022-06-06 12:25:10.000000000 -0700 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -145,9 +177,249 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol - function _getVotingUnits(address) internal view virtual returns (uint256); + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public } +diff -ruN metatx/MinimalForwarder.sol metatx/MinimalForwarder.sol +--- metatx/MinimalForwarder.sol 2022-06-14 06:30:55.000000000 -0700 ++++ metatx/MinimalForwarder.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -8,11 +8,6 @@ + + /** + * @dev Simple minimal forwarder to be used together with an ERC2771 compatible contract. See {ERC2771Context}. +- * +- * MinimalForwarder is mainly meant for testing, as it is missing features to be a good production-ready forwarder. This +- * contract does not intend to have all the properties that are needed for a sound forwarding system. A fully +- * functioning forwarding system with good properties requires more complexity. We suggest you look at other projects +- * such as the GSN which do have the goal of building a system like that. + */ + contract MinimalForwarder is EIP712 { + using ECDSA for bytes32; +diff -ruN mocks/ERC20TokenizedVaultMock.sol mocks/ERC20TokenizedVaultMock.sol +--- mocks/ERC20TokenizedVaultMock.sol 1969-12-31 16:00:00.000000000 -0800 ++++ mocks/ERC20TokenizedVaultMock.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -0,0 +1,22 @@ ++// SPDX-License-Identifier: MIT ++ ++pragma solidity ^0.8.0; ++ ++import "../token/ERC20/extensions/ERC20TokenizedVault.sol"; ++ ++// mock class using ERC20 ++contract ERC20TokenizedVaultMock is ERC20TokenizedVault { ++ constructor( ++ IERC20Metadata asset, ++ string memory name, ++ string memory symbol ++ ) ERC20(name, symbol) ERC20TokenizedVault(asset) {} ++ ++ function mockMint(address account, uint256 amount) public { ++ _mint(account, amount); ++ } ++ ++ function mockBurn(address account, uint256 amount) public { ++ _burn(account, amount); ++ } ++} +diff -ruN mocks/ERC4626Mock.sol mocks/ERC4626Mock.sol +--- mocks/ERC4626Mock.sol 2022-06-15 14:13:43.000000000 -0700 ++++ mocks/ERC4626Mock.sol 1969-12-31 16:00:00.000000000 -0800 +@@ -1,22 +0,0 @@ +-// SPDX-License-Identifier: MIT +- +-pragma solidity ^0.8.0; +- +-import "../token/ERC20/extensions/ERC4626.sol"; +- +-// mock class using ERC20 +-contract ERC4626Mock is ERC4626 { +- constructor( +- IERC20Metadata asset, +- string memory name, +- string memory symbol +- ) ERC20(name, symbol) ERC4626(asset) {} +- +- function mockMint(address account, uint256 amount) public { +- _mint(account, amount); +- } +- +- function mockBurn(address account, uint256 amount) public { +- _burn(account, amount); +- } +-} +diff -ruN mocks/MathMock.sol mocks/MathMock.sol +--- mocks/MathMock.sol 2022-06-14 06:30:55.000000000 -0700 ++++ mocks/MathMock.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -29,8 +29,4 @@ + ) public pure returns (uint256) { + return Math.mulDiv(a, b, denominator, direction); + } +- +- function sqrt(uint256 a, Math.Rounding direction) public pure returns (uint256) { +- return Math.sqrt(a, direction); +- } + } +diff -ruN mocks/SafeERC20Helper.sol mocks/SafeERC20Helper.sol +--- mocks/SafeERC20Helper.sol 2022-06-14 06:30:55.000000000 -0700 ++++ mocks/SafeERC20Helper.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -4,7 +4,6 @@ + + import "../utils/Context.sol"; + import "../token/ERC20/IERC20.sol"; +-import "../token/ERC20/extensions/draft-ERC20Permit.sol"; + import "../token/ERC20/utils/SafeERC20.sol"; + + contract ERC20ReturnFalseMock is Context { +@@ -106,43 +105,6 @@ + } + } + +-contract ERC20PermitNoRevertMock is +- ERC20("ERC20PermitNoRevertMock", "ERC20PermitNoRevertMock"), +- ERC20Permit("ERC20PermitNoRevertMock") +-{ +- function getChainId() external view returns (uint256) { +- return block.chainid; +- } +- +- function permitThatMayRevert( +- address owner, +- address spender, +- uint256 value, +- uint256 deadline, +- uint8 v, +- bytes32 r, +- bytes32 s +- ) public virtual { +- super.permit(owner, spender, value, deadline, v, r, s); +- } +- +- function permit( +- address owner, +- address spender, +- uint256 value, +- uint256 deadline, +- uint8 v, +- bytes32 r, +- bytes32 s +- ) public virtual override { +- try this.permitThatMayRevert(owner, spender, value, deadline, v, r, s) { +- // do nothing +- } catch { +- // do nothing +- } +- } +-} +- + contract SafeERC20Wrapper is Context { + using SafeERC20 for IERC20; + +@@ -172,18 +134,6 @@ + _token.safeDecreaseAllowance(address(0), amount); + } + +- function permit( +- address owner, +- address spender, +- uint256 value, +- uint256 deadline, +- uint8 v, +- bytes32 r, +- bytes32 s +- ) public { +- SafeERC20.safePermit(IERC20Permit(address(_token)), owner, spender, value, deadline, v, r, s); +- } +- + function setAllowance(uint256 allowance_) public { + ERC20ReturnTrueMock(address(_token)).setAllowance(allowance_); + } +diff -ruN proxy/Clones.sol proxy/Clones.sol +--- proxy/Clones.sol 2022-06-14 06:30:55.000000000 -0700 ++++ proxy/Clones.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -26,10 +26,10 @@ + /// @solidity memory-safe-assembly + assembly { + let ptr := mload(0x40) +- mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) +- mstore(add(ptr, 0x14), shl(0x60, implementation)) +- mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) +- instance := create(0, ptr, 0x37) ++ mstore(ptr, 0x602d8060093d393df3363d3d373d3d3d363d7300000000000000000000000000) ++ mstore(add(ptr, 0x13), shl(0x60, implementation)) ++ mstore(add(ptr, 0x27), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) ++ instance := create(0, ptr, 0x36) + } + require(instance != address(0), "ERC1167: create failed"); + } +@@ -45,10 +45,10 @@ + /// @solidity memory-safe-assembly + assembly { + let ptr := mload(0x40) +- mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) +- mstore(add(ptr, 0x14), shl(0x60, implementation)) +- mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) +- instance := create2(0, ptr, 0x37, salt) ++ mstore(ptr, 0x602d8060093d393df3363d3d373d3d3d363d7300000000000000000000000000) ++ mstore(add(ptr, 0x13), shl(0x60, implementation)) ++ mstore(add(ptr, 0x27), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) ++ instance := create2(0, ptr, 0x36, salt) + } + require(instance != address(0), "ERC1167: create2 failed"); + } +@@ -64,13 +64,13 @@ + /// @solidity memory-safe-assembly + assembly { + let ptr := mload(0x40) +- mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) +- mstore(add(ptr, 0x14), shl(0x60, implementation)) +- mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000) +- mstore(add(ptr, 0x38), shl(0x60, deployer)) +- mstore(add(ptr, 0x4c), salt) +- mstore(add(ptr, 0x6c), keccak256(ptr, 0x37)) +- predicted := keccak256(add(ptr, 0x37), 0x55) ++ mstore(ptr, 0x602d8060093d393df3363d3d373d3d3d363d7300000000000000000000000000) ++ mstore(add(ptr, 0x13), shl(0x60, implementation)) ++ mstore(add(ptr, 0x27), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000) ++ mstore(add(ptr, 0x37), shl(0x60, deployer)) ++ mstore(add(ptr, 0x4b), salt) ++ mstore(add(ptr, 0x6b), keccak256(ptr, 0x36)) ++ predicted := keccak256(add(ptr, 0x36), 0x55) + } + } + +diff -ruN proxy/ERC1967/ERC1967Proxy.sol proxy/ERC1967/ERC1967Proxy.sol +--- proxy/ERC1967/ERC1967Proxy.sol 2022-06-14 06:30:55.000000000 -0700 ++++ proxy/ERC1967/ERC1967Proxy.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -20,6 +20,7 @@ + * function call, and allows initializing the storage of the proxy like a Solidity constructor. + */ + constructor(address _logic, bytes memory _data) payable { ++ assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); + _upgradeToAndCall(_logic, _data, false); + } + +diff -ruN proxy/beacon/BeaconProxy.sol proxy/beacon/BeaconProxy.sol +--- proxy/beacon/BeaconProxy.sol 2022-06-14 06:30:55.000000000 -0700 ++++ proxy/beacon/BeaconProxy.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -28,6 +28,7 @@ + * - `beacon` must be a contract with the interface {IBeacon}. + */ + constructor(address beacon, bytes memory data) payable { ++ assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1)); + _upgradeBeaconToAndCall(beacon, data, false); + } + +diff -ruN proxy/transparent/TransparentUpgradeableProxy.sol proxy/transparent/TransparentUpgradeableProxy.sol +--- proxy/transparent/TransparentUpgradeableProxy.sol 2022-06-14 06:30:55.000000000 -0700 ++++ proxy/transparent/TransparentUpgradeableProxy.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -36,6 +36,7 @@ + address admin_, + bytes memory _data + ) payable ERC1967Proxy(_logic, _data) { ++ assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); + _changeAdmin(admin_); + } + diff -ruN proxy/utils/Initializable.sol proxy/utils/Initializable.sol --- proxy/utils/Initializable.sol 2022-06-06 10:42:37.000000000 -0700 -+++ proxy/utils/Initializable.sol 2022-06-06 11:21:40.000000000 -0700 ++++ proxy/utils/Initializable.sol 2022-06-06 12:25:10.000000000 -0700 @@ -59,12 +59,12 @@ * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool @@ -165,7 +437,7 @@ diff -ruN proxy/utils/Initializable.sol proxy/utils/Initializable.sol * @dev Triggered when the contract has been initialized or reinitialized. diff -ruN proxy/utils/Initializable.sol.orig proxy/utils/Initializable.sol.orig --- proxy/utils/Initializable.sol.orig 1969-12-31 16:00:00.000000000 -0800 -+++ proxy/utils/Initializable.sol.orig 2022-06-06 11:21:40.000000000 -0700 ++++ proxy/utils/Initializable.sol.orig 2022-06-06 12:25:10.000000000 -0700 @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (proxy/utils/Initializable.sol) @@ -307,7 +579,7 @@ diff -ruN proxy/utils/Initializable.sol.orig proxy/utils/Initializable.sol.orig +} diff -ruN proxy/utils/Initializable.sol.rej proxy/utils/Initializable.sol.rej --- proxy/utils/Initializable.sol.rej 1969-12-31 16:00:00.000000000 -0800 -+++ proxy/utils/Initializable.sol.rej 2022-06-06 11:21:40.000000000 -0700 ++++ proxy/utils/Initializable.sol.rej 2022-06-06 12:25:10.000000000 -0700 @@ -0,0 +1,17 @@ +*************** +*** 130,136 **** @@ -326,9 +598,75 @@ diff -ruN proxy/utils/Initializable.sol.rej proxy/utils/Initializable.sol.rej + // If the contract is initializing we ignore whether _initialized is set in order to support multiple + // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level + // of initializers, because in other contexts the contract may have been reentered. +diff -ruN security/Pausable.sol security/Pausable.sol +--- security/Pausable.sol 2022-06-14 06:30:55.000000000 -0700 ++++ security/Pausable.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -35,6 +35,13 @@ + } + + /** ++ * @dev Returns true if the contract is paused, and false otherwise. ++ */ ++ function paused() public view virtual returns (bool) { ++ return _paused; ++ } ++ ++ /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: +@@ -42,7 +49,7 @@ + * - The contract must not be paused. + */ + modifier whenNotPaused() { +- _requireNotPaused(); ++ require(!paused(), "Pausable: paused"); + _; + } + +@@ -54,29 +61,8 @@ + * - The contract must be paused. + */ + modifier whenPaused() { +- _requirePaused(); +- _; +- } +- +- /** +- * @dev Returns true if the contract is paused, and false otherwise. +- */ +- function paused() public view virtual returns (bool) { +- return _paused; +- } +- +- /** +- * @dev Throws if the contract is paused. +- */ +- function _requireNotPaused() internal view virtual { +- require(!paused(), "Pausable: paused"); +- } +- +- /** +- * @dev Throws if the contract is not paused. +- */ +- function _requirePaused() internal view virtual { + require(paused(), "Pausable: not paused"); ++ _; + } + + /** diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol --- token/ERC1155/ERC1155.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC1155/ERC1155.sol 2022-06-06 11:23:46.000000000 -0700 ++++ token/ERC1155/ERC1155.sol 2022-06-15 16:37:15.000000000 -0700 +@@ -21,7 +21,7 @@ + using Address for address; + + // Mapping from token ID to account balances +- mapping(uint256 => mapping(address => uint256)) private _balances; ++ mapping(uint256 => mapping(address => uint256)) internal _balances; // MUNGED private => internal + + // Mapping from account to operator approvals + mapping(address => mapping(address => bool)) private _operatorApprovals; @@ -471,7 +471,7 @@ uint256 id, uint256 amount, @@ -349,7 +687,7 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol --- token/ERC20/ERC20.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC20/ERC20.sol 2022-06-06 11:21:40.000000000 -0700 ++++ token/ERC20/ERC20.sol 2022-06-06 12:25:10.000000000 -0700 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -359,9 +697,30 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); +diff -ruN token/ERC20/README.adoc token/ERC20/README.adoc +--- token/ERC20/README.adoc 2022-06-15 14:13:43.000000000 -0700 ++++ token/ERC20/README.adoc 2022-06-06 12:25:10.000000000 -0700 +@@ -24,7 +24,7 @@ + * {ERC20Votes}: support for voting and vote delegation. + * {ERC20VotesComp}: support for voting and vote delegation (compatible with Compound's token, with uint96 restrictions). + * {ERC20Wrapper}: wrapper to create an ERC20 backed by another ERC20, with deposit and withdraw methods. Useful in conjunction with {ERC20Votes}. +-* {ERC4626}: tokenized vault that manages shares (represented as ERC20) that are backed by assets (another ERC20). ++* {ERC20TokenizedVault}: tokenized vault that manages shares (represented as ERC20) that are backed by assets (another ERC20). + + Finally, there are some utilities to interact with ERC20 contracts in various ways. + +@@ -63,7 +63,7 @@ + + {{ERC20FlashMint}} + +-{{ERC4626}} ++{{ERC20TokenizedVault}} + + == Draft EIPs + diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol --- token/ERC20/extensions/ERC20FlashMint.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-06-06 11:21:40.000000000 -0700 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-06-06 12:25:10.000000000 -0700 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -375,9 +734,230 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 /** * @dev Returns the receiver address of the flash fee. By default this * implementation returns the address(0) which means the fee amount will be burnt. +diff -ruN token/ERC20/extensions/ERC20TokenizedVault.sol token/ERC20/extensions/ERC20TokenizedVault.sol +--- token/ERC20/extensions/ERC20TokenizedVault.sol 1969-12-31 16:00:00.000000000 -0800 ++++ token/ERC20/extensions/ERC20TokenizedVault.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -0,0 +1,217 @@ ++// SPDX-License-Identifier: MIT ++ ++pragma solidity ^0.8.0; ++ ++import "../ERC20.sol"; ++import "../utils/SafeERC20.sol"; ++import "../../../interfaces/IERC4626.sol"; ++import "../../../utils/math/Math.sol"; ++ ++/** ++ * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in ++ * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. ++ * ++ * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for ++ * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends ++ * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this ++ * contract and not the "assets" token which is an independent contract. ++ * ++ * _Available since v4.7._ ++ */ ++abstract contract ERC20TokenizedVault is ERC20, IERC4626 { ++ using Math for uint256; ++ ++ IERC20Metadata private immutable _asset; ++ ++ /** ++ * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). ++ */ ++ constructor(IERC20Metadata asset_) { ++ _asset = asset_; ++ } ++ ++ /** @dev See {IERC4262-asset} */ ++ function asset() public view virtual override returns (address) { ++ return address(_asset); ++ } ++ ++ /** @dev See {IERC4262-totalAssets} */ ++ function totalAssets() public view virtual override returns (uint256) { ++ return _asset.balanceOf(address(this)); ++ } ++ ++ /** @dev See {IERC4262-convertToShares} */ ++ function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) { ++ return _convertToShares(assets, Math.Rounding.Down); ++ } ++ ++ /** @dev See {IERC4262-convertToAssets} */ ++ function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) { ++ return _convertToAssets(shares, Math.Rounding.Down); ++ } ++ ++ /** @dev See {IERC4262-maxDeposit} */ ++ function maxDeposit(address) public view virtual override returns (uint256) { ++ return _isVaultCollateralized() ? type(uint256).max : 0; ++ } ++ ++ /** @dev See {IERC4262-maxMint} */ ++ function maxMint(address) public view virtual override returns (uint256) { ++ return type(uint256).max; ++ } ++ ++ /** @dev See {IERC4262-maxWithdraw} */ ++ function maxWithdraw(address owner) public view virtual override returns (uint256) { ++ return _convertToAssets(balanceOf(owner), Math.Rounding.Down); ++ } ++ ++ /** @dev See {IERC4262-maxRedeem} */ ++ function maxRedeem(address owner) public view virtual override returns (uint256) { ++ return balanceOf(owner); ++ } ++ ++ /** @dev See {IERC4262-previewDeposit} */ ++ function previewDeposit(uint256 assets) public view virtual override returns (uint256) { ++ return _convertToShares(assets, Math.Rounding.Down); ++ } ++ ++ /** @dev See {IERC4262-previewMint} */ ++ function previewMint(uint256 shares) public view virtual override returns (uint256) { ++ return _convertToAssets(shares, Math.Rounding.Up); ++ } ++ ++ /** @dev See {IERC4262-previewWithdraw} */ ++ function previewWithdraw(uint256 assets) public view virtual override returns (uint256) { ++ return _convertToShares(assets, Math.Rounding.Up); ++ } ++ ++ /** @dev See {IERC4262-previewRedeem} */ ++ function previewRedeem(uint256 shares) public view virtual override returns (uint256) { ++ return _convertToAssets(shares, Math.Rounding.Down); ++ } ++ ++ /** @dev See {IERC4262-deposit} */ ++ function deposit(uint256 assets, address receiver) public virtual override returns (uint256) { ++ require(assets <= maxDeposit(receiver), "ERC20TokenizedVault: deposit more than max"); ++ ++ uint256 shares = previewDeposit(assets); ++ _deposit(_msgSender(), receiver, assets, shares); ++ ++ return shares; ++ } ++ ++ /** @dev See {IERC4262-mint} */ ++ function mint(uint256 shares, address receiver) public virtual override returns (uint256) { ++ require(shares <= maxMint(receiver), "ERC20TokenizedVault: mint more than max"); ++ ++ uint256 assets = previewMint(shares); ++ _deposit(_msgSender(), receiver, assets, shares); ++ ++ return assets; ++ } ++ ++ /** @dev See {IERC4262-withdraw} */ ++ function withdraw( ++ uint256 assets, ++ address receiver, ++ address owner ++ ) public virtual override returns (uint256) { ++ require(assets <= maxWithdraw(owner), "ERC20TokenizedVault: withdraw more than max"); ++ ++ uint256 shares = previewWithdraw(assets); ++ _withdraw(_msgSender(), receiver, owner, assets, shares); ++ ++ return shares; ++ } ++ ++ /** @dev See {IERC4262-redeem} */ ++ function redeem( ++ uint256 shares, ++ address receiver, ++ address owner ++ ) public virtual override returns (uint256) { ++ require(shares <= maxRedeem(owner), "ERC20TokenizedVault: redeem more than max"); ++ ++ uint256 assets = previewRedeem(shares); ++ _withdraw(_msgSender(), receiver, owner, assets, shares); ++ ++ return assets; ++ } ++ ++ /** ++ * @dev Internal convertion function (from assets to shares) with support for rounding direction ++ * ++ * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset ++ * would represent an infinite amout of shares. ++ */ ++ function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) { ++ uint256 supply = totalSupply(); ++ return ++ (assets == 0 || supply == 0) ++ ? assets.mulDiv(10**decimals(), 10**_asset.decimals(), rounding) ++ : assets.mulDiv(supply, totalAssets(), rounding); ++ } ++ ++ /** ++ * @dev Internal convertion function (from shares to assets) with support for rounding direction ++ */ ++ function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) { ++ uint256 supply = totalSupply(); ++ return ++ (supply == 0) ++ ? shares.mulDiv(10**_asset.decimals(), 10**decimals(), rounding) ++ : shares.mulDiv(totalAssets(), supply, rounding); ++ } ++ ++ /** ++ * @dev Deposit/mint common workflow ++ */ ++ function _deposit( ++ address caller, ++ address receiver, ++ uint256 assets, ++ uint256 shares ++ ) private { ++ // If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the ++ // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, ++ // calls the vault, which is assumed not malicious. ++ // ++ // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the ++ // assets are transfered and before the shares are minted, which is a valid state. ++ // slither-disable-next-line reentrancy-no-eth ++ SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); ++ _mint(receiver, shares); ++ ++ emit Deposit(caller, receiver, assets, shares); ++ } ++ ++ /** ++ * @dev Withdraw/redeem common workflow ++ */ ++ function _withdraw( ++ address caller, ++ address receiver, ++ address owner, ++ uint256 assets, ++ uint256 shares ++ ) private { ++ if (caller != owner) { ++ _spendAllowance(owner, caller, shares); ++ } ++ ++ // If _asset is ERC777, `transfer` can trigger trigger a reentrancy AFTER the transfer happens through the ++ // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, ++ // calls the vault, which is assumed not malicious. ++ // ++ // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the ++ // shares are burned and after the assets are transfered, which is a valid state. ++ _burn(owner, shares); ++ SafeERC20.safeTransfer(_asset, receiver, assets); ++ ++ emit Withdraw(caller, receiver, owner, assets, shares); ++ } ++ ++ function _isVaultCollateralized() private view returns (bool) { ++ return totalAssets() > 0 || totalSupply() == 0; ++ } ++} diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol --- token/ERC20/extensions/ERC20Votes.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-06-06 11:21:40.000000000 -0700 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-06-06 12:25:10.000000000 -0700 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -400,7 +980,7 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol --- token/ERC20/extensions/ERC20Wrapper.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-06-06 11:21:40.000000000 -0700 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-06-06 12:25:10.000000000 -0700 @@ -55,7 +55,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -410,9 +990,264 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr uint256 value = underlying.balanceOf(address(this)) - totalSupply(); _mint(account, value); return value; +diff -ruN token/ERC20/extensions/ERC4626.sol token/ERC20/extensions/ERC4626.sol +--- token/ERC20/extensions/ERC4626.sol 2022-06-15 14:13:43.000000000 -0700 ++++ token/ERC20/extensions/ERC4626.sol 1969-12-31 16:00:00.000000000 -0800 +@@ -1,217 +0,0 @@ +-// SPDX-License-Identifier: MIT +- +-pragma solidity ^0.8.0; +- +-import "../ERC20.sol"; +-import "../utils/SafeERC20.sol"; +-import "../../../interfaces/IERC4626.sol"; +-import "../../../utils/math/Math.sol"; +- +-/** +- * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in +- * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. +- * +- * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for +- * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends +- * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this +- * contract and not the "assets" token which is an independent contract. +- * +- * _Available since v4.7._ +- */ +-abstract contract ERC4626 is ERC20, IERC4626 { +- using Math for uint256; +- +- IERC20Metadata private immutable _asset; +- +- /** +- * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). +- */ +- constructor(IERC20Metadata asset_) { +- _asset = asset_; +- } +- +- /** @dev See {IERC4262-asset} */ +- function asset() public view virtual override returns (address) { +- return address(_asset); +- } +- +- /** @dev See {IERC4262-totalAssets} */ +- function totalAssets() public view virtual override returns (uint256) { +- return _asset.balanceOf(address(this)); +- } +- +- /** @dev See {IERC4262-convertToShares} */ +- function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) { +- return _convertToShares(assets, Math.Rounding.Down); +- } +- +- /** @dev See {IERC4262-convertToAssets} */ +- function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) { +- return _convertToAssets(shares, Math.Rounding.Down); +- } +- +- /** @dev See {IERC4262-maxDeposit} */ +- function maxDeposit(address) public view virtual override returns (uint256) { +- return _isVaultCollateralized() ? type(uint256).max : 0; +- } +- +- /** @dev See {IERC4262-maxMint} */ +- function maxMint(address) public view virtual override returns (uint256) { +- return type(uint256).max; +- } +- +- /** @dev See {IERC4262-maxWithdraw} */ +- function maxWithdraw(address owner) public view virtual override returns (uint256) { +- return _convertToAssets(balanceOf(owner), Math.Rounding.Down); +- } +- +- /** @dev See {IERC4262-maxRedeem} */ +- function maxRedeem(address owner) public view virtual override returns (uint256) { +- return balanceOf(owner); +- } +- +- /** @dev See {IERC4262-previewDeposit} */ +- function previewDeposit(uint256 assets) public view virtual override returns (uint256) { +- return _convertToShares(assets, Math.Rounding.Down); +- } +- +- /** @dev See {IERC4262-previewMint} */ +- function previewMint(uint256 shares) public view virtual override returns (uint256) { +- return _convertToAssets(shares, Math.Rounding.Up); +- } +- +- /** @dev See {IERC4262-previewWithdraw} */ +- function previewWithdraw(uint256 assets) public view virtual override returns (uint256) { +- return _convertToShares(assets, Math.Rounding.Up); +- } +- +- /** @dev See {IERC4262-previewRedeem} */ +- function previewRedeem(uint256 shares) public view virtual override returns (uint256) { +- return _convertToAssets(shares, Math.Rounding.Down); +- } +- +- /** @dev See {IERC4262-deposit} */ +- function deposit(uint256 assets, address receiver) public virtual override returns (uint256) { +- require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max"); +- +- uint256 shares = previewDeposit(assets); +- _deposit(_msgSender(), receiver, assets, shares); +- +- return shares; +- } +- +- /** @dev See {IERC4262-mint} */ +- function mint(uint256 shares, address receiver) public virtual override returns (uint256) { +- require(shares <= maxMint(receiver), "ERC4626: mint more than max"); +- +- uint256 assets = previewMint(shares); +- _deposit(_msgSender(), receiver, assets, shares); +- +- return assets; +- } +- +- /** @dev See {IERC4262-withdraw} */ +- function withdraw( +- uint256 assets, +- address receiver, +- address owner +- ) public virtual override returns (uint256) { +- require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max"); +- +- uint256 shares = previewWithdraw(assets); +- _withdraw(_msgSender(), receiver, owner, assets, shares); +- +- return shares; +- } +- +- /** @dev See {IERC4262-redeem} */ +- function redeem( +- uint256 shares, +- address receiver, +- address owner +- ) public virtual override returns (uint256) { +- require(shares <= maxRedeem(owner), "ERC4626: redeem more than max"); +- +- uint256 assets = previewRedeem(shares); +- _withdraw(_msgSender(), receiver, owner, assets, shares); +- +- return assets; +- } +- +- /** +- * @dev Internal convertion function (from assets to shares) with support for rounding direction +- * +- * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset +- * would represent an infinite amout of shares. +- */ +- function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) { +- uint256 supply = totalSupply(); +- return +- (assets == 0 || supply == 0) +- ? assets.mulDiv(10**decimals(), 10**_asset.decimals(), rounding) +- : assets.mulDiv(supply, totalAssets(), rounding); +- } +- +- /** +- * @dev Internal convertion function (from shares to assets) with support for rounding direction +- */ +- function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) { +- uint256 supply = totalSupply(); +- return +- (supply == 0) +- ? shares.mulDiv(10**_asset.decimals(), 10**decimals(), rounding) +- : shares.mulDiv(totalAssets(), supply, rounding); +- } +- +- /** +- * @dev Deposit/mint common workflow +- */ +- function _deposit( +- address caller, +- address receiver, +- uint256 assets, +- uint256 shares +- ) private { +- // If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the +- // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, +- // calls the vault, which is assumed not malicious. +- // +- // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the +- // assets are transfered and before the shares are minted, which is a valid state. +- // slither-disable-next-line reentrancy-no-eth +- SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); +- _mint(receiver, shares); +- +- emit Deposit(caller, receiver, assets, shares); +- } +- +- /** +- * @dev Withdraw/redeem common workflow +- */ +- function _withdraw( +- address caller, +- address receiver, +- address owner, +- uint256 assets, +- uint256 shares +- ) private { +- if (caller != owner) { +- _spendAllowance(owner, caller, shares); +- } +- +- // If _asset is ERC777, `transfer` can trigger trigger a reentrancy AFTER the transfer happens through the +- // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, +- // calls the vault, which is assumed not malicious. +- // +- // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the +- // shares are burned and after the assets are transfered, which is a valid state. +- _burn(owner, shares); +- SafeERC20.safeTransfer(_asset, receiver, assets); +- +- emit Withdraw(caller, receiver, owner, assets, shares); +- } +- +- function _isVaultCollateralized() private view returns (bool) { +- return totalAssets() > 0 || totalSupply() == 0; +- } +-} +diff -ruN token/ERC20/utils/SafeERC20.sol token/ERC20/utils/SafeERC20.sol +--- token/ERC20/utils/SafeERC20.sol 2022-06-14 06:30:55.000000000 -0700 ++++ token/ERC20/utils/SafeERC20.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -4,7 +4,6 @@ + pragma solidity ^0.8.0; + + import "../IERC20.sol"; +-import "../extensions/draft-IERC20Permit.sol"; + import "../../../utils/Address.sol"; + + /** +@@ -80,22 +79,6 @@ + } + } + +- function safePermit( +- IERC20Permit token, +- address owner, +- address spender, +- uint256 value, +- uint256 deadline, +- uint8 v, +- bytes32 r, +- bytes32 s +- ) internal { +- uint256 nonceBefore = token.nonces(owner); +- token.permit(owner, spender, value, deadline, v, r, s); +- uint256 nonceAfter = token.nonces(owner); +- require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); +- } +- + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol --- token/ERC721/extensions/draft-ERC721Votes.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-06-06 11:21:40.000000000 -0700 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-06-06 12:25:10.000000000 -0700 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. @@ -424,7 +1259,7 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ } diff -ruN utils/Address.sol utils/Address.sol --- utils/Address.sol 2022-06-06 10:42:37.000000000 -0700 -+++ utils/Address.sol 2022-06-06 11:21:40.000000000 -0700 ++++ utils/Address.sol 2022-06-06 12:25:10.000000000 -0700 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage @@ -433,3 +1268,85 @@ diff -ruN utils/Address.sol utils/Address.sol require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); +diff -ruN utils/math/Math.sol utils/math/Math.sol +--- utils/math/Math.sol 2022-06-14 06:30:55.000000000 -0700 ++++ utils/math/Math.sol 2022-06-06 12:25:10.000000000 -0700 +@@ -149,78 +149,4 @@ + } + return result; + } +- +- /** +- * @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down. +- * +- * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). +- */ +- function sqrt(uint256 a) internal pure returns (uint256) { +- if (a == 0) { +- return 0; +- } +- +- // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. +- // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have +- // `msb(a) <= a < 2*msb(a)`. +- // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`. +- // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`. +- // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a +- // good first aproximation of `sqrt(a)` with at least 1 correct bit. +- uint256 result = 1; +- uint256 x = a; +- if (x >> 128 > 0) { +- x >>= 128; +- result <<= 64; +- } +- if (x >> 64 > 0) { +- x >>= 64; +- result <<= 32; +- } +- if (x >> 32 > 0) { +- x >>= 32; +- result <<= 16; +- } +- if (x >> 16 > 0) { +- x >>= 16; +- result <<= 8; +- } +- if (x >> 8 > 0) { +- x >>= 8; +- result <<= 4; +- } +- if (x >> 4 > 0) { +- x >>= 4; +- result <<= 2; +- } +- if (x >> 2 > 0) { +- result <<= 1; +- } +- +- // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, +- // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at +- // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision +- // into the expected uint128 result. +- unchecked { +- result = (result + a / result) >> 1; +- result = (result + a / result) >> 1; +- result = (result + a / result) >> 1; +- result = (result + a / result) >> 1; +- result = (result + a / result) >> 1; +- result = (result + a / result) >> 1; +- result = (result + a / result) >> 1; +- return min(result, a / result); +- } +- } +- +- /** +- * @notice Calculates sqrt(a), following the selected rounding direction. +- */ +- function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { +- uint256 result = sqrt(a); +- if (rounding == Rounding.Up && result * result < a) { +- result += 1; +- } +- return result; +- } + } diff --git a/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol b/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol index 7bea70487..6702ede58 100644 --- a/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol +++ b/certora/harnesses/ERC1155/ERC1155SupplyHarness.sol @@ -1,14 +1,61 @@ import "../../munged/token/ERC1155/extensions/ERC1155Supply.sol"; + contract ERC1155SupplyHarness is ERC1155Supply { - constructor(string memory uri_) - ERC1155(uri_) - {} + address public owner; + constructor(string memory uri_) ERC1155(uri_) { + owner = msg.sender; + } // workaround for problem caused by `exists` being a CVL keyword function exists_wrapper(uint256 id) public view virtual returns (bool) { return exists(id); } + // These rules were not implemented in the base but there are changes in supply + // that are affected by the internal contracts so we implemented them. We assume + // only the owner can call any of these functions to be able to test them but also + // limit false positives. + + modifier onlyOwner { + require(msg.sender == owner); + _; + } + + function burn( address from, uint256 id, uint256 amount) public virtual onlyOwner { + _burn(from, id, amount); + } + function burnBatch( + address from, + uint256[] memory ids, + uint256[] memory amounts + ) public virtual onlyOwner { + _burnBatch(from, ids, amounts); + } + + function mint( + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual onlyOwner { + _mint(to, id, amount, data); + } + + function mintBatch( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual onlyOwner { + _mintBatch(to, ids, amounts, data); + } + + // In order to check the invariant that zero address never holds any tokens, we need to remove the require + // from this function. + function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { + // require(account != address(0), "ERC1155: address zero is not a valid owner"); + return _balances[id][account]; + } } diff --git a/certora/scripts/Round3/verifyERC1155Burnable.sh b/certora/scripts/Round3/verifyERC1155Burnable.sh index 0231296c7..6d60cf746 100644 --- a/certora/scripts/Round3/verifyERC1155Burnable.sh +++ b/certora/scripts/Round3/verifyERC1155Burnable.sh @@ -1,7 +1,7 @@ certoraRun \ certora/harnesses/ERC1155/ERC1155BurnableHarness.sol \ --verify ERC1155BurnableHarness:certora/specs/ERC1155Burnable.spec \ - --solc solc8.2 \ + --solc solc8.4 \ --optimistic_loop \ --loop_iter 3 \ --msg "ERC1155 Burnable verification all rules" diff --git a/certora/scripts/Round3/verifyERC1155Pausable.sh b/certora/scripts/Round3/verifyERC1155Pausable.sh index 0292037b6..bfddf66de 100755 --- a/certora/scripts/Round3/verifyERC1155Pausable.sh +++ b/certora/scripts/Round3/verifyERC1155Pausable.sh @@ -1,7 +1,7 @@ certoraRun \ certora/harnesses/ERC1155/ERC1155PausableHarness.sol \ --verify ERC1155PausableHarness:certora/specs/ERC1155Pausable.spec \ - --solc solc8.2 \ + --solc solc8.4 \ --optimistic_loop \ --loop_iter 3 \ --msg "ERC1155 Pausable verification all rules" diff --git a/certora/scripts/Round3/verifyERC1155Supply.sh b/certora/scripts/Round3/verifyERC1155Supply.sh index 4677e0694..1b688c6e3 100755 --- a/certora/scripts/Round3/verifyERC1155Supply.sh +++ b/certora/scripts/Round3/verifyERC1155Supply.sh @@ -1,7 +1,7 @@ certoraRun \ certora/harnesses/ERC1155/ERC1155SupplyHarness.sol \ --verify ERC1155SupplyHarness:certora/specs/ERC1155Supply.spec \ - --solc solc8.2 \ + --solc solc8.4 \ --optimistic_loop \ --loop_iter 3 \ --msg "ERC1155 Supply verification all rules" diff --git a/certora/scripts/Round3/verifyGovernorPreventLateQuorum.sh b/certora/scripts/Round3/verifyGovernorPreventLateQuorum.sh index b99f0b8aa..81720cae0 100644 --- a/certora/scripts/Round3/verifyGovernorPreventLateQuorum.sh +++ b/certora/scripts/Round3/verifyGovernorPreventLateQuorum.sh @@ -1,7 +1,7 @@ certoraRun \ certora/harnesses/ERC721VotesHarness.sol certora/munged/governance/TimelockController.sol certora/harnesses/GovernorPreventLateQuorumHarness.sol \ --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorPreventLateQuorum.spec \ - --solc solc8.2 \ + --solc solc8.4 \ --optimistic_loop \ --loop_iter 1 \ --msg "GovernorPreventLateQuorum verification all rules" \ diff --git a/certora/scripts/Round3/verifyInitializable.sh b/certora/scripts/Round3/verifyInitializable.sh index 291f1757e..30dba2bb8 100644 --- a/certora/scripts/Round3/verifyInitializable.sh +++ b/certora/scripts/Round3/verifyInitializable.sh @@ -1,7 +1,7 @@ certoraRun \ certora/harnesses/InitializableComplexHarness.sol \ --verify InitializableComplexHarness:certora/specs/Initializable.spec \ - --solc solc8.2 \ + --solc solc8.4 \ --optimistic_loop \ --loop_iter 3 \ --msg "Initializable verificaiton all rules on complex harness" \ diff --git a/certora/specs/ERC1155Supply.spec b/certora/specs/ERC1155Supply.spec index a33aaade6..0060ab146 100644 --- a/certora/specs/ERC1155Supply.spec +++ b/certora/specs/ERC1155Supply.spec @@ -3,6 +3,7 @@ methods { totalSupply(uint256) returns uint256 envfree balanceOf(address, uint256) returns uint256 envfree exists_wrapper(uint256) returns bool envfree + owner() returns address envfree } /// given two different token ids, if totalSupply for one changes, then @@ -19,6 +20,7 @@ filtered { uint256 token2_before = totalSupply(token2); env e; calldataarg args; + require e.msg.sender != owner(); // owner can call mintBatch and burnBatch in our harness f(e, args); uint256 token1_after = totalSupply(token1); From 3e6045155e5432b1c25a27ab97c61d689ec653e3 Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Thu, 11 Aug 2022 20:40:13 -0700 Subject: [PATCH 251/254] reorganized scripts --- certora/scripts/{old => Round1}/Governor.sh | 0 .../GovernorCountingSimple-counting.sh | 0 .../WizardControlFirstPriority.sh | 0 .../scripts/{old => Round1}/WizardFirstTry.sh | 0 certora/scripts/{old => Round1}/sanity.sh | 0 .../scripts/{old => Round1}/sanityGovernor.sh | 0 .../scripts/{old => Round1}/sanityTokens.sh | 0 certora/scripts/{old => Round1}/verifyAll.sh | 0 .../scripts/{old => Round1}/verifyGovernor.sh | 0 .../{old => Round2}/verifyAccessControl.sh | 3 ++- certora/scripts/Round2/verifyAll2.sh | 11 ++++++++ .../scripts/{old => Round2}/verifyERC1155.sh | 1 + .../{old => Round2}/verifyERC20FlashMint.sh | 3 ++- certora/scripts/Round2/verifyERC20Votes.sh | 13 ++++++++++ .../{old => Round2}/verifyERC20Wrapper.sh | 3 ++- certora/scripts/Round2/verifyERC721Votes.sh | 14 ++++++++++ .../scripts/{old => Round2}/verifyTimelock.sh | 7 +++-- certora/scripts/old/ERC20VotesRule.sh | 23 ---------------- certora/scripts/old/verifyAll2.sh | 7 ----- certora/scripts/old/verifyERC20Votes.sh | 25 ------------------ certora/scripts/old/verifyERC721Votes.sh | 26 ------------------- 21 files changed, 50 insertions(+), 86 deletions(-) rename certora/scripts/{old => Round1}/Governor.sh (100%) rename certora/scripts/{old => Round1}/GovernorCountingSimple-counting.sh (100%) rename certora/scripts/{old => Round1}/WizardControlFirstPriority.sh (100%) rename certora/scripts/{old => Round1}/WizardFirstTry.sh (100%) rename certora/scripts/{old => Round1}/sanity.sh (100%) rename certora/scripts/{old => Round1}/sanityGovernor.sh (100%) rename certora/scripts/{old => Round1}/sanityTokens.sh (100%) rename certora/scripts/{old => Round1}/verifyAll.sh (100%) rename certora/scripts/{old => Round1}/verifyGovernor.sh (100%) rename certora/scripts/{old => Round2}/verifyAccessControl.sh (77%) create mode 100644 certora/scripts/Round2/verifyAll2.sh rename certora/scripts/{old => Round2}/verifyERC1155.sh (94%) rename certora/scripts/{old => Round2}/verifyERC20FlashMint.sh (85%) create mode 100644 certora/scripts/Round2/verifyERC20Votes.sh rename certora/scripts/{old => Round2}/verifyERC20Wrapper.sh (82%) create mode 100644 certora/scripts/Round2/verifyERC721Votes.sh rename certora/scripts/{old => Round2}/verifyTimelock.sh (71%) delete mode 100644 certora/scripts/old/ERC20VotesRule.sh delete mode 100644 certora/scripts/old/verifyAll2.sh delete mode 100644 certora/scripts/old/verifyERC20Votes.sh delete mode 100644 certora/scripts/old/verifyERC721Votes.sh diff --git a/certora/scripts/old/Governor.sh b/certora/scripts/Round1/Governor.sh similarity index 100% rename from certora/scripts/old/Governor.sh rename to certora/scripts/Round1/Governor.sh diff --git a/certora/scripts/old/GovernorCountingSimple-counting.sh b/certora/scripts/Round1/GovernorCountingSimple-counting.sh similarity index 100% rename from certora/scripts/old/GovernorCountingSimple-counting.sh rename to certora/scripts/Round1/GovernorCountingSimple-counting.sh diff --git a/certora/scripts/old/WizardControlFirstPriority.sh b/certora/scripts/Round1/WizardControlFirstPriority.sh similarity index 100% rename from certora/scripts/old/WizardControlFirstPriority.sh rename to certora/scripts/Round1/WizardControlFirstPriority.sh diff --git a/certora/scripts/old/WizardFirstTry.sh b/certora/scripts/Round1/WizardFirstTry.sh similarity index 100% rename from certora/scripts/old/WizardFirstTry.sh rename to certora/scripts/Round1/WizardFirstTry.sh diff --git a/certora/scripts/old/sanity.sh b/certora/scripts/Round1/sanity.sh similarity index 100% rename from certora/scripts/old/sanity.sh rename to certora/scripts/Round1/sanity.sh diff --git a/certora/scripts/old/sanityGovernor.sh b/certora/scripts/Round1/sanityGovernor.sh similarity index 100% rename from certora/scripts/old/sanityGovernor.sh rename to certora/scripts/Round1/sanityGovernor.sh diff --git a/certora/scripts/old/sanityTokens.sh b/certora/scripts/Round1/sanityTokens.sh similarity index 100% rename from certora/scripts/old/sanityTokens.sh rename to certora/scripts/Round1/sanityTokens.sh diff --git a/certora/scripts/old/verifyAll.sh b/certora/scripts/Round1/verifyAll.sh similarity index 100% rename from certora/scripts/old/verifyAll.sh rename to certora/scripts/Round1/verifyAll.sh diff --git a/certora/scripts/old/verifyGovernor.sh b/certora/scripts/Round1/verifyGovernor.sh similarity index 100% rename from certora/scripts/old/verifyGovernor.sh rename to certora/scripts/Round1/verifyGovernor.sh diff --git a/certora/scripts/old/verifyAccessControl.sh b/certora/scripts/Round2/verifyAccessControl.sh similarity index 77% rename from certora/scripts/old/verifyAccessControl.sh rename to certora/scripts/Round2/verifyAccessControl.sh index eb706d063..6fb08381f 100644 --- a/certora/scripts/old/verifyAccessControl.sh +++ b/certora/scripts/Round2/verifyAccessControl.sh @@ -4,5 +4,6 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --cloud \ - --msg "AccessControl verification" + --msg "AccessControl verification" \ + --send_only \ No newline at end of file diff --git a/certora/scripts/Round2/verifyAll2.sh b/certora/scripts/Round2/verifyAll2.sh new file mode 100644 index 000000000..e1805a2ec --- /dev/null +++ b/certora/scripts/Round2/verifyAll2.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +make -C certora munged + +sh certora/scripts/Round2/verifyAccessControl.sh +sh certora/scripts/Round2/verifyERC20FlashMint.sh +sh certora/scripts/Round2/verifyERC20Votes.sh +sh certora/scripts/Round2/verifyERC20Wrapper.sh +sh certora/scripts/Round2/verifyERC721Votes.sh +sh certora/scripts/Round2/verifyERC1155.sh +sh certora/scripts/Round2/verifyTimelock.sh \ No newline at end of file diff --git a/certora/scripts/old/verifyERC1155.sh b/certora/scripts/Round2/verifyERC1155.sh similarity index 94% rename from certora/scripts/old/verifyERC1155.sh rename to certora/scripts/Round2/verifyERC1155.sh index d2c00df78..733882f0f 100644 --- a/certora/scripts/old/verifyERC1155.sh +++ b/certora/scripts/Round2/verifyERC1155.sh @@ -5,5 +5,6 @@ certoraRun \ --optimistic_loop \ --loop_iter 3 \ --send_only \ + --cloud \ --msg "ERC1155" diff --git a/certora/scripts/old/verifyERC20FlashMint.sh b/certora/scripts/Round2/verifyERC20FlashMint.sh similarity index 85% rename from certora/scripts/old/verifyERC20FlashMint.sh rename to certora/scripts/Round2/verifyERC20FlashMint.sh index 0af81e006..d8d03d24a 100644 --- a/certora/scripts/old/verifyERC20FlashMint.sh +++ b/certora/scripts/Round2/verifyERC20FlashMint.sh @@ -5,5 +5,6 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --cloud \ - --msg "ERC20FlashMint verification" + --msg "ERC20FlashMint verification" \ + --send_only \ No newline at end of file diff --git a/certora/scripts/Round2/verifyERC20Votes.sh b/certora/scripts/Round2/verifyERC20Votes.sh new file mode 100644 index 000000000..dea9bc695 --- /dev/null +++ b/certora/scripts/Round2/verifyERC20Votes.sh @@ -0,0 +1,13 @@ +certoraRun \ + certora/harnesses/ERC20VotesHarness.sol \ + --verify ERC20VotesHarness:certora/specs/ERC20Votes.spec \ + --solc solc8.2 \ + --disableLocalTypeChecking \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --cloud \ + --send_only \ + --msg "ERC20Votes" \ + + + # --staging "alex/new-dt-hashing-alpha" \ diff --git a/certora/scripts/old/verifyERC20Wrapper.sh b/certora/scripts/Round2/verifyERC20Wrapper.sh similarity index 82% rename from certora/scripts/old/verifyERC20Wrapper.sh rename to certora/scripts/Round2/verifyERC20Wrapper.sh index e1ef85ef0..6b60c9556 100644 --- a/certora/scripts/old/verifyERC20Wrapper.sh +++ b/certora/scripts/Round2/verifyERC20Wrapper.sh @@ -5,5 +5,6 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --cloud \ - --msg "ERC20Wrapper verification" + --msg "ERC20Wrapper verification" \ + --send_only \ No newline at end of file diff --git a/certora/scripts/Round2/verifyERC721Votes.sh b/certora/scripts/Round2/verifyERC721Votes.sh new file mode 100644 index 000000000..7e023ef28 --- /dev/null +++ b/certora/scripts/Round2/verifyERC721Votes.sh @@ -0,0 +1,14 @@ +certoraRun \ + certora/harnesses/ERC721VotesHarness.sol \ + certora/munged/utils/Checkpoints.sol \ + --verify ERC721VotesHarness:certora/specs/ERC721Votes.spec \ + --solc solc8.2 \ + --disableLocalTypeChecking \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --cloud \ + --send_only \ + --msg "ERC721Votes" + + # --staging "alex/new-dt-hashing-alpha" \ + diff --git a/certora/scripts/old/verifyTimelock.sh b/certora/scripts/Round2/verifyTimelock.sh similarity index 71% rename from certora/scripts/old/verifyTimelock.sh rename to certora/scripts/Round2/verifyTimelock.sh index 5afc11911..270f76db9 100644 --- a/certora/scripts/old/verifyTimelock.sh +++ b/certora/scripts/Round2/verifyTimelock.sh @@ -4,7 +4,10 @@ certoraRun \ --solc solc8.2 \ --optimistic_loop \ --loop_iter 3 \ - --staging alex/new-dt-hashing-alpha \ + --cloud \ --settings -byteMapHashingPrecision=32 \ - --msg "TimelockController verification" + --msg "TimelockController verification" \ + --send_only \ + + # --staging alex/new-dt-hashing-alpha \ \ No newline at end of file diff --git a/certora/scripts/old/ERC20VotesRule.sh b/certora/scripts/old/ERC20VotesRule.sh deleted file mode 100644 index 28517b730..000000000 --- a/certora/scripts/old/ERC20VotesRule.sh +++ /dev/null @@ -1,23 +0,0 @@ -if [ -z "$2" ] - then - echo "Incorrect number of arguments" - echo "" - echo "Usage: (from git root)" - echo " ./certora/scripts/`basename $0` [message describing the run] [rule or invariant]" - echo "" - exit 1 -fi - -rule=$1 -msg=$2 -shift 2 - -certoraRun \ - certora/harnesses/ERC20VotesHarness.sol \ - --verify ERC20VotesHarness:certora/specs/ERC20Votes.spec \ - --solc solc8.2 \ - --optimistic_loop \ - --rule ${rule} \ - --msg "${msg}" \ - --staging "alex/new-dt-hashing-alpha" \ - # --rule_sanity \ \ No newline at end of file diff --git a/certora/scripts/old/verifyAll2.sh b/certora/scripts/old/verifyAll2.sh deleted file mode 100644 index 43c73e477..000000000 --- a/certora/scripts/old/verifyAll2.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -make -C certora munged - -sh certora/scripts/verifyAllSasha -sh certora/scripts/verifyERC20Votes.sh "checking ERC20Votes.spec on ERC20Votes.sol" -sh certora/scripts/verifyERC721Votes.sh "checking ERC721Votes.spec on draft-ERC721Votes.sol and Votes.sol" \ No newline at end of file diff --git a/certora/scripts/old/verifyERC20Votes.sh b/certora/scripts/old/verifyERC20Votes.sh deleted file mode 100644 index 8bc5ed9fc..000000000 --- a/certora/scripts/old/verifyERC20Votes.sh +++ /dev/null @@ -1,25 +0,0 @@ -make -C certora munged - -if [ -z "$1" ] - then - echo "Incorrect number of arguments" - echo "" - echo "Usage: (from git root)" - echo " ./certora/scripts/`basename $0` [message describing the run]" - echo "" - exit 1 -fi - -msg=$1 -shift 1 - -certoraRun \ - certora/harnesses/ERC20VotesHarness.sol \ - --verify ERC20VotesHarness:certora/specs/ERC20Votes.spec \ - --solc solc8.2 \ - --disableLocalTypeChecking \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --send_only \ - --staging "alex/new-dt-hashing-alpha" \ - --msg "${msg}" \ diff --git a/certora/scripts/old/verifyERC721Votes.sh b/certora/scripts/old/verifyERC721Votes.sh deleted file mode 100644 index e2a7320f9..000000000 --- a/certora/scripts/old/verifyERC721Votes.sh +++ /dev/null @@ -1,26 +0,0 @@ -make -C certora munged - -if [ -z "$1" ] - then - echo "Incorrect number of arguments" - echo "" - echo "Usage: (from git root)" - echo " ./certora/scripts/`basename $0` [message describing the run]" - echo "" - exit 1 -fi - -msg=$1 -shift 1 - -certoraRun \ - certora/harnesses/ERC721VotesHarness.sol \ - certora/munged/utils/Checkpoints.sol \ - --verify ERC721VotesHarness:certora/specs/ERC721Votes.spec \ - --solc solc8.2 \ - --disableLocalTypeChecking \ - --optimistic_loop \ - --settings -copyLoopUnroll=4 \ - --send_only \ - --staging "alex/new-dt-hashing-alpha" \ - --msg "${msg}" From 4a3b0bb87582da5d946a2e96c259b4044a8b63e0 Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Thu, 11 Aug 2022 21:11:01 -0700 Subject: [PATCH 252/254] filters for skipped functions --- certora/specs/ERC20Votes.spec | 20 ++++---------------- certora/specs/ERC20Wrapper.spec | 2 +- certora/specs/ERC721Votes.spec | 13 ++++--------- certora/specs/TimelockController.spec | 8 ++++---- 4 files changed, 13 insertions(+), 30 deletions(-) diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index e74509cf2..badf3f557 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -18,13 +18,10 @@ methods { mint(address, uint256) burn(address, uint256) unsafeNumCheckpoints(address) returns (uint256) envfree - // solidity generated getters _delegates(address) returns (address) envfree // external functions - - } // gets the most recent votes for a user ghost userVotes(address) returns uint224 { @@ -36,9 +33,7 @@ ghost totalVotes() returns mathint { init_state axiom totalVotes() == 0; axiom totalVotes() >= 0; } - ghost lastIndex(address) returns uint32; - // helper hook Sstore _checkpoints[KEY address account][INDEX uint32 index].votes uint224 newVotes (uint224 oldVotes) STORAGE { @@ -82,12 +77,12 @@ invariant votes_solvency() // for some checkpoint, the fromBlock is less than the current block number invariant blockNum_constrains_fromBlock(address account, uint32 index, env e) ckptFromBlock(account, index) < e.block.number + filtered { f -> !f.isView } { preserved { require index < numCheckpoints(account); } } - // numCheckpoints are less than maxInt // passes because numCheckpoints does a safeCast // invariant maxInt_constrains_numBlocks(address account) @@ -97,6 +92,7 @@ invariant blockNum_constrains_fromBlock(address account, uint32 index, env e) // passes invariant fromBlock_constrains_numBlocks(address account) numCheckpoints(account) <= ckptFromBlock(account, numCheckpoints(account) - 1) + filtered { f -> !f.isView } { preserved with(env e) { require e.block.number >= ckptFromBlock(account, numCheckpoints(account) - 1); // this should be true from the invariant above!! }} @@ -109,11 +105,13 @@ invariant fromBlock_constrains_numBlocks(address account) // passes + rule sanity invariant fromBlock_greaterThanEq_pos(address account, uint32 pos) ckptFromBlock(account, pos) >= pos + filtered { f -> !f.isView } // a larger index must have a larger fromBlock // passes + rule sanity invariant fromBlock_increasing(address account, uint32 pos, uint32 pos2) pos > pos2 => ckptFromBlock(account, pos) > ckptFromBlock(account, pos2) + filtered { f -> !f.isView } // converted from an invariant to a rule to slightly change the logic @@ -144,27 +142,21 @@ rule transfer_safe() { uint256 amount; address a; address b; require delegates(a) != delegates(b); // confirmed if they both delegate to the same person then transfer keeps the votes the same - require numCheckpoints(delegates(a)) < 1000000; require numCheckpoints(delegates(b)) < 1000000; - uint256 votesA_pre = getVotes(delegates(a)); uint256 votesB_pre = getVotes(delegates(b)); - mathint totalVotes_pre = totalVotes(); - transferFrom(e, a, b, amount); mathint totalVotes_post = totalVotes(); uint256 votesA_post = getVotes(delegates(a)); uint256 votesB_post = getVotes(delegates(b)); - // if an account that has not delegated transfers balance to an account that has, it will increase the total supply of votes assert totalVotes_pre == totalVotes_post, "transfer changed total supply"; assert delegates(a) != 0 => votesA_pre - votesA_post == amount, "A lost the wrong amount of votes"; assert delegates(b) != 0 => votesB_post - votesB_pre == amount, "B lost the wrong amount of votes"; } - // for any given function f, if the delegate is changed the function must be delegate or delegateBySig // passes rule delegates_safe(method f) filtered {f -> (f.selector != delegate(address).selector && @@ -174,14 +166,10 @@ rule delegates_safe(method f) filtered {f -> (f.selector != delegate(address).se env e; calldataarg args; address account; address pre = delegates(account); - f(e, args); - address post = delegates(account); - assert pre == post, "invalid delegate change"; } - // delegates increases the delegatee's votes by the proper amount // passes + rule sanity rule delegatee_receives_votes() { diff --git a/certora/specs/ERC20Wrapper.spec b/certora/specs/ERC20Wrapper.spec index bc95288e9..1ab9012c2 100644 --- a/certora/specs/ERC20Wrapper.spec +++ b/certora/specs/ERC20Wrapper.spec @@ -15,7 +15,7 @@ methods { // totalsupply of wrapped should be less than or equal to underlying (assuming no external transfer) - solvency invariant whatAboutTotal(env e) totalSupply(e) <= underlyingTotalSupply() - filtered { f -> f.selector != certorafallback_0().selector } + filtered { f -> f.selector != certorafallback_0().selector && !f.isView} { preserved { require underlyingBalanceOf(currentContract) <= underlyingTotalSupply(); diff --git a/certora/specs/ERC721Votes.spec b/certora/specs/ERC721Votes.spec index 79bbd563b..b4cc8ba3e 100644 --- a/certora/specs/ERC721Votes.spec +++ b/certora/specs/ERC721Votes.spec @@ -67,19 +67,11 @@ hook Sstore _checkpoints[KEY address account].fromBlock uint32 newBlock (uint32 doubleFromBlock@new(account) == (newBlock == lastFromBlock(account)); } -// sum of user balances is >= total amount of delegated votes -// blocked by tool error -invariant votes_solvency() - to_mathint(totalSupply()) >= totalVotes() -{ preserved with(env e) { - require forall address account. numCheckpoints(account) < 1000000; -} } - - // for some checkpoint, the fromBlock is less than the current block number // passes but fails rule sanity from hash on delegate by sig invariant timestamp_constrains_fromBlock(address account, uint32 index, env e) ckptFromBlock(account, index) < e.block.number + filtered { f -> !f.isView } { preserved { require index < numCheckpoints(account); @@ -95,6 +87,7 @@ invariant timestamp_constrains_fromBlock(address account, uint32 index, env e) // passes invariant fromBlock_constrains_numBlocks(address account) numCheckpoints(account) <= ckptFromBlock(account, numCheckpoints(account) - 1) + filtered { f -> !f.isView } { preserved with(env e) { require e.block.number >= ckptFromBlock(account, numCheckpoints(account) - 1); // this should be true from the invariant above!! }} @@ -107,11 +100,13 @@ invariant fromBlock_constrains_numBlocks(address account) // passes + rule sanity invariant fromBlock_greaterThanEq_pos(address account, uint32 pos) ckptFromBlock(account, pos) >= pos + filtered { f -> !f.isView } // a larger index must have a larger fromBlock // passes + rule sanity invariant fromBlock_increasing(address account, uint32 pos, uint32 pos2) pos > pos2 => ckptFromBlock(account, pos) > ckptFromBlock(account, pos2) + filtered { f -> !f.isView } // converted from an invariant to a rule to slightly change the logic diff --git a/certora/specs/TimelockController.spec b/certora/specs/TimelockController.spec index 2277fecb1..9d5df6b97 100644 --- a/certora/specs/TimelockController.spec +++ b/certora/specs/TimelockController.spec @@ -40,25 +40,25 @@ function hashIdCorrelation(bytes32 id, address target, uint256 value, bytes data // `isOperation()` correctness check invariant operationCheck(bytes32 id) getTimestamp(id) > 0 <=> isOperation(id) - +filtered { f -> !f.isView } // STATUS - verified // `isOperationPending()` correctness check invariant pendingCheck(bytes32 id) getTimestamp(id) > _DONE_TIMESTAMP() <=> isOperationPending(id) - +filtered { f -> !f.isView } // STATUS - verified // `isOperationReady()` correctness check invariant readyCheck(env e, bytes32 id) (e.block.timestamp >= getTimestamp(id) && getTimestamp(id) > 1) <=> isOperationReady(e, id) - +filtered { f -> !f.isView } // STATUS - verified // `isOperationDone()` correctness check invariant doneCheck(bytes32 id) getTimestamp(id) == _DONE_TIMESTAMP() <=> isOperationDone(id) - +filtered { f -> !f.isView } //////////////////////////////////////////////////////////////////////////// From 4820ed4ea8bf5059fa1380880fc3712f6f3bf2c5 Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Thu, 11 Aug 2022 21:27:17 -0700 Subject: [PATCH 253/254] missing erc20votes harnessing --- certora/applyHarness.patch | 233 +++++++++++++++++++++++++++---------- 1 file changed, 171 insertions(+), 62 deletions(-) diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index c1391565e..1d3ae55a5 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,12 +1,12 @@ diff -ruN .gitignore .gitignore --- .gitignore 1969-12-31 16:00:00.000000000 -0800 -+++ .gitignore 2022-06-06 12:25:10.000000000 -0700 ++++ .gitignore 2022-08-11 21:15:52.000000000 -0700 @@ -0,0 +1,2 @@ +* +!.gitignore diff -ruN access/AccessControl.sol access/AccessControl.sol ---- access/AccessControl.sol 2022-06-06 10:42:37.000000000 -0700 -+++ access/AccessControl.sol 2022-06-06 12:25:10.000000000 -0700 +--- access/AccessControl.sol 2022-08-11 21:13:55.000000000 -0700 ++++ access/AccessControl.sol 2022-08-11 21:15:52.000000000 -0700 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -17,8 +17,8 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol } diff -ruN access/Ownable.sol access/Ownable.sol ---- access/Ownable.sol 2022-06-14 06:30:55.000000000 -0700 -+++ access/Ownable.sol 2022-06-06 12:25:10.000000000 -0700 +--- access/Ownable.sol 2022-08-11 21:13:55.000000000 -0700 ++++ access/Ownable.sol 2022-08-11 21:15:52.000000000 -0700 @@ -30,14 +30,6 @@ } @@ -49,8 +49,8 @@ diff -ruN access/Ownable.sol access/Ownable.sol /** diff -ruN governance/Governor.sol governance/Governor.sol ---- governance/Governor.sol 2022-06-06 10:42:37.000000000 -0700 -+++ governance/Governor.sol 2022-06-06 12:25:10.000000000 -0700 +--- governance/Governor.sol 2022-08-11 21:13:55.000000000 -0700 ++++ governance/Governor.sol 2022-08-11 21:15:52.000000000 -0700 @@ -44,7 +44,7 @@ string private _name; @@ -61,8 +61,8 @@ diff -ruN governance/Governor.sol governance/Governor.sol // This queue keeps track of the governor operating on itself. Calls to functions protected by the // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol ---- governance/TimelockController.sol 2022-06-06 10:42:37.000000000 -0700 -+++ governance/TimelockController.sol 2022-06-06 12:25:10.000000000 -0700 +--- governance/TimelockController.sol 2022-08-11 21:13:55.000000000 -0700 ++++ governance/TimelockController.sol 2022-08-11 21:15:52.000000000 -0700 @@ -28,10 +28,10 @@ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); @@ -77,8 +77,8 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol /** * @dev Emitted when a call is scheduled as part of operation `id`. diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol ---- governance/extensions/GovernorCountingSimple.sol 2022-06-14 16:49:50.000000000 -0700 -+++ governance/extensions/GovernorCountingSimple.sol 2022-06-06 12:25:10.000000000 -0700 +--- governance/extensions/GovernorCountingSimple.sol 2022-08-11 21:13:55.000000000 -0700 ++++ governance/extensions/GovernorCountingSimple.sol 2022-08-11 21:15:52.000000000 -0700 @@ -27,7 +27,7 @@ mapping(address => bool) hasVoted; } @@ -89,8 +89,8 @@ diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions /** * @dev See {IGovernor-COUNTING_MODE}. diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol ---- governance/extensions/GovernorPreventLateQuorum.sol 2022-06-06 10:42:37.000000000 -0700 -+++ governance/extensions/GovernorPreventLateQuorum.sol 2022-06-15 17:00:30.000000000 -0700 +--- governance/extensions/GovernorPreventLateQuorum.sol 2022-08-11 21:13:55.000000000 -0700 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-08-11 21:15:52.000000000 -0700 @@ -21,8 +21,8 @@ using SafeCast for uint256; using Timers for Timers.BlockNumber; @@ -103,8 +103,8 @@ diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensi /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol ---- governance/utils/Votes.sol 2022-06-06 10:42:37.000000000 -0700 -+++ governance/utils/Votes.sol 2022-06-06 12:25:10.000000000 -0700 +--- governance/utils/Votes.sol 2022-08-11 21:13:55.000000000 -0700 ++++ governance/utils/Votes.sol 2022-08-11 21:15:52.000000000 -0700 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -178,8 +178,8 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public } diff -ruN metatx/MinimalForwarder.sol metatx/MinimalForwarder.sol ---- metatx/MinimalForwarder.sol 2022-06-14 06:30:55.000000000 -0700 -+++ metatx/MinimalForwarder.sol 2022-06-06 12:25:10.000000000 -0700 +--- metatx/MinimalForwarder.sol 2022-08-11 21:13:55.000000000 -0700 ++++ metatx/MinimalForwarder.sol 2022-08-11 21:15:52.000000000 -0700 @@ -8,11 +8,6 @@ /** @@ -194,7 +194,7 @@ diff -ruN metatx/MinimalForwarder.sol metatx/MinimalForwarder.sol using ECDSA for bytes32; diff -ruN mocks/ERC20TokenizedVaultMock.sol mocks/ERC20TokenizedVaultMock.sol --- mocks/ERC20TokenizedVaultMock.sol 1969-12-31 16:00:00.000000000 -0800 -+++ mocks/ERC20TokenizedVaultMock.sol 2022-06-06 12:25:10.000000000 -0700 ++++ mocks/ERC20TokenizedVaultMock.sol 2022-08-11 21:15:52.000000000 -0700 @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + @@ -219,7 +219,7 @@ diff -ruN mocks/ERC20TokenizedVaultMock.sol mocks/ERC20TokenizedVaultMock.sol + } +} diff -ruN mocks/ERC4626Mock.sol mocks/ERC4626Mock.sol ---- mocks/ERC4626Mock.sol 2022-06-15 14:13:43.000000000 -0700 +--- mocks/ERC4626Mock.sol 2022-08-11 21:13:55.000000000 -0700 +++ mocks/ERC4626Mock.sol 1969-12-31 16:00:00.000000000 -0800 @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT @@ -245,8 +245,8 @@ diff -ruN mocks/ERC4626Mock.sol mocks/ERC4626Mock.sol - } -} diff -ruN mocks/MathMock.sol mocks/MathMock.sol ---- mocks/MathMock.sol 2022-06-14 06:30:55.000000000 -0700 -+++ mocks/MathMock.sol 2022-06-06 12:25:10.000000000 -0700 +--- mocks/MathMock.sol 2022-08-11 21:13:55.000000000 -0700 ++++ mocks/MathMock.sol 2022-08-11 21:15:52.000000000 -0700 @@ -29,8 +29,4 @@ ) public pure returns (uint256) { return Math.mulDiv(a, b, denominator, direction); @@ -257,8 +257,8 @@ diff -ruN mocks/MathMock.sol mocks/MathMock.sol - } } diff -ruN mocks/SafeERC20Helper.sol mocks/SafeERC20Helper.sol ---- mocks/SafeERC20Helper.sol 2022-06-14 06:30:55.000000000 -0700 -+++ mocks/SafeERC20Helper.sol 2022-06-06 12:25:10.000000000 -0700 +--- mocks/SafeERC20Helper.sol 2022-08-11 21:13:55.000000000 -0700 ++++ mocks/SafeERC20Helper.sol 2022-08-11 21:15:52.000000000 -0700 @@ -4,7 +4,6 @@ import "../utils/Context.sol"; @@ -331,8 +331,8 @@ diff -ruN mocks/SafeERC20Helper.sol mocks/SafeERC20Helper.sol ERC20ReturnTrueMock(address(_token)).setAllowance(allowance_); } diff -ruN proxy/Clones.sol proxy/Clones.sol ---- proxy/Clones.sol 2022-06-14 06:30:55.000000000 -0700 -+++ proxy/Clones.sol 2022-06-06 12:25:10.000000000 -0700 +--- proxy/Clones.sol 2022-08-11 21:13:55.000000000 -0700 ++++ proxy/Clones.sol 2022-08-11 21:15:52.000000000 -0700 @@ -26,10 +26,10 @@ /// @solidity memory-safe-assembly assembly { @@ -385,8 +385,8 @@ diff -ruN proxy/Clones.sol proxy/Clones.sol } diff -ruN proxy/ERC1967/ERC1967Proxy.sol proxy/ERC1967/ERC1967Proxy.sol ---- proxy/ERC1967/ERC1967Proxy.sol 2022-06-14 06:30:55.000000000 -0700 -+++ proxy/ERC1967/ERC1967Proxy.sol 2022-06-06 12:25:10.000000000 -0700 +--- proxy/ERC1967/ERC1967Proxy.sol 2022-08-11 21:13:55.000000000 -0700 ++++ proxy/ERC1967/ERC1967Proxy.sol 2022-08-11 21:15:52.000000000 -0700 @@ -20,6 +20,7 @@ * function call, and allows initializing the storage of the proxy like a Solidity constructor. */ @@ -396,8 +396,8 @@ diff -ruN proxy/ERC1967/ERC1967Proxy.sol proxy/ERC1967/ERC1967Proxy.sol } diff -ruN proxy/beacon/BeaconProxy.sol proxy/beacon/BeaconProxy.sol ---- proxy/beacon/BeaconProxy.sol 2022-06-14 06:30:55.000000000 -0700 -+++ proxy/beacon/BeaconProxy.sol 2022-06-06 12:25:10.000000000 -0700 +--- proxy/beacon/BeaconProxy.sol 2022-08-11 21:13:55.000000000 -0700 ++++ proxy/beacon/BeaconProxy.sol 2022-08-11 21:15:52.000000000 -0700 @@ -28,6 +28,7 @@ * - `beacon` must be a contract with the interface {IBeacon}. */ @@ -407,8 +407,8 @@ diff -ruN proxy/beacon/BeaconProxy.sol proxy/beacon/BeaconProxy.sol } diff -ruN proxy/transparent/TransparentUpgradeableProxy.sol proxy/transparent/TransparentUpgradeableProxy.sol ---- proxy/transparent/TransparentUpgradeableProxy.sol 2022-06-14 06:30:55.000000000 -0700 -+++ proxy/transparent/TransparentUpgradeableProxy.sol 2022-06-06 12:25:10.000000000 -0700 +--- proxy/transparent/TransparentUpgradeableProxy.sol 2022-08-11 21:13:55.000000000 -0700 ++++ proxy/transparent/TransparentUpgradeableProxy.sol 2022-08-11 21:15:52.000000000 -0700 @@ -36,6 +36,7 @@ address admin_, bytes memory _data @@ -418,8 +418,8 @@ diff -ruN proxy/transparent/TransparentUpgradeableProxy.sol proxy/transparent/Tr } diff -ruN proxy/utils/Initializable.sol proxy/utils/Initializable.sol ---- proxy/utils/Initializable.sol 2022-06-06 10:42:37.000000000 -0700 -+++ proxy/utils/Initializable.sol 2022-06-06 12:25:10.000000000 -0700 +--- proxy/utils/Initializable.sol 2022-08-11 21:13:55.000000000 -0700 ++++ proxy/utils/Initializable.sol 2022-08-11 21:15:52.000000000 -0700 @@ -59,12 +59,12 @@ * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool @@ -437,7 +437,7 @@ diff -ruN proxy/utils/Initializable.sol proxy/utils/Initializable.sol * @dev Triggered when the contract has been initialized or reinitialized. diff -ruN proxy/utils/Initializable.sol.orig proxy/utils/Initializable.sol.orig --- proxy/utils/Initializable.sol.orig 1969-12-31 16:00:00.000000000 -0800 -+++ proxy/utils/Initializable.sol.orig 2022-06-06 12:25:10.000000000 -0700 ++++ proxy/utils/Initializable.sol.orig 2022-08-11 21:15:52.000000000 -0700 @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (proxy/utils/Initializable.sol) @@ -579,7 +579,7 @@ diff -ruN proxy/utils/Initializable.sol.orig proxy/utils/Initializable.sol.orig +} diff -ruN proxy/utils/Initializable.sol.rej proxy/utils/Initializable.sol.rej --- proxy/utils/Initializable.sol.rej 1969-12-31 16:00:00.000000000 -0800 -+++ proxy/utils/Initializable.sol.rej 2022-06-06 12:25:10.000000000 -0700 ++++ proxy/utils/Initializable.sol.rej 2022-08-11 21:15:52.000000000 -0700 @@ -0,0 +1,17 @@ +*************** +*** 130,136 **** @@ -599,8 +599,8 @@ diff -ruN proxy/utils/Initializable.sol.rej proxy/utils/Initializable.sol.rej + // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level + // of initializers, because in other contexts the contract may have been reentered. diff -ruN security/Pausable.sol security/Pausable.sol ---- security/Pausable.sol 2022-06-14 06:30:55.000000000 -0700 -+++ security/Pausable.sol 2022-06-06 12:25:10.000000000 -0700 +--- security/Pausable.sol 2022-08-11 21:13:55.000000000 -0700 ++++ security/Pausable.sol 2022-08-11 21:15:52.000000000 -0700 @@ -35,6 +35,13 @@ } @@ -656,8 +656,8 @@ diff -ruN security/Pausable.sol security/Pausable.sol /** diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol ---- token/ERC1155/ERC1155.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC1155/ERC1155.sol 2022-06-15 16:37:15.000000000 -0700 +--- token/ERC1155/ERC1155.sol 2022-08-11 21:13:55.000000000 -0700 ++++ token/ERC1155/ERC1155.sol 2022-08-11 21:15:52.000000000 -0700 @@ -21,7 +21,7 @@ using Address for address; @@ -686,8 +686,8 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol ---- token/ERC20/ERC20.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC20/ERC20.sol 2022-06-06 12:25:10.000000000 -0700 +--- token/ERC20/ERC20.sol 2022-08-11 21:25:35.000000000 -0700 ++++ token/ERC20/ERC20.sol 2022-08-11 21:15:52.000000000 -0700 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -698,8 +698,8 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol _beforeTokenTransfer(account, address(0), amount); diff -ruN token/ERC20/README.adoc token/ERC20/README.adoc ---- token/ERC20/README.adoc 2022-06-15 14:13:43.000000000 -0700 -+++ token/ERC20/README.adoc 2022-06-06 12:25:10.000000000 -0700 +--- token/ERC20/README.adoc 2022-08-11 21:13:55.000000000 -0700 ++++ token/ERC20/README.adoc 2022-08-11 21:15:52.000000000 -0700 @@ -24,7 +24,7 @@ * {ERC20Votes}: support for voting and vote delegation. * {ERC20VotesComp}: support for voting and vote delegation (compatible with Compound's token, with uint96 restrictions). @@ -719,8 +719,8 @@ diff -ruN token/ERC20/README.adoc token/ERC20/README.adoc == Draft EIPs diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol ---- token/ERC20/extensions/ERC20FlashMint.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-06-06 12:25:10.000000000 -0700 +--- token/ERC20/extensions/ERC20FlashMint.sol 2022-08-11 21:13:55.000000000 -0700 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-08-11 21:15:52.000000000 -0700 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -736,7 +736,7 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 * implementation returns the address(0) which means the fee amount will be burnt. diff -ruN token/ERC20/extensions/ERC20TokenizedVault.sol token/ERC20/extensions/ERC20TokenizedVault.sol --- token/ERC20/extensions/ERC20TokenizedVault.sol 1969-12-31 16:00:00.000000000 -0800 -+++ token/ERC20/extensions/ERC20TokenizedVault.sol 2022-06-06 12:25:10.000000000 -0700 ++++ token/ERC20/extensions/ERC20TokenizedVault.sol 2022-08-11 21:15:52.000000000 -0700 @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT + @@ -956,8 +956,8 @@ diff -ruN token/ERC20/extensions/ERC20TokenizedVault.sol token/ERC20/extensions/ + } +} diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol ---- token/ERC20/extensions/ERC20Votes.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-06-06 12:25:10.000000000 -0700 +--- token/ERC20/extensions/ERC20Votes.sol 2022-08-11 21:16:57.000000000 -0700 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-08-11 21:25:13.000000000 -0700 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -969,18 +969,127 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote Checkpoint[] private _totalSupplyCheckpoints; /** -@@ -169,7 +169,7 @@ +@@ -152,7 +152,7 @@ + /** + * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). + */ +- function _maxSupply() internal view virtual returns (uint224) { ++ function _maxSupply() public view virtual returns (uint224) { //harnessed to public + return type(uint224).max; + } + +@@ -163,16 +163,16 @@ + super._mint(account, amount); + require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); + +- _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); ++ _writeCheckpointAdd(_totalSupplyCheckpoints, amount); // HARNESS: new version without pointer + } + /** * @dev Snapshots the totalSupply after it has been decreased. */ - function _burn(address account, uint256 amount) internal virtual override { -+ function _burn(address account, uint256 amount) public virtual override { ++ function _burn(address account, uint256 amount) public virtual override { // HARNESS: internal -> public (to comply with the ERC20 harness) super._burn(account, amount); - _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); +- _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); ++ _writeCheckpointSub(_totalSupplyCheckpoints, amount); // HARNESS: new version without pointer + } + + /** +@@ -187,7 +187,7 @@ + ) internal virtual override { + super._afterTokenTransfer(from, to, amount); + +- _moveVotingPower(delegates(from), delegates(to), amount); ++ _moveVotingPower(delegates(from), delegates(to), amount); + } + + /** +@@ -195,7 +195,7 @@ + * + * Emits events {DelegateChanged} and {DelegateVotesChanged}. + */ +- function _delegate(address delegator, address delegatee) internal virtual { ++ function _delegate(address delegator, address delegatee) public virtual { // HARNESSED TO MAKE PUBLIC + address currentDelegate = delegates(delegator); + uint256 delegatorBalance = balanceOf(delegator); + _delegates[delegator] = delegatee; +@@ -212,25 +212,25 @@ + ) private { + if (src != dst && amount > 0) { + if (src != address(0)) { +- (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); ++ (uint256 oldWeight, uint256 newWeight) = _writeCheckpointSub(_checkpoints[src], amount); // HARNESS: new version without pointer + emit DelegateVotesChanged(src, oldWeight, newWeight); + } + + if (dst != address(0)) { +- (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); ++ (uint256 oldWeight, uint256 newWeight) = _writeCheckpointAdd(_checkpoints[dst], amount); // HARNESS: new version without pointer + emit DelegateVotesChanged(dst, oldWeight, newWeight); + } + } + } + +- function _writeCheckpoint( ++ // HARNESS: split _writeCheckpoint() to two functions as a workaround for function pointers that cannot be managed by the tool ++ function _writeCheckpointAdd( + Checkpoint[] storage ckpts, +- function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; +- newWeight = op(oldWeight, delta); ++ newWeight = _add(oldWeight, delta); + + if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { + ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); +@@ -239,6 +239,39 @@ + } + } + ++ function _writeCheckpointSub( ++ Checkpoint[] storage ckpts, ++ uint256 delta ++ ) private returns (uint256 oldWeight, uint256 newWeight) { ++ uint256 pos = ckpts.length; ++ oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; ++ newWeight = _subtract(oldWeight, delta); ++ ++ if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { ++ ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); ++ } else { ++ ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); ++ } ++ } ++ ++ // backup of original function ++ // ++ // function _writeCheckpoint( ++ // Checkpoint[] storage ckpts, ++ // function(uint256, uint256) view returns (uint256) op, ++ // uint256 delta ++ // ) private returns (uint256 oldWeight, uint256 newWeight) { ++ // uint256 pos = ckpts.length; ++ // oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; ++ // newWeight = op(oldWeight, delta); ++ // ++ // if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { ++ // ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); ++ // } else { ++ // ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); ++ // } ++ // } ++ + function _add(uint256 a, uint256 b) private pure returns (uint256) { + return a + b; + } diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol ---- token/ERC20/extensions/ERC20Wrapper.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-06-06 12:25:10.000000000 -0700 +--- token/ERC20/extensions/ERC20Wrapper.sol 2022-08-11 21:13:55.000000000 -0700 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-08-11 21:15:52.000000000 -0700 @@ -55,7 +55,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. @@ -991,7 +1100,7 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr _mint(account, value); return value; diff -ruN token/ERC20/extensions/ERC4626.sol token/ERC20/extensions/ERC4626.sol ---- token/ERC20/extensions/ERC4626.sol 2022-06-15 14:13:43.000000000 -0700 +--- token/ERC20/extensions/ERC4626.sol 2022-08-11 21:13:55.000000000 -0700 +++ token/ERC20/extensions/ERC4626.sol 1969-12-31 16:00:00.000000000 -0800 @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: MIT @@ -1212,8 +1321,8 @@ diff -ruN token/ERC20/extensions/ERC4626.sol token/ERC20/extensions/ERC4626.sol - } -} diff -ruN token/ERC20/utils/SafeERC20.sol token/ERC20/utils/SafeERC20.sol ---- token/ERC20/utils/SafeERC20.sol 2022-06-14 06:30:55.000000000 -0700 -+++ token/ERC20/utils/SafeERC20.sol 2022-06-06 12:25:10.000000000 -0700 +--- token/ERC20/utils/SafeERC20.sol 2022-08-11 21:13:55.000000000 -0700 ++++ token/ERC20/utils/SafeERC20.sol 2022-08-11 21:15:52.000000000 -0700 @@ -4,7 +4,6 @@ pragma solidity ^0.8.0; @@ -1246,8 +1355,8 @@ diff -ruN token/ERC20/utils/SafeERC20.sol token/ERC20/utils/SafeERC20.sol * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol ---- token/ERC721/extensions/draft-ERC721Votes.sol 2022-06-06 10:42:37.000000000 -0700 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-06-06 12:25:10.000000000 -0700 +--- token/ERC721/extensions/draft-ERC721Votes.sol 2022-08-11 21:13:55.000000000 -0700 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-08-11 21:15:52.000000000 -0700 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. @@ -1258,8 +1367,8 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ } } diff -ruN utils/Address.sol utils/Address.sol ---- utils/Address.sol 2022-06-06 10:42:37.000000000 -0700 -+++ utils/Address.sol 2022-06-06 12:25:10.000000000 -0700 +--- utils/Address.sol 2022-08-11 21:13:55.000000000 -0700 ++++ utils/Address.sol 2022-08-11 21:15:52.000000000 -0700 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage @@ -1269,8 +1378,8 @@ diff -ruN utils/Address.sol utils/Address.sol require(isContract(target), "Address: call to non-contract"); diff -ruN utils/math/Math.sol utils/math/Math.sol ---- utils/math/Math.sol 2022-06-14 06:30:55.000000000 -0700 -+++ utils/math/Math.sol 2022-06-06 12:25:10.000000000 -0700 +--- utils/math/Math.sol 2022-08-11 21:13:55.000000000 -0700 ++++ utils/math/Math.sol 2022-08-11 21:15:52.000000000 -0700 @@ -149,78 +149,4 @@ } return result; From 2627753bfe773d9daba005da3962cc62502c1998 Mon Sep 17 00:00:00 2001 From: Nick Armstrong Date: Fri, 12 Aug 2022 00:32:35 -0700 Subject: [PATCH 254/254] votes solvency passing again --- certora/applyHarness.patch | 143 ++++++++++++--------- certora/scripts/Round2/verifyERC20Votes.sh | 5 +- certora/specs/ERC20Votes.spec | 25 ++-- 3 files changed, 101 insertions(+), 72 deletions(-) diff --git a/certora/applyHarness.patch b/certora/applyHarness.patch index 1d3ae55a5..e339df7c3 100644 --- a/certora/applyHarness.patch +++ b/certora/applyHarness.patch @@ -1,12 +1,12 @@ diff -ruN .gitignore .gitignore --- .gitignore 1969-12-31 16:00:00.000000000 -0800 -+++ .gitignore 2022-08-11 21:15:52.000000000 -0700 ++++ .gitignore 2022-08-11 21:28:36.000000000 -0700 @@ -0,0 +1,2 @@ +* +!.gitignore diff -ruN access/AccessControl.sol access/AccessControl.sol ---- access/AccessControl.sol 2022-08-11 21:13:55.000000000 -0700 -+++ access/AccessControl.sol 2022-08-11 21:15:52.000000000 -0700 +--- access/AccessControl.sol 2022-08-11 21:28:00.000000000 -0700 ++++ access/AccessControl.sol 2022-08-11 21:28:36.000000000 -0700 @@ -93,7 +93,7 @@ * * _Available since v4.6._ @@ -17,8 +17,8 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol } diff -ruN access/Ownable.sol access/Ownable.sol ---- access/Ownable.sol 2022-08-11 21:13:55.000000000 -0700 -+++ access/Ownable.sol 2022-08-11 21:15:52.000000000 -0700 +--- access/Ownable.sol 2022-08-11 21:28:00.000000000 -0700 ++++ access/Ownable.sol 2022-08-11 21:28:36.000000000 -0700 @@ -30,14 +30,6 @@ } @@ -49,8 +49,8 @@ diff -ruN access/Ownable.sol access/Ownable.sol /** diff -ruN governance/Governor.sol governance/Governor.sol ---- governance/Governor.sol 2022-08-11 21:13:55.000000000 -0700 -+++ governance/Governor.sol 2022-08-11 21:15:52.000000000 -0700 +--- governance/Governor.sol 2022-08-11 21:28:00.000000000 -0700 ++++ governance/Governor.sol 2022-08-11 21:28:36.000000000 -0700 @@ -44,7 +44,7 @@ string private _name; @@ -61,8 +61,8 @@ diff -ruN governance/Governor.sol governance/Governor.sol // This queue keeps track of the governor operating on itself. Calls to functions protected by the // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol ---- governance/TimelockController.sol 2022-08-11 21:13:55.000000000 -0700 -+++ governance/TimelockController.sol 2022-08-11 21:15:52.000000000 -0700 +--- governance/TimelockController.sol 2022-08-11 21:28:00.000000000 -0700 ++++ governance/TimelockController.sol 2022-08-11 21:28:36.000000000 -0700 @@ -28,10 +28,10 @@ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); @@ -77,8 +77,8 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol /** * @dev Emitted when a call is scheduled as part of operation `id`. diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol ---- governance/extensions/GovernorCountingSimple.sol 2022-08-11 21:13:55.000000000 -0700 -+++ governance/extensions/GovernorCountingSimple.sol 2022-08-11 21:15:52.000000000 -0700 +--- governance/extensions/GovernorCountingSimple.sol 2022-08-11 21:28:00.000000000 -0700 ++++ governance/extensions/GovernorCountingSimple.sol 2022-08-11 21:28:36.000000000 -0700 @@ -27,7 +27,7 @@ mapping(address => bool) hasVoted; } @@ -89,8 +89,8 @@ diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions /** * @dev See {IGovernor-COUNTING_MODE}. diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol ---- governance/extensions/GovernorPreventLateQuorum.sol 2022-08-11 21:13:55.000000000 -0700 -+++ governance/extensions/GovernorPreventLateQuorum.sol 2022-08-11 21:15:52.000000000 -0700 +--- governance/extensions/GovernorPreventLateQuorum.sol 2022-08-11 21:28:00.000000000 -0700 ++++ governance/extensions/GovernorPreventLateQuorum.sol 2022-08-11 21:28:36.000000000 -0700 @@ -21,8 +21,8 @@ using SafeCast for uint256; using Timers for Timers.BlockNumber; @@ -103,8 +103,8 @@ diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensi /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol ---- governance/utils/Votes.sol 2022-08-11 21:13:55.000000000 -0700 -+++ governance/utils/Votes.sol 2022-08-11 21:15:52.000000000 -0700 +--- governance/utils/Votes.sol 2022-08-11 21:28:00.000000000 -0700 ++++ governance/utils/Votes.sol 2022-08-11 21:28:36.000000000 -0700 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -178,8 +178,8 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public } diff -ruN metatx/MinimalForwarder.sol metatx/MinimalForwarder.sol ---- metatx/MinimalForwarder.sol 2022-08-11 21:13:55.000000000 -0700 -+++ metatx/MinimalForwarder.sol 2022-08-11 21:15:52.000000000 -0700 +--- metatx/MinimalForwarder.sol 2022-08-11 21:28:00.000000000 -0700 ++++ metatx/MinimalForwarder.sol 2022-08-11 21:28:36.000000000 -0700 @@ -8,11 +8,6 @@ /** @@ -194,7 +194,7 @@ diff -ruN metatx/MinimalForwarder.sol metatx/MinimalForwarder.sol using ECDSA for bytes32; diff -ruN mocks/ERC20TokenizedVaultMock.sol mocks/ERC20TokenizedVaultMock.sol --- mocks/ERC20TokenizedVaultMock.sol 1969-12-31 16:00:00.000000000 -0800 -+++ mocks/ERC20TokenizedVaultMock.sol 2022-08-11 21:15:52.000000000 -0700 ++++ mocks/ERC20TokenizedVaultMock.sol 2022-08-11 21:28:36.000000000 -0700 @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + @@ -219,7 +219,7 @@ diff -ruN mocks/ERC20TokenizedVaultMock.sol mocks/ERC20TokenizedVaultMock.sol + } +} diff -ruN mocks/ERC4626Mock.sol mocks/ERC4626Mock.sol ---- mocks/ERC4626Mock.sol 2022-08-11 21:13:55.000000000 -0700 +--- mocks/ERC4626Mock.sol 2022-08-11 21:28:00.000000000 -0700 +++ mocks/ERC4626Mock.sol 1969-12-31 16:00:00.000000000 -0800 @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT @@ -245,8 +245,8 @@ diff -ruN mocks/ERC4626Mock.sol mocks/ERC4626Mock.sol - } -} diff -ruN mocks/MathMock.sol mocks/MathMock.sol ---- mocks/MathMock.sol 2022-08-11 21:13:55.000000000 -0700 -+++ mocks/MathMock.sol 2022-08-11 21:15:52.000000000 -0700 +--- mocks/MathMock.sol 2022-08-11 21:28:00.000000000 -0700 ++++ mocks/MathMock.sol 2022-08-11 21:28:36.000000000 -0700 @@ -29,8 +29,4 @@ ) public pure returns (uint256) { return Math.mulDiv(a, b, denominator, direction); @@ -257,8 +257,8 @@ diff -ruN mocks/MathMock.sol mocks/MathMock.sol - } } diff -ruN mocks/SafeERC20Helper.sol mocks/SafeERC20Helper.sol ---- mocks/SafeERC20Helper.sol 2022-08-11 21:13:55.000000000 -0700 -+++ mocks/SafeERC20Helper.sol 2022-08-11 21:15:52.000000000 -0700 +--- mocks/SafeERC20Helper.sol 2022-08-11 21:28:00.000000000 -0700 ++++ mocks/SafeERC20Helper.sol 2022-08-11 21:28:36.000000000 -0700 @@ -4,7 +4,6 @@ import "../utils/Context.sol"; @@ -331,8 +331,8 @@ diff -ruN mocks/SafeERC20Helper.sol mocks/SafeERC20Helper.sol ERC20ReturnTrueMock(address(_token)).setAllowance(allowance_); } diff -ruN proxy/Clones.sol proxy/Clones.sol ---- proxy/Clones.sol 2022-08-11 21:13:55.000000000 -0700 -+++ proxy/Clones.sol 2022-08-11 21:15:52.000000000 -0700 +--- proxy/Clones.sol 2022-08-11 21:28:00.000000000 -0700 ++++ proxy/Clones.sol 2022-08-11 21:28:36.000000000 -0700 @@ -26,10 +26,10 @@ /// @solidity memory-safe-assembly assembly { @@ -385,8 +385,8 @@ diff -ruN proxy/Clones.sol proxy/Clones.sol } diff -ruN proxy/ERC1967/ERC1967Proxy.sol proxy/ERC1967/ERC1967Proxy.sol ---- proxy/ERC1967/ERC1967Proxy.sol 2022-08-11 21:13:55.000000000 -0700 -+++ proxy/ERC1967/ERC1967Proxy.sol 2022-08-11 21:15:52.000000000 -0700 +--- proxy/ERC1967/ERC1967Proxy.sol 2022-08-11 21:28:00.000000000 -0700 ++++ proxy/ERC1967/ERC1967Proxy.sol 2022-08-11 21:28:36.000000000 -0700 @@ -20,6 +20,7 @@ * function call, and allows initializing the storage of the proxy like a Solidity constructor. */ @@ -396,8 +396,8 @@ diff -ruN proxy/ERC1967/ERC1967Proxy.sol proxy/ERC1967/ERC1967Proxy.sol } diff -ruN proxy/beacon/BeaconProxy.sol proxy/beacon/BeaconProxy.sol ---- proxy/beacon/BeaconProxy.sol 2022-08-11 21:13:55.000000000 -0700 -+++ proxy/beacon/BeaconProxy.sol 2022-08-11 21:15:52.000000000 -0700 +--- proxy/beacon/BeaconProxy.sol 2022-08-11 21:28:00.000000000 -0700 ++++ proxy/beacon/BeaconProxy.sol 2022-08-11 21:28:36.000000000 -0700 @@ -28,6 +28,7 @@ * - `beacon` must be a contract with the interface {IBeacon}. */ @@ -407,8 +407,8 @@ diff -ruN proxy/beacon/BeaconProxy.sol proxy/beacon/BeaconProxy.sol } diff -ruN proxy/transparent/TransparentUpgradeableProxy.sol proxy/transparent/TransparentUpgradeableProxy.sol ---- proxy/transparent/TransparentUpgradeableProxy.sol 2022-08-11 21:13:55.000000000 -0700 -+++ proxy/transparent/TransparentUpgradeableProxy.sol 2022-08-11 21:15:52.000000000 -0700 +--- proxy/transparent/TransparentUpgradeableProxy.sol 2022-08-11 21:28:00.000000000 -0700 ++++ proxy/transparent/TransparentUpgradeableProxy.sol 2022-08-11 21:28:36.000000000 -0700 @@ -36,6 +36,7 @@ address admin_, bytes memory _data @@ -418,8 +418,8 @@ diff -ruN proxy/transparent/TransparentUpgradeableProxy.sol proxy/transparent/Tr } diff -ruN proxy/utils/Initializable.sol proxy/utils/Initializable.sol ---- proxy/utils/Initializable.sol 2022-08-11 21:13:55.000000000 -0700 -+++ proxy/utils/Initializable.sol 2022-08-11 21:15:52.000000000 -0700 +--- proxy/utils/Initializable.sol 2022-08-11 21:28:00.000000000 -0700 ++++ proxy/utils/Initializable.sol 2022-08-11 21:28:36.000000000 -0700 @@ -59,12 +59,12 @@ * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool @@ -437,7 +437,7 @@ diff -ruN proxy/utils/Initializable.sol proxy/utils/Initializable.sol * @dev Triggered when the contract has been initialized or reinitialized. diff -ruN proxy/utils/Initializable.sol.orig proxy/utils/Initializable.sol.orig --- proxy/utils/Initializable.sol.orig 1969-12-31 16:00:00.000000000 -0800 -+++ proxy/utils/Initializable.sol.orig 2022-08-11 21:15:52.000000000 -0700 ++++ proxy/utils/Initializable.sol.orig 2022-08-11 21:28:36.000000000 -0700 @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (proxy/utils/Initializable.sol) @@ -579,7 +579,7 @@ diff -ruN proxy/utils/Initializable.sol.orig proxy/utils/Initializable.sol.orig +} diff -ruN proxy/utils/Initializable.sol.rej proxy/utils/Initializable.sol.rej --- proxy/utils/Initializable.sol.rej 1969-12-31 16:00:00.000000000 -0800 -+++ proxy/utils/Initializable.sol.rej 2022-08-11 21:15:52.000000000 -0700 ++++ proxy/utils/Initializable.sol.rej 2022-08-11 21:28:36.000000000 -0700 @@ -0,0 +1,17 @@ +*************** +*** 130,136 **** @@ -599,8 +599,8 @@ diff -ruN proxy/utils/Initializable.sol.rej proxy/utils/Initializable.sol.rej + // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level + // of initializers, because in other contexts the contract may have been reentered. diff -ruN security/Pausable.sol security/Pausable.sol ---- security/Pausable.sol 2022-08-11 21:13:55.000000000 -0700 -+++ security/Pausable.sol 2022-08-11 21:15:52.000000000 -0700 +--- security/Pausable.sol 2022-08-11 21:28:00.000000000 -0700 ++++ security/Pausable.sol 2022-08-11 21:28:36.000000000 -0700 @@ -35,6 +35,13 @@ } @@ -656,8 +656,8 @@ diff -ruN security/Pausable.sol security/Pausable.sol /** diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol ---- token/ERC1155/ERC1155.sol 2022-08-11 21:13:55.000000000 -0700 -+++ token/ERC1155/ERC1155.sol 2022-08-11 21:15:52.000000000 -0700 +--- token/ERC1155/ERC1155.sol 2022-08-11 21:28:00.000000000 -0700 ++++ token/ERC1155/ERC1155.sol 2022-08-11 21:28:36.000000000 -0700 @@ -21,7 +21,7 @@ using Address for address; @@ -686,8 +686,8 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol ---- token/ERC20/ERC20.sol 2022-08-11 21:25:35.000000000 -0700 -+++ token/ERC20/ERC20.sol 2022-08-11 21:15:52.000000000 -0700 +--- token/ERC20/ERC20.sol 2022-08-11 21:28:00.000000000 -0700 ++++ token/ERC20/ERC20.sol 2022-08-11 23:01:50.000000000 -0700 @@ -277,7 +277,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. @@ -698,8 +698,8 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol _beforeTokenTransfer(account, address(0), amount); diff -ruN token/ERC20/README.adoc token/ERC20/README.adoc ---- token/ERC20/README.adoc 2022-08-11 21:13:55.000000000 -0700 -+++ token/ERC20/README.adoc 2022-08-11 21:15:52.000000000 -0700 +--- token/ERC20/README.adoc 2022-08-11 21:28:00.000000000 -0700 ++++ token/ERC20/README.adoc 2022-08-11 21:28:36.000000000 -0700 @@ -24,7 +24,7 @@ * {ERC20Votes}: support for voting and vote delegation. * {ERC20VotesComp}: support for voting and vote delegation (compatible with Compound's token, with uint96 restrictions). @@ -719,8 +719,8 @@ diff -ruN token/ERC20/README.adoc token/ERC20/README.adoc == Draft EIPs diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol ---- token/ERC20/extensions/ERC20FlashMint.sol 2022-08-11 21:13:55.000000000 -0700 -+++ token/ERC20/extensions/ERC20FlashMint.sol 2022-08-11 21:15:52.000000000 -0700 +--- token/ERC20/extensions/ERC20FlashMint.sol 2022-08-11 21:28:00.000000000 -0700 ++++ token/ERC20/extensions/ERC20FlashMint.sol 2022-08-11 21:28:36.000000000 -0700 @@ -40,9 +40,11 @@ require(token == address(this), "ERC20FlashMint: wrong token"); // silence warning about unused variable without the addition of bytecode. @@ -736,7 +736,7 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20 * implementation returns the address(0) which means the fee amount will be burnt. diff -ruN token/ERC20/extensions/ERC20TokenizedVault.sol token/ERC20/extensions/ERC20TokenizedVault.sol --- token/ERC20/extensions/ERC20TokenizedVault.sol 1969-12-31 16:00:00.000000000 -0800 -+++ token/ERC20/extensions/ERC20TokenizedVault.sol 2022-08-11 21:15:52.000000000 -0700 ++++ token/ERC20/extensions/ERC20TokenizedVault.sol 2022-08-11 21:28:36.000000000 -0700 @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT + @@ -957,7 +957,7 @@ diff -ruN token/ERC20/extensions/ERC20TokenizedVault.sol token/ERC20/extensions/ +} diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol --- token/ERC20/extensions/ERC20Votes.sol 2022-08-11 21:16:57.000000000 -0700 -+++ token/ERC20/extensions/ERC20Votes.sol 2022-08-11 21:25:13.000000000 -0700 ++++ token/ERC20/extensions/ERC20Votes.sol 2022-08-11 22:47:30.000000000 -0700 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -1088,9 +1088,34 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote return a + b; } diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol ---- token/ERC20/extensions/ERC20Wrapper.sol 2022-08-11 21:13:55.000000000 -0700 -+++ token/ERC20/extensions/ERC20Wrapper.sol 2022-08-11 21:15:52.000000000 -0700 -@@ -55,7 +55,7 @@ +--- token/ERC20/extensions/ERC20Wrapper.sol 2022-08-11 21:28:00.000000000 -0700 ++++ token/ERC20/extensions/ERC20Wrapper.sol 2022-08-11 21:29:19.000000000 -0700 +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: MIT +-// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/extensions/ERC20Wrapper.sol) ++// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Wrapper.sol) + + pragma solidity ^0.8.0; + +@@ -23,17 +23,6 @@ + } + + /** +- * @dev See {ERC20-decimals}. +- */ +- function decimals() public view virtual override returns (uint8) { +- try IERC20Metadata(address(underlying)).decimals() returns (uint8 value) { +- return value; +- } catch { +- return super.decimals(); +- } +- } +- +- /** + * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens. + */ + function depositFor(address account, uint256 amount) public virtual returns (bool) { +@@ -55,7 +44,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. */ @@ -1100,7 +1125,7 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr _mint(account, value); return value; diff -ruN token/ERC20/extensions/ERC4626.sol token/ERC20/extensions/ERC4626.sol ---- token/ERC20/extensions/ERC4626.sol 2022-08-11 21:13:55.000000000 -0700 +--- token/ERC20/extensions/ERC4626.sol 2022-08-11 21:28:00.000000000 -0700 +++ token/ERC20/extensions/ERC4626.sol 1969-12-31 16:00:00.000000000 -0800 @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: MIT @@ -1321,8 +1346,8 @@ diff -ruN token/ERC20/extensions/ERC4626.sol token/ERC20/extensions/ERC4626.sol - } -} diff -ruN token/ERC20/utils/SafeERC20.sol token/ERC20/utils/SafeERC20.sol ---- token/ERC20/utils/SafeERC20.sol 2022-08-11 21:13:55.000000000 -0700 -+++ token/ERC20/utils/SafeERC20.sol 2022-08-11 21:15:52.000000000 -0700 +--- token/ERC20/utils/SafeERC20.sol 2022-08-11 21:28:00.000000000 -0700 ++++ token/ERC20/utils/SafeERC20.sol 2022-08-11 21:28:36.000000000 -0700 @@ -4,7 +4,6 @@ pragma solidity ^0.8.0; @@ -1355,8 +1380,8 @@ diff -ruN token/ERC20/utils/SafeERC20.sol token/ERC20/utils/SafeERC20.sol * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol ---- token/ERC721/extensions/draft-ERC721Votes.sol 2022-08-11 21:13:55.000000000 -0700 -+++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-08-11 21:15:52.000000000 -0700 +--- token/ERC721/extensions/draft-ERC721Votes.sol 2022-08-11 21:28:00.000000000 -0700 ++++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-08-11 21:28:36.000000000 -0700 @@ -34,7 +34,7 @@ /** * @dev Returns the balance of `account`. @@ -1367,8 +1392,8 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/ } } diff -ruN utils/Address.sol utils/Address.sol ---- utils/Address.sol 2022-08-11 21:13:55.000000000 -0700 -+++ utils/Address.sol 2022-08-11 21:15:52.000000000 -0700 +--- utils/Address.sol 2022-08-11 21:28:00.000000000 -0700 ++++ utils/Address.sol 2022-08-11 21:28:36.000000000 -0700 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage @@ -1378,8 +1403,8 @@ diff -ruN utils/Address.sol utils/Address.sol require(isContract(target), "Address: call to non-contract"); diff -ruN utils/math/Math.sol utils/math/Math.sol ---- utils/math/Math.sol 2022-08-11 21:13:55.000000000 -0700 -+++ utils/math/Math.sol 2022-08-11 21:15:52.000000000 -0700 +--- utils/math/Math.sol 2022-08-11 21:28:00.000000000 -0700 ++++ utils/math/Math.sol 2022-08-11 21:28:36.000000000 -0700 @@ -149,78 +149,4 @@ } return result; diff --git a/certora/scripts/Round2/verifyERC20Votes.sh b/certora/scripts/Round2/verifyERC20Votes.sh index dea9bc695..c9e5db059 100644 --- a/certora/scripts/Round2/verifyERC20Votes.sh +++ b/certora/scripts/Round2/verifyERC20Votes.sh @@ -2,12 +2,11 @@ certoraRun \ certora/harnesses/ERC20VotesHarness.sol \ --verify ERC20VotesHarness:certora/specs/ERC20Votes.spec \ --solc solc8.2 \ - --disableLocalTypeChecking \ --optimistic_loop \ --settings -copyLoopUnroll=4 \ --cloud \ --send_only \ - --msg "ERC20Votes" \ - + --msg "ERC20Votes $1" \ + # --disableLocalTypeChecking \ # --staging "alex/new-dt-hashing-alpha" \ diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index badf3f557..f0b9cde0d 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -29,19 +29,18 @@ ghost userVotes(address) returns uint224 { } // sums the total votes for all users -ghost totalVotes() returns mathint { +ghost totalVotes() returns uint224 { init_state axiom totalVotes() == 0; - axiom totalVotes() >= 0; } ghost lastIndex(address) returns uint32; // helper -hook Sstore _checkpoints[KEY address account][INDEX uint32 index].votes uint224 newVotes (uint224 oldVotes) STORAGE { +hook Sstore _checkpoints[KEY address account][INDEX uint32 index].votes uint224 newVotes (uint224 oldVotes) STORAGE { havoc userVotes assuming userVotes@new(account) == newVotes; havoc totalVotes assuming - totalVotes@new() == totalVotes@old() + to_mathint(newVotes) - to_mathint(userVotes(account)); + totalVotes@new() == totalVotes@old() + newVotes - userVotes(account); havoc lastIndex assuming lastIndex@new(account) == index; @@ -68,10 +67,16 @@ hook Sstore _checkpoints[KEY address account][INDEX uint32 index].fromBlock uint // sum of user balances is >= total amount of delegated votes // fails on burn. This is because burn does not remove votes from the users invariant votes_solvency() - to_mathint(totalSupply()) >= totalVotes() + totalSupply() >= to_uint256(totalVotes()) +filtered { f -> f.selector != _burn(address, uint256).selector} { preserved with(env e) { require forall address account. numCheckpoints(account) < 1000000; -} } +} preserved burn(address a, uint256 amount) with(env e){ + require _delegates(0) == 0; + require forall address a2. (_delegates(a) != _delegates(a2)) && (balanceOf(e, _delegates(a)) + balanceOf(e, _delegates(a2)) <= totalVotes()); + require balanceOf(e, _delegates(a)) < totalVotes(); + require amount < 100000; +}} // for some checkpoint, the fromBlock is less than the current block number @@ -146,10 +151,10 @@ rule transfer_safe() { require numCheckpoints(delegates(b)) < 1000000; uint256 votesA_pre = getVotes(delegates(a)); uint256 votesB_pre = getVotes(delegates(b)); - mathint totalVotes_pre = totalVotes(); + uint224 totalVotes_pre = totalVotes(); transferFrom(e, a, b, amount); - mathint totalVotes_post = totalVotes(); + uint224 totalVotes_post = totalVotes(); uint256 votesA_post = getVotes(delegates(a)); uint256 votesB_post = getVotes(delegates(b)); // if an account that has not delegated transfers balance to an account that has, it will increase the total supply of votes @@ -308,7 +313,7 @@ rule mint_doesnt_increase_totalVotes() { env e; uint256 amount; address account; - mathint totalVotes_ = totalVotes(); + uint224 totalVotes_ = totalVotes(); mint(e, account, amount); @@ -319,7 +324,7 @@ rule burn_doesnt_decrease_totalVotes() { env e; uint256 amount; address account; - mathint totalVotes_ = totalVotes(); + uint224 totalVotes_ = totalVotes(); burn(e, account, amount);