Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Add minimal approval configuration #28

Open
wants to merge 46 commits into
base: develop
Choose a base branch
from

Conversation

clauBv23
Copy link

@clauBv23 clauBv23 commented Aug 26, 2024

Task ID: OD-1451

TBD:

  • The new updateMinApproval permission id should be UPDATE_VOTING_SETTINGS_PERMISSION_ID or a new one should be created?
  • Old initialize function on Token voting should revert or continue allowing initialize the plugin without minApproval value?

packages/contracts/src/MajorityVotingBase.sol Outdated Show resolved Hide resolved
packages/contracts/src/MajorityVotingBase.sol Outdated Show resolved Hide resolved
packages/contracts/src/MajorityVotingBase.sol Outdated Show resolved Hide resolved
@@ -266,17 +273,25 @@ abstract contract MajorityVotingBase is
uint256 minProposerVotingPower
);

/// @notice Emitted when the min approval value is updated.
/// @param minApproval The minimum amount of yes votes needed for a proposal succeed.
event VotingMinApprovalUpdated(uint32 minApproval);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to have this event defined in the interface?

Copy link
Author

@clauBv23 clauBv23 Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely certain about the approach we're taking with this, but I noticed that VotingMinApprovalUpdated](

/// @notice Emitted when the voting settings are updated.
/// @param votingMode A parameter to select the vote mode.
/// @param supportThreshold The support threshold value.
/// @param minParticipation The minimum participation value.
/// @param minDuration The minimum duration of the proposal vote in seconds.
/// @param minProposerVotingPower The minimum voting power required to create a proposal.
event VotingSettingsUpdated(
VotingMode votingMode,
uint32 supportThreshold,
uint32 minParticipation,
uint64 minDuration,
uint256 minProposerVotingPower
);
) was defined in the contract rather than the interface.

Given that events are not part of the interface ID, I believe it’s reasonable to define them in the interface. However, for the sake of consistency, I lean to keep it here unless you have a strong reason to move it to the interface.

// todo TBD define if permission should be the same one as update settings
/// @notice Updates the minimal approval value.
/// @param _minApproval The new minimal approval value.
function updateMinApproval(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we always doing breaks for function arguments or not? I see a small inconsistency here :-)

Copy link
Author

@clauBv23 clauBv23 Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will depend on the line length (100 - 120).
If there are too many arguments will break into lines, if not it should be on the same one.
In this case, there is a single argument but the modifier name on the auth function increases the line length and breaks in lines

revert RatioOutOfBounds({limit: RATIO_BASE, actual: _minApproval});
}

minApprovals = _minApproval;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small incosistency. name them the same.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done f7e0f97

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you still use _minApproval instead of _minApprovals. hahaha

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and rename the function as well to _updateMinApprovals.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hopefully all are renamed now 92070c4 9b6cf85

@@ -224,6 +227,10 @@ abstract contract MajorityVotingBase is
/// @notice The struct storing the voting settings.
VotingSettings private votingSettings;

/// @notice The minimal ratio of yes votes needed for a proposal succeed.
/// @dev is not on the VotingSettings for compatibility reasons.
uint32 private minApprovals; // added in v1.3

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for going with uint32 instead of uint256 ? I wonder the reason in your mind was that in the future, if we add more state variable, they would be tightly packed ? If that's the reason, I think it's a wrong reason.

packing only makes good and efficient sense if state variables are stored in the same tx. I highly doubt that whatever new state variable we add, that value and minApprovals value would be updated in the same tx. From the solidity docs:

If you are not reading or writing all the values in a slot at the same time, this can have the opposite effect, though: When one value is written to a multi-value storage slot, the storage slot has to be read first and then combined with the new value such that other data in the same slot is not destroyed.

Up to you as I haven't thought this very deep, but from first impression, that's the impression I got.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, can you remind me of why we can not put this state variable inside VotingSettings struct ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with uint32 mainly because minParticipation and supportThreshold the other ratios we already store are defined as uint32.
At some point questioned it because an extra operation is needed for casting the value every single time, however just continued with uint32 for consistency among the ratios.

 

Didn't store it inside VotingSettings because it is being received as a parameter for several functions:

  • __MajorityVotingBase_init: is not important because is not on any interface id.
  • initialize: that is in TOKEN_VOTING_INTERFACE_ID but we are already overriding it so we can manage it.
  • updateVotingSettings: that is in IMajorityVoting interface, adding a new entry to the struct will change the function selector, and even if we implement erc165 with both IDs, we still need to "support" the old function which has a no longer existent struct.

A new struct could be created to add this parameter and deprecate the old one, but to be honest, I don't have a strong opinion on this, neither option is clean.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with uint32 mainly because minParticipation and supportThreshold the other ratios we already store are defined as uint32.

This was because of packing, but for new minApproval, no packing needed. I'd go with uint256.

As for other question, I missed this detail and yes, I think we don't have any better way. Have to think this through

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also agree to go with uint256 done in 4ce4388

Copy link

@novaknole novaknole left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1st ROUND.

@@ -211,6 +213,7 @@ abstract contract MajorityVotingBase is
this.totalVotingPower.selector ^
this.getProposal.selector ^
this.updateVotingSettings.selector ^
this.updateMinApprovals.selector ^
this.createProposal.selector;

/// @notice The ID of the permission required to call the `updateVotingSettings` function.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// @notice The ID of the permission required to call the `updateVotingSettings` function.
/// @notice The ID of the permission required to call the `updateVotingSettings` and `updateMinApprovals` functions.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clauBv23 - sorry for the long delay in getting to this review. Functionally this looks like it works as specified in the requirements.

The only thing that really makes me uncomfortable is not including minApprovals in the voting setting struct. It is a governance setting just like the others, so having it separate, and with its own permission doesn't make any practical sense.

Will this always be a problem when we want to extend the functionality of existing plugins? Are there any other product inputs that can help inform how to improve this decision?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants