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

Add Selected Strategies Option #239

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 42 additions & 12 deletions src/Space.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/O
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import { IERC4824 } from "src/interfaces/IERC4824.sol";
import { ISpace, ISpaceActions, ISpaceState, ISpaceOwnerActions } from "src/interfaces/ISpace.sol";
import { IERC4824 } from "./interfaces/IERC4824.sol";
import { ISpace, ISpaceActions, ISpaceState, ISpaceOwnerActions } from "./interfaces/ISpace.sol";
import {
Choice,
FinalizationStatus,
Expand All @@ -19,10 +19,10 @@ import {
InitializeCalldata,
TRUE,
FALSE
} from "src/types.sol";
import { IVotingStrategy } from "src/interfaces/IVotingStrategy.sol";
import { IExecutionStrategy } from "src/interfaces/IExecutionStrategy.sol";
import { IProposalValidationStrategy } from "src/interfaces/IProposalValidationStrategy.sol";
} from "./types.sol";
import { IVotingStrategy } from "./interfaces/IVotingStrategy.sol";
import { IExecutionStrategy } from "./interfaces/IExecutionStrategy.sol";
import { IProposalValidationStrategy } from "./interfaces/IProposalValidationStrategy.sol";
import { SXUtils } from "./utils/SXUtils.sol";
import { BitPacker } from "./utils/BitPacker.sol";

Expand Down Expand Up @@ -65,8 +65,9 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra
Strategy public override proposalValidationStrategy;
/// @inheritdoc ISpaceState
mapping(address auth => uint256 allowed) public override authenticators;
/// @inheritdoc ISpaceState
mapping(uint256 proposalId => Proposal proposal) public override proposals;

mapping(uint256 proposalId => Proposal proposal) public proposals;

// @inheritdoc ISpaceState
mapping(uint256 proposalId => mapping(Choice choice => uint256 votePower)) public override votePower;
/// @inheritdoc ISpaceState
Expand Down Expand Up @@ -168,6 +169,15 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra
}
}

function setAuthenticator(address auth) external onlyOwner{
authenticators[auth] = TRUE;
}

function addVotingStrategy(Strategy[] calldata newStrategy) external onlyAuthenticator returns(uint8){
_addVotingStrategies(newStrategy);
return nextVotingStrategyIndex - 1;
}

/// @dev Gates access to whitelisted authenticators only.
modifier onlyAuthenticator() {
if (authenticators[msg.sender] == FALSE) revert AuthenticatorNotWhitelisted();
Expand Down Expand Up @@ -204,7 +214,8 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra
address author,
string calldata metadataURI,
Strategy calldata executionStrategy,
bytes calldata userProposalValidationParams
bytes calldata userProposalValidationParams,
uint8[] calldata selectedVotingStrategiesIndices
) external override onlyAuthenticator {
if (
!IProposalValidationStrategy(proposalValidationStrategy.addr).validate(
Expand All @@ -230,9 +241,10 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra
maxEndBlockNumber,
FinalizationStatus.Pending,
executionPayloadHash,
activeVotingStrategies
activeVotingStrategies,
_getSelectedBitArray(selectedVotingStrategiesIndices)
);

proposals[nextProposalId] = proposal;
emit ProposalCreated(nextProposalId, author, proposal, metadataURI, executionStrategy.params);

Expand All @@ -256,11 +268,16 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra

voteRegistry[proposalId][voter] = TRUE;

uint256 activeSelectedVotingStrategies =
proposal.selectedVotingStrategies > 0 ?
proposal.selectedVotingStrategies :
proposal.activeVotingStrategies;

uint256 votingPower = _getCumulativePower(
voter,
proposal.startBlockNumber,
userVotingStrategies,
proposal.activeVotingStrategies
activeSelectedVotingStrategies
);
if (votingPower == 0) revert UserHasNoVotingPower();
votePower[proposalId][choice] += votingPower;
Expand Down Expand Up @@ -435,4 +452,17 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra
}
return totalVotingPower;
}

function _getSelectedBitArray(uint8[] calldata selectedIndices) internal returns(uint256) {
uint256 cachedActiveStrategies = activeVotingStrategies;
uint256 selectedVotingStrategies;
for(uint256 i = 0; i < selectedIndices.length; i++) {
uint8 strategyIndex = selectedIndices[i];
// Selected strategy must also be active
if(cachedActiveStrategies.isBitSet(strategyIndex)){
selectedVotingStrategies = selectedVotingStrategies.setBit(strategyIndex, true);
}
}
return selectedVotingStrategies;
}
}
2 changes: 1 addition & 1 deletion src/interfaces/IERC4824.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;
pragma solidity 0.8.19;

