Commit 4424552c authored by Maurelian's avatar Maurelian Committed by GitHub

ctb: Add DeputyGuardianModule (#9982)

* ctb: Allow the Liveness Module's threshold to be customized

* ctb: Hardcode threshold percentage as immutable in livenessmodule

* ctb: test threshold math differentially

* ctb: Threshold tests with hardcoded values and boundary fuzz tests
parent 02c1193e
......@@ -92,8 +92,8 @@
"sourceCodeHash": "0x9633cea9b66077e222f470439fe3e9a31f3e33b4f7a5618374c44310fd234b24"
},
"src/Safe/LivenessModule.sol": {
"initCodeHash": "0xcd8b76f70634330e242d4ff497bba1f8e0aaa11d7aecf9845090029c43027a7f",
"sourceCodeHash": "0x3b358b51fce4f1516191104d2e1f782c8ec3edecee63c502cc301671aa632b93"
"initCodeHash": "0xa8b233f0f26f8a73b997b12ba06d64cefa8ee98d523f68cd63320e9787468fae",
"sourceCodeHash": "0x73aa5934e56ba2a45f368806c5db1d442bf5713d51b2184749f4638eaceb832e"
},
"src/cannon/MIPS.sol": {
"initCodeHash": "0xaf2ac814f64ccf12e9c6738db7cef865f51f9e39f39105adef9fba11465f6ee1",
......
......@@ -21,6 +21,11 @@
"name": "_minOwners",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_thresholdPercentage",
"type": "uint256"
},
{
"internalType": "address",
"name": "_fallbackOwner",
......@@ -70,7 +75,7 @@
"type": "uint256"
}
],
"name": "get75PercentThreshold",
"name": "getRequiredThreshold",
"outputs": [
{
"internalType": "uint256",
......@@ -78,7 +83,7 @@
"type": "uint256"
}
],
"stateMutability": "pure",
"stateMutability": "view",
"type": "function"
},
{
......@@ -151,6 +156,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "thresholdPercentage",
"outputs": [
{
"internalType": "uint256",
"name": "thresholdPercentage_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "version",
......
......@@ -35,6 +35,9 @@ contract LivenessModule is ISemver {
/// This can be updated by replacing with a new module.
uint256 internal immutable MIN_OWNERS;
/// @notice The percentage used to calculate the threshold for the Safe.
uint256 internal immutable THRESHOLD_PERCENTAGE;
/// @notice The fallback owner of the Safe
/// This can be updated by replacing with a new module.
address internal immutable FALLBACK_OWNER;
......@@ -44,8 +47,8 @@ contract LivenessModule is ISemver {
uint256 internal constant GUARD_STORAGE_SLOT = 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8;
/// @notice Semantic version.
/// @custom:semver 1.0.0
string public constant version = "1.0.0";
/// @custom:semver 1.1.0
string public constant version = "1.1.0";
// Constructor to initialize the Safe and baseModule instances
constructor(
......@@ -53,25 +56,29 @@ contract LivenessModule is ISemver {
LivenessGuard _livenessGuard,
uint256 _livenessInterval,
uint256 _minOwners,
uint256 _thresholdPercentage,
address _fallbackOwner
) {
SAFE = _safe;
LIVENESS_GUARD = _livenessGuard;
LIVENESS_INTERVAL = _livenessInterval;
THRESHOLD_PERCENTAGE = _thresholdPercentage;
FALLBACK_OWNER = _fallbackOwner;
MIN_OWNERS = _minOwners;
address[] memory owners = _safe.getOwners();
require(_minOwners <= owners.length, "LivenessModule: minOwners must be less than the number of owners");
require(
_safe.getThreshold() >= get75PercentThreshold(owners.length),
"LivenessModule: Safe must have a threshold of at least 75% of the number of owners"
_safe.getThreshold() >= getRequiredThreshold(owners.length),
"LivenessModule: Insufficient threshold for the number of owners"
);
require(_thresholdPercentage > 0, "LivenessModule: thresholdPercentage must be greater than 0");
require(_thresholdPercentage <= 100, "LivenessModule: thresholdPercentage must be less than or equal to 100");
}
/// @notice For a given number of owners, return the lowest threshold which is greater than 75.
/// @notice For a given number of owners, return the lowest threshold which is greater than the required percentage.
/// Note: this function returns 1 for numOwners == 1.
function get75PercentThreshold(uint256 _numOwners) public pure returns (uint256 threshold_) {
threshold_ = (_numOwners * 75 + 99) / 100;
function getRequiredThreshold(uint256 _numOwners) public view returns (uint256 threshold_) {
threshold_ = (_numOwners * THRESHOLD_PERCENTAGE + 99) / 100;
}
/// @notice Getter function for the Safe contract instance
......@@ -98,7 +105,13 @@ contract LivenessModule is ISemver {
minOwners_ = MIN_OWNERS;
}
/// @notice Getter function for the fallback owner
/// @notice Getter function for the required threshold percentage
/// @return thresholdPercentage_ The minimum number of owners
function thresholdPercentage() public view returns (uint256 thresholdPercentage_) {
thresholdPercentage_ = THRESHOLD_PERCENTAGE;
}
/// @notice Getter function for the fallback
/// @return fallbackOwner_ The fallback owner of the Safe
function fallbackOwner() public view returns (address fallbackOwner_) {
fallbackOwner_ = FALLBACK_OWNER;
......@@ -161,7 +174,7 @@ contract LivenessModule is ISemver {
/// @param _newOwnersCount New number of owners after removal.
function _removeOwner(address _prevOwner, address _ownerToRemove, uint256 _newOwnersCount) internal {
if (_newOwnersCount > 0) {
uint256 newThreshold = get75PercentThreshold(_newOwnersCount);
uint256 newThreshold = getRequiredThreshold(_newOwnersCount);
// Remove the owner and update the threshold
_removeOwnerSafeCall({ _prevOwner: _prevOwner, _owner: _ownerToRemove, _threshold: newThreshold });
} else {
......@@ -223,11 +236,11 @@ contract LivenessModule is ISemver {
// Check that"LivenessModule: must remove all owners and transfer to fallback owner if numOwners < minOwners"
// the threshold is correct. This check is also correct when there is a single
// owner, because get75PercentThreshold(1) returns 1.
// owner, because getRequiredThreshold(1) returns 1.
uint256 threshold = SAFE.getThreshold();
require(
threshold == get75PercentThreshold(numOwners),
"LivenessModule: Safe must have a threshold of 75% of the number of owners"
threshold == getRequiredThreshold(numOwners),
"LivenessModule: Insufficient threshold for the number of owners"
);
// Check that the guard has not been changed
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment