Skip to content

Commit

Permalink
fix(OpenQuestion): veto and lift veto
Browse files Browse the repository at this point in the history
  • Loading branch information
NicoAcosta committed Nov 20, 2024
1 parent 8074a8f commit 4a46a6f
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 13 deletions.
56 changes: 44 additions & 12 deletions src/questions/OpenQuestion.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ contract OpenQuestion is Question, IOpenQuestion {
// Mapping to store user votes for each option
mapping(address user => mapping(uint256 optionId => bool hasVoted)) private userVotes;

/// @notice Mapping to track if an option is vetoed
mapping(uint256 => bool) public vetoed;

/// @notice Initializes the OpenQuestion contract
/// @dev Sets up the question details and minimum points required to add an option
/// @param _space The address of the Space contract
Expand All @@ -26,33 +29,62 @@ contract OpenQuestion is Question, IOpenQuestion {
string[] memory _tags,
uint256 _deadline,
address _plasa
) Question(_space, _points, _title, _description, _deadline, _tags, _plasa) {
)
Question(_space, _points, _title, _description, _deadline, _tags, _plasa)
onlyAllowed(ISpaceAccessControl.PermissionName.CreateOpenQuestion)
{
questionType = QuestionType.Open;

// Check if creator has permission to create open questions
if (!space.hasPermission(ISpaceAccessControl.PermissionName.CreateOpenQuestion, msg.sender)) {
revert NotAllowed(msg.sender, ISpaceAccessControl.PermissionName.CreateOpenQuestion);
}
}

/// @inheritdoc IOpenQuestion
function addOption(
string memory _title,
string memory _description
) external whileActive returns (uint256 optionId) {
if (!space.canAddOpenQuestionOption(msg.sender)) {
revert InsufficientPoints();
}
if (!space.canAddOpenQuestionOption(msg.sender)) revert InsufficientPoints();

optionId = _addOption(_title, _description);
}

/// @inheritdoc IOpenQuestion
function vetoOption(
uint256 optionId
)
external
whileActive
validOption(optionId)
onlyAllowed(ISpaceAccessControl.PermissionName.VetoOpenQuestionOption)
{
vetoed[optionId] = true;

emit OptionVetoed(msg.sender, optionId);
}

/// @inheritdoc IOpenQuestion
function liftOptionVeto(
uint256 optionId
)
external
whileActive
validOption(optionId)
onlyAllowed(ISpaceAccessControl.PermissionName.LiftVetoOpenQuestionOption)
{
vetoed[optionId] = false;

emit OptionVetoLifted(msg.sender, optionId);
}

/// @inheritdoc Question
function _isVetoed(uint256 optionId) internal view override returns (bool) {
return vetoed[optionId];
}

/// @notice Processes a vote for a specific option
/// @dev Overrides the base _processVote function to check for duplicate votes
/// @param optionId The ID of the option being voted for
function _processVote(uint256 optionId) internal override {
if (userVotes[msg.sender][optionId]) {
revert UserAlreadyVotedThisOption(msg.sender, optionId);
}
if (userVotes[msg.sender][optionId]) revert UserAlreadyVotedOption(msg.sender, optionId);

userVotes[msg.sender][optionId] = true;
}

Expand Down
22 changes: 21 additions & 1 deletion src/questions/interfaces/IOpenQuestion.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,36 @@ interface IOpenQuestion is IQuestion {
/// @dev This error should be used when enforcing point requirements for adding options
error InsufficientPoints();

/// @dev Emitted when a question is vetoed
/// @param moderator The address of the moderator who vetoed the option
/// @param optionId The ID of the option vetoed
event OptionVetoed(address indexed moderator, uint256 optionId);

/// @dev Emitted when a veto is lifted
/// @param moderator The address of the moderator who lifted the veto
/// @param optionId The ID of the option whose veto was lifted
event OptionVetoLifted(address indexed moderator, uint256 optionId);

/// @notice Thrown when a user tries to vote for an option they've already voted for
/// @dev This error helps prevent double voting
/// @param voter The address of the voter attempting to vote again
/// @param optionId The ID of the option the user is attempting to vote for again
error UserAlreadyVotedThisOption(address voter, uint256 optionId);
error UserAlreadyVotedOption(address voter, uint256 optionId);

/// @notice Adds a new option to the question
/// @dev Requires the caller to have sufficient points. Implementers should emit an event after adding the option.
/// @param _title The title of the new option
/// @param _description The description of the new option
/// @return optionId The ID of the newly added option
function addOption(string memory _title, string memory _description) external returns (uint256 optionId);

/// @notice Vets an option
/// @dev Only allowed users can veto options
/// @param optionId The ID of the option to veto
function vetoOption(uint256 optionId) external;

/// @notice Lifts a veto on an option
/// @dev Only allowed users can lift vetoes
/// @param optionId The ID of the option to lift the veto on
function liftOptionVeto(uint256 optionId) external;
}
1 change: 1 addition & 0 deletions src/questions/interfaces/IQuestionView.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ interface IQuestionView {
string proposerName;
uint256 voteCount;
uint256 pointsAtDeadline;
bool isVetoed;
}

/// @notice Struct containing user-specific option data
Expand Down

0 comments on commit 4a46a6f

Please sign in to comment.