Skip to content

Commit

Permalink
Merge pull request #36 from aragon/feature/metadata
Browse files Browse the repository at this point in the history
metadata extension
  • Loading branch information
novaknole authored Oct 8, 2024
2 parents 0647d5d + 7b3d080 commit ba6dcf8
Show file tree
Hide file tree
Showing 12 changed files with 264 additions and 85 deletions.
1 change: 0 additions & 1 deletion packages/contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ const config: HardhatUserConfig = {
solidity: {
version: '0.8.17',
settings: {
viaIR: true,
metadata: {
// Not including the metadata hash
// https://github.com/paulrberg/hardhat-template/issues/31
Expand Down
13 changes: 11 additions & 2 deletions packages/contracts/src/MajorityVotingBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {PluginUUPSUpgradeable} from "@aragon/osx-commons-contracts/src/plugin/Pl
import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";
import {IProposal} from "@aragon/osx-commons-contracts/src/plugin/extensions/proposal/IProposal.sol";
import {Action} from "@aragon/osx-commons-contracts/src/executors/IExecutor.sol";
import {MetadataExtensionUpgradeable} from "@aragon/osx-commons-contracts/src/utils/metadata/MetadataExtensionUpgradeable.sol";

import {IMajorityVoting} from "./IMajorityVoting.sol";

Expand Down Expand Up @@ -122,6 +123,7 @@ abstract contract MajorityVotingBase is
IMajorityVoting,
Initializable,
ERC165Upgradeable,
MetadataExtensionUpgradeable,
PluginUUPSUpgradeable,
ProposalUpgradeable
{
Expand Down Expand Up @@ -300,12 +302,14 @@ abstract contract MajorityVotingBase is
IDAO _dao,
VotingSettings calldata _votingSettings,
TargetConfig calldata _targetConfig,
uint256 _minApprovals
uint256 _minApprovals,
bytes calldata _pluginMetadata
) internal onlyInitializing {
__PluginUUPSUpgradeable_init(_dao);
_updateVotingSettings(_votingSettings);
_updateMinApprovals(_minApprovals);
_setTargetConfig(_targetConfig);
_updateMetadata(_pluginMetadata);
}

/// @notice Checks if this or the parent contract supports an interface by its ID.
Expand All @@ -317,7 +321,12 @@ abstract contract MajorityVotingBase is
public
view
virtual
override(ERC165Upgradeable, PluginUUPSUpgradeable, ProposalUpgradeable)
override(
ERC165Upgradeable,
MetadataExtensionUpgradeable,
PluginUUPSUpgradeable,
ProposalUpgradeable
)
returns (bool)
{
// In addition to the current IMajorityVoting interface, also support previous version
Expand Down
45 changes: 33 additions & 12 deletions packages/contracts/src/TokenVoting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ contract TokenVoting is IMembership, MajorityVotingBase {
VotingSettings calldata _votingSettings,
IVotesUpgradeable _token,
TargetConfig calldata _targetConfig,
uint256 _minApprovals
uint256 _minApprovals,
bytes calldata _pluginMetadata
) external onlyCallAtInitialization reinitializer(2) {
__MajorityVotingBase_init(_dao, _votingSettings, _targetConfig, _minApprovals);
__MajorityVotingBase_init(_dao, _votingSettings, _targetConfig, _minApprovals, _pluginMetadata);

votingToken = _token;

Expand All @@ -61,13 +62,14 @@ contract TokenVoting is IMembership, MajorityVotingBase {
/// @param _initData The initialization data to be passed to via `upgradeToAndCall` (see [ERC-1967](https://docs.openzeppelin.com/contracts/4.x/api/proxy#ERC1967Upgrade)).
function initializeFrom(uint16 _fromBuild, bytes calldata _initData) external reinitializer(2) {
if (_fromBuild < 3) {
(uint256 minApprovals, TargetConfig memory targetConfig) = abi.decode(
_initData,
(uint256, TargetConfig)
);
(uint256 minApprovals, TargetConfig memory targetConfig, bytes memory pluginMetadata) = abi
.decode(_initData, (uint256, TargetConfig, bytes));

_updateMinApprovals(minApprovals);

_setTargetConfig(targetConfig);

_updateMetadata(pluginMetadata);
}
}

Expand Down Expand Up @@ -158,14 +160,13 @@ contract TokenVoting is IMembership, MajorityVotingBase {
vote(proposalId, _voteOption, _tryEarlyExecution);
}

emit ProposalCreated(
proposalId,
_msgSender(),
_startDate,
_endDate,
_emitProposalCreatedEvent(
_metadata,
_actions,
_allowFailureMap
_allowFailureMap,
proposalId,
_startDate,
_endDate
);
}

Expand Down Expand Up @@ -311,6 +312,26 @@ contract TokenVoting is IMembership, MajorityVotingBase {
return true;
}

/// @dev Helper function to avoid stack too deep in non via-ir compilation mode.
function _emitProposalCreatedEvent(
bytes calldata _metadata,
Action[] calldata _actions,
uint256 _allowFailureMap,
uint256 proposalId,
uint64 _startDate,
uint64 _endDate
) private {
emit ProposalCreated(
proposalId,
_msgSender(),
_startDate,
_endDate,
_metadata,
_actions,
_allowFailureMap
);
}

/// @dev This empty reserved space is put in place to allow future versions to add new
/// variables without shifting down storage in the inheritance chain.
/// https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
Expand Down
91 changes: 58 additions & 33 deletions packages/contracts/src/TokenVotingSetup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ contract TokenVotingSetup is PluginUpgradeableSetup {
bytes32 public constant SET_TARGET_CONFIG_PERMISSION_ID =
keccak256("SET_TARGET_CONFIG_PERMISSION");

/// @notice The ID of the permission required to call the `updateMetadata` function.
bytes32 public constant UPDATE_METADATA_PERMISSION_ID = keccak256("UPDATE_METADATA_PERMISSION");

/// @notice The ID of the permission required to call the `upgradeToAndCall` function.
bytes32 internal constant UPGRADE_PLUGIN_PERMISSION_ID = keccak256("UPGRADE_PLUGIN_PERMISSION");

Expand Down Expand Up @@ -104,25 +107,23 @@ contract TokenVotingSetup is PluginUpgradeableSetup {
// only used for GovernanceERC20(token is not passed)
GovernanceERC20.MintSettings memory mintSettings,
PluginUUPSUpgradeable.TargetConfig memory targetConfig,
uint256 minApprovals
uint256 minApprovals,
bytes memory pluginMetadata
) = abi.decode(
_data,
(
MajorityVotingBase.VotingSettings,
TokenSettings,
GovernanceERC20.MintSettings,
PluginUUPSUpgradeable.TargetConfig,
uint256
uint256,
bytes
)
);

address token = tokenSettings.addr;
bool tokenAddressNotZero = token != address(0);

// Prepare helpers.
address[] memory helpers = new address[](2);

if (tokenAddressNotZero) {
if (tokenSettings.addr != address(0)) {
if (!token.isContract()) {
revert TokenNotContract(token);
}
Expand Down Expand Up @@ -153,28 +154,29 @@ contract TokenVotingSetup is PluginUpgradeableSetup {
);
}


// Prepare and deploy plugin proxy.
plugin = address(tokenVotingBase).deployUUPSProxy(
abi.encodeWithSignature(
"initialize(address,(uint8,uint32,uint32,uint64,uint256),address,(address,uint8),uint256)",
IDAO(_dao),
votingSettings,
IVotesUpgradeable(token),
targetConfig,
minApprovals
abi.encodeCall(
TokenVoting.initialize,
(
IDAO(_dao),
votingSettings,
IVotesUpgradeable(token),
targetConfig,
minApprovals,
pluginMetadata
)
)
);

address votingPowerCondition = address(new VotingPowerCondition(plugin));

helpers[0] = token;
helpers[1] = votingPowerCondition;
preparedSetupData.helpers = new address[](2);
preparedSetupData.helpers[0] = token;
preparedSetupData.helpers[1] = address(new VotingPowerCondition(plugin));

// Prepare permissions
PermissionLib.MultiTargetPermission[]
memory permissions = new PermissionLib.MultiTargetPermission[](
tokenAddressNotZero ? 4 : 5
tokenSettings.addr != address(0) ? 5 : 6
);

// Set plugin permissions to be granted.
Expand All @@ -200,7 +202,7 @@ contract TokenVotingSetup is PluginUpgradeableSetup {
PermissionLib.Operation.GrantWithCondition,
plugin,
address(type(uint160).max), // ANY_ADDR
votingPowerCondition,
preparedSetupData.helpers[1], // VotingPowerCondition
TokenVoting(IMPLEMENTATION).CREATE_PROPOSAL_PERMISSION_ID()
);

Expand All @@ -212,10 +214,18 @@ contract TokenVotingSetup is PluginUpgradeableSetup {
permissionId: SET_TARGET_CONFIG_PERMISSION_ID
});

if (!tokenAddressNotZero) {
permissions[4] = PermissionLib.MultiTargetPermission({
operation: PermissionLib.Operation.Grant,
where: plugin,
who: _dao,
condition: PermissionLib.NO_CONDITION,
permissionId: UPDATE_METADATA_PERMISSION_ID
});

if (tokenSettings.addr == address(0)) {
bytes32 tokenMintPermission = GovernanceERC20(token).MINT_PERMISSION_ID();

permissions[4] = PermissionLib.MultiTargetPermission({
permissions[5] = PermissionLib.MultiTargetPermission({
operation: PermissionLib.Operation.Grant,
where: token,
who: _dao,
Expand All @@ -224,7 +234,6 @@ contract TokenVotingSetup is PluginUpgradeableSetup {
});
}

preparedSetupData.helpers = helpers;
preparedSetupData.permissions = permissions;
}

Expand All @@ -243,7 +252,7 @@ contract TokenVotingSetup is PluginUpgradeableSetup {
address votingPowerCondition = address(new VotingPowerCondition(_payload.plugin));

PermissionLib.MultiTargetPermission[]
memory permissions = new PermissionLib.MultiTargetPermission[](3);
memory permissions = new PermissionLib.MultiTargetPermission[](4);

permissions[0] = PermissionLib.MultiTargetPermission({
operation: PermissionLib.Operation.Revoke,
Expand All @@ -269,6 +278,14 @@ contract TokenVotingSetup is PluginUpgradeableSetup {
permissionId: SET_TARGET_CONFIG_PERMISSION_ID
});

permissions[3] = PermissionLib.MultiTargetPermission({
operation: PermissionLib.Operation.Grant,
where: _payload.plugin,
who: _dao,
condition: PermissionLib.NO_CONDITION,
permissionId: UPDATE_METADATA_PERMISSION_ID
});

preparedSetupData.permissions = permissions;
preparedSetupData.helpers = new address[](1);
preparedSetupData.helpers[0] = votingPowerCondition;
Expand All @@ -283,7 +300,7 @@ contract TokenVotingSetup is PluginUpgradeableSetup {
SetupPayload calldata _payload
) external view returns (PermissionLib.MultiTargetPermission[] memory permissions) {
// Prepare permissions.
permissions = new PermissionLib.MultiTargetPermission[](4);
permissions = new PermissionLib.MultiTargetPermission[](5);

// Set permissions to be Revoked.
permissions[0] = PermissionLib.MultiTargetPermission({
Expand All @@ -310,13 +327,21 @@ contract TokenVotingSetup is PluginUpgradeableSetup {
permissionId: SET_TARGET_CONFIG_PERMISSION_ID
});

permissions[3] = PermissionLib.MultiTargetPermission(
PermissionLib.Operation.Revoke,
_payload.plugin,
address(type(uint160).max), // ANY_ADDR
PermissionLib.NO_CONDITION,
TokenVoting(IMPLEMENTATION).CREATE_PROPOSAL_PERMISSION_ID()
);
permissions[3] = PermissionLib.MultiTargetPermission({
operation: PermissionLib.Operation.Revoke,
where: _payload.plugin,
who: _dao,
condition: PermissionLib.NO_CONDITION,
permissionId: UPDATE_METADATA_PERMISSION_ID
});

permissions[4] = PermissionLib.MultiTargetPermission({
operation: PermissionLib.Operation.Revoke,
where: _payload.plugin,
who: address(type(uint160).max), // ANY_ADDR
condition: PermissionLib.NO_CONDITION,
permissionId: TokenVoting(IMPLEMENTATION).CREATE_PROPOSAL_PERMISSION_ID()
});
}

/// @notice Unsatisfiably determines if the token is an IVotes interface.
Expand Down
20 changes: 16 additions & 4 deletions packages/contracts/src/build-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@
"name": "minApproval",
"type": "uint256",
"description": "The minimum amount of yes votes needed for the proposal advance."
},
{
"internalType": "bytes",
"name": "metadata",
"type": "bytes",
"description": "The metadata that contains the information about the multisig."
}
]
},
Expand All @@ -127,11 +133,11 @@
"inputs": []
},
"3": {
"description": "The information required for the installation.",
"description": "The information required for the update.",
"inputs": [
{
"internalType": "uint256",
"name": "minApproval",
"name": "minApprovals",
"type": "uint256",
"description": "The minimum amount of yes votes needed for the proposal advance."
},
Expand All @@ -144,16 +150,22 @@
"description": "The target contract to which actions will be forwarded to for execution."
},
{
"internalType": "enum PluginUUPSUpgradeable.Operation",
"internalType": "uint8",
"name": "operation",
"type": "uint8",
"description": "The operation type(either `call` or `delegatecall`) that will be used for execution forwarding."
}
],
"internalType": "struct PluginUUPSUpgradeable.TargetConfig",
"internalType": "struct TokenVoting.TargetConfig",
"name": "TargetConfig",
"type": "tuple",
"description": "The initial target config"
},
{
"internalType": "bytes",
"name": "metadata",
"type": "bytes",
"description": "The metadata that contains the information about the multisig."
}
]
}
Expand Down
5 changes: 3 additions & 2 deletions packages/contracts/src/mocks/MajorityVotingMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ contract MajorityVotingMock is MajorityVotingBase {
IDAO _dao,
VotingSettings calldata _votingSettings,
TargetConfig calldata _targetConfig,
uint256 _minApprovals
uint256 _minApprovals,
bytes calldata _metadata
) public initializer {
__MajorityVotingBase_init(_dao, _votingSettings, _targetConfig, _minApprovals);
__MajorityVotingBase_init(_dao, _votingSettings, _targetConfig, _minApprovals, _metadata);
}

function createProposalId(
Expand Down
Loading

0 comments on commit ba6dcf8

Please sign in to comment.