Hats-Protocol / hats-zodiac

Hats Protocol-enabled Zodiac contracts
MIT License
21 stars 9 forks source link

Threshold module #69

Closed gershido closed 1 month ago

gershido commented 1 month ago

This PR adds another threshold calculation logic - PROPORTIONAL threshold as a percentage of the number of owners in the Safe, in addition to the current ABSOLUTE threshold. Both of them define the required amount of valid signatures by owners of the safe in order to execute transactions.

Following this PR, the required amount of valid signatures is defined with the ThresholdConfig:

/// @notice Struct for the threshold configuration
  /// @param thresholdType The type of target threshold, either ABSOLUTE or PROPORTIONAL
  /// @param min The minimum threshold
  /// @param target The target threshold
  struct ThresholdConfig {
    TargetThresholdType thresholdType;
    uint120 min;
    uint120 target;
  }

It is set at deployment time and can be changed later by wearers of the HSG's owner hat.

Here's how the config is then used to calculate the required amount of valid signatures:

/// @dev Internal function to calculate the required amount of valid signatures according to the current number of
  /// owners in the safe and the threshold config
  /// @param numOwners The number of owners in the safe
  /// @return _requiredValidSignatures The required amount of valid signatures
  function _getRequiredValidSignatures(uint256 numOwners) internal view returns (uint256 _requiredValidSignatures) {
    // get the threshold config
    ThresholdConfig memory config = _thresholdConfig;

    // calculate the correct threshold
    if (config.thresholdType == TargetThresholdType.ABSOLUTE) {
      // ABSOLUTE
      if (numOwners < config.min) _requiredValidSignatures = config.min;
      else if (numOwners > config.target) _requiredValidSignatures = config.target;
      else _requiredValidSignatures = numOwners;
    } else {
      // PROPORTIONAL
      // add 9999 to round up
      _requiredValidSignatures = ((numOwners * config.target) + 9999) / 10_000;
      // ensure that the threshold is not lower than the min threshold
      if (_requiredValidSignatures < config.min) _requiredValidSignatures = config.min;
    }
  }

In both cases, the threshold is calculated based on the current amount of owners in the Safe, whether they are valid owners or not. This allows the HSG to maintain the invariant: threshold = Min(requiredValidSignatures, numOwners) Where threshold is the current Safe's threshold, requiredValidSignatures is calculated by the absolute/proportional logic and numOwners is the Safe's current amount of owners.