/// @title EIP-4824 Common Interfaces for DAOs
/// @notice See https://eips.ethereum.org/EIPS/eip-4824
Expand Down
6 changes: 4 additions & 2 deletions src/interfaces/space/ISpaceActions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pragma solidity ^0.8.18;

import { Choice, IndexedStrategy, Strategy, InitializeCalldata } from "src/types.sol";
import { Choice, IndexedStrategy, Strategy, InitializeCalldata } from "../../types.sol";

/// @title Space Actions
/// @notice User focused actions that can be performed on a space.
Expand Down Expand Up @@ -31,11 +31,13 @@ interface ISpaceActions {
/// @param executionStrategy The execution strategy for the proposal,
/// consisting of a strategy address and an execution payload.
/// @param userProposalValidationParams The user provided parameters for proposal validation.
/// @param selectedVotingStrategyIndices The indices of voting strategies to use for this specific proposal. Empty array yields all active.
function propose(
address author,
string calldata metadataURI,
Strategy calldata executionStrategy,
bytes calldata userProposalValidationParams
bytes calldata userProposalValidationParams,
uint8[] calldata selectedVotingStrategyIndices
) external;

/// @notice Casts a vote.
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/space/ISpaceEvents.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import { IndexedStrategy, Proposal, Strategy, Choice, InitializeCalldata } from "src/types.sol";
import { IndexedStrategy, Proposal, Strategy, Choice, InitializeCalldata } from "../../types.sol";

/// @title Space Events
interface ISpaceEvents {
Expand Down
8 changes: 5 additions & 3 deletions src/interfaces/space/ISpaceState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

pragma solidity ^0.8.18;

import { Choice, Proposal, ProposalStatus, FinalizationStatus, Strategy } from "src/types.sol";
import { IExecutionStrategy } from "src/interfaces/IExecutionStrategy.sol";
import { Choice, Proposal, ProposalStatus, FinalizationStatus, Strategy } from "../../types.sol";
import { IExecutionStrategy } from "../../interfaces/IExecutionStrategy.sol";

/// @title Space State
interface ISpaceState {
Expand Down Expand Up @@ -63,6 +63,7 @@ interface ISpaceState {
/// @return finalizationStatus The finalization status of the proposal. See `FinalizationStatus`.
/// @return executionPayloadHash The keccak256 hash of the execution payload.
/// @return activeVotingStrategies The bit array of the active voting strategies for the proposal.
/// @return selectedVotingStrategies
function proposals(
uint256 proposalId
)
Expand All @@ -76,7 +77,8 @@ interface ISpaceState {
uint32 maxEndBlockNumber,
FinalizationStatus finalizationStatus,
bytes32 executionPayloadHash,
uint256 activeVotingStrategies
uint256 activeVotingStrategies,
uint256 selectedVotingStrategies
);

/// @notice Returns the status of a proposal.
Expand Down
6 changes: 5 additions & 1 deletion src/types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
pragma solidity ^0.8.18;

import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import { IExecutionStrategy } from "src/interfaces/IExecutionStrategy.sol";
import { IExecutionStrategy } from "./interfaces/IExecutionStrategy.sol";

/// @dev Constants used to replace the `bool` type in mappings for gas efficiency.
uint256 constant TRUE = 1;
Expand Down Expand Up @@ -37,6 +37,10 @@ struct Proposal {
// Bit array where the index of each each bit corresponds to whether the voting strategy.
// at that index is active at the time of proposal creation.
uint256 activeVotingStrategies;
// SLOT 5:
// Bit array where the index of each bit corresponds to whether the voting strategy
// at that index was selected for the given proposal. empty array/0 indicates all active strategies are valid.
uint256 selectedVotingStrategies;
}

/// @notice The data stored for each strategy.
Expand Down
2 changes: 1 addition & 1 deletion src/utils/SXUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pragma solidity ^0.8.18;

import { IndexedStrategy } from "src/types.sol";
import { IndexedStrategy } from "../types.sol";

/// @title Snapshot X Types Utilities Library
library SXUtils {
Expand Down