-
Notifications
You must be signed in to change notification settings - Fork 2.3k
feat(protocol): add Proposal0020 to revamp the security council #22010
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
Open
ggonzalez94
wants to merge
9
commits into
main
Choose a base branch
from
revamp-security-council
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
b2bed06
feat(protocol): add BuildDirectProposal base for direct-DAO governanc…
da239c7
feat(protocol): add Proposal0020 to revamp the security council
de136ba
docs(protocol): add generated Proposal0020.action.md
6d57357
refactor(protocol): drop the verify mode from BuildDirectProposal
52a5227
refactor(protocol): drop Proposal0020.metadata.json
797d328
Merge branch 'main' into revamp-security-council
dantaik dc5fdc6
refactor(protocol): move security council seat addresses to LibL1Addr…
dantaik 330175c
fix(protocol): address review findings on Proposal0020 (#22016)
dantaik b78e28b
Merge branch 'main' into revamp-security-council
dantaik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
262 changes: 262 additions & 0 deletions
262
packages/protocol/script/layer1/governance/BuildDirectProposal.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,262 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.24; | ||
|
|
||
| import "forge-std/src/Script.sol"; | ||
| import { LibL1Addrs as L1 } from "src/layer1/mainnet/LibL1Addrs.sol"; | ||
|
|
||
| /// @notice Base for DAO proposals whose actions the Aragon DAO executes directly — i.e. | ||
| /// changes to the governance stack itself (SignerList, multisig plugins, | ||
| /// EncryptionRegistry). Protocol changes executed via the DAO Controller use | ||
| /// `BuildProposal` instead; the controller holds no permissions on the Aragon contracts, | ||
| /// so its `Execute` path cannot carry these actions. | ||
| /// | ||
| /// Such proposals are created on the Standard Multisig, usually via the Taiko DAO UI | ||
| /// (dao.taiko.xyz): the UI pins the metadata to IPFS and assembles the `createProposal` | ||
| /// call from actions pasted into its custom-action (calldata) form. | ||
| /// | ||
| /// Modes (`MODE` env): | ||
| /// - `print`: write `Proposal$P.action.md` — the actions to paste into the DAO UI. | ||
| /// If `METADATA_URI` is set, also emit the full `createProposal` calldata | ||
| /// for direct submission (requires pre-pinned metadata). | ||
| /// - `l1dryrun`: on a fork — run `checkBaseline()`, create the proposal as `SENDER` | ||
| /// (`APPROVE=true` additionally approves at creation; default false), | ||
| /// run `simulatePreExecution()`, apply the actions as the DAO, then run | ||
| /// `checkPostState()`. | ||
| /// | ||
| /// The print-mode direct-submission fallback always encodes `approveProposal = false`; | ||
| /// a direct submitter approves separately. | ||
| abstract contract BuildDirectProposal is Script { | ||
| /// @dev Mirrors Aragon OSx `IDAO.Action`. | ||
| struct Action { | ||
| address to; | ||
| uint256 value; | ||
| bytes data; | ||
| } | ||
|
|
||
| error MissingEnv(string name); | ||
| error CheckFailed(string what); | ||
|
|
||
| function run() external { | ||
| string memory mode = vm.envString("MODE"); | ||
| if (keccak256(abi.encodePacked(mode)) == keccak256(abi.encodePacked("print"))) { | ||
| logProposalAction(vm.envString("P")); | ||
| } else if (keccak256(abi.encodePacked(mode)) == keccak256(abi.encodePacked("l1dryrun"))) { | ||
| dryrunL1(); | ||
| } else { | ||
| console2.log("Error: Invalid mode. Must be one of: print, l1dryrun"); | ||
| } | ||
| } | ||
|
|
||
| /// @dev The actions the DAO will execute, in order. | ||
| function buildDaoActions() internal view virtual returns (Action[] memory); | ||
|
|
||
| /// @dev Pre-creation assertions against the forked chain (dryrun only). | ||
| function checkBaseline() internal view virtual { } | ||
|
|
||
| /// @dev Out-of-band steps that must happen between proposal creation and DAO | ||
| /// execution, simulated on the fork (dryrun only). Default: none. | ||
| function simulatePreExecution() internal virtual { } | ||
|
|
||
| /// @dev Post-execution assertions against the forked chain (dryrun only). | ||
| function checkPostState() internal view virtual { } | ||
|
|
||
| // --------------------------------------------------------------- | ||
| // Mode implementations | ||
| // --------------------------------------------------------------- | ||
|
|
||
| function logProposalAction(string memory _proposalId) internal { | ||
| Action[] memory actions = buildDaoActions(); | ||
|
|
||
| string memory fileName = | ||
| string.concat("./script/layer1/proposals/Proposal", _proposalId, ".action.md"); | ||
| string memory fileContent = string.concat( | ||
| "# Proposal", | ||
| _proposalId, | ||
| "\n\nCreated via the Taiko DAO UI on the Standard Multisig `", | ||
| vm.toString(L1.DAO_STANDARD_MULTISIG), | ||
| "`.\nThe UI pins the metadata and assembles `createProposal`; paste each action", | ||
| " below into the\nUI's custom-action (calldata) form, in order. After creation,", | ||
| " run the `verify` mode\nagainst the new proposal id before approving.\n\n", | ||
| "- Destination plugin (set by the UI): `", | ||
| vm.toString(L1.DAO_OPTIMISTIC_TOKEN_VOTING_PLUGIN), | ||
| "`\n" | ||
| ); | ||
|
|
||
| for (uint256 i; i < actions.length; ++i) { | ||
| fileContent = string.concat( | ||
| fileContent, | ||
| "\n## Action ", | ||
| vm.toString(i + 1), | ||
| "\n- To: `", | ||
| vm.toString(actions[i].to), | ||
| "` (", | ||
| nameOf(actions[i].to), | ||
| ")\n- Value: `", | ||
| vm.toString(actions[i].value), | ||
| "`\n- Data: `", | ||
| vm.toString(actions[i].data), | ||
| "`\n" | ||
| ); | ||
| } | ||
|
|
||
| // Fallback for submitting createProposal directly (not via the UI): requires a | ||
| // pre-pinned METADATA_URI, since nothing pins the metadata for you outside the UI. | ||
| string memory metadataURI = vm.envOr("METADATA_URI", string("")); | ||
| if (bytes(metadataURI).length > 0) { | ||
| fileContent = string.concat( | ||
| fileContent, | ||
| "\n## Direct submission fallback (bypassing the UI)\n- To (Standard Multisig): `", | ||
| vm.toString(L1.DAO_STANDARD_MULTISIG), | ||
| "`\n- Function: `createProposal`\n- Value: `0`\n- Metadata URI: `", | ||
| metadataURI, | ||
| "`\n- Calldata: `", | ||
| vm.toString(buildCreateProposalCalldata(metadataURI, false)), | ||
| "`\n" | ||
| ); | ||
| } | ||
|
|
||
| vm.writeFile(fileName, fileContent); | ||
| console2.log(fileContent); | ||
| console2.log("Proposal action details written to", fileName); | ||
| } | ||
|
|
||
| function dryrunL1() internal { | ||
| checkBaseline(); | ||
|
|
||
| address sender = vm.envOr("SENDER", address(0)); | ||
| if (sender == address(0)) revert MissingEnv("SENDER"); | ||
| string memory metadataURI = vm.envOr("METADATA_URI", string("ipfs://dryrun-placeholder")); | ||
|
|
||
| // Built before the prank: a subclass's buildDaoActions may make view calls, and | ||
| // an external call inside the argument expression would consume the prank. | ||
| bytes memory createCalldata = | ||
| buildCreateProposalCalldata(metadataURI, vm.envOr("APPROVE", false)); | ||
| vm.prank(sender); | ||
| (bool ok, bytes memory ret) = L1.DAO_STANDARD_MULTISIG.call(createCalldata); | ||
| logIfReverted(ok, ret); | ||
| check(ok, "createProposal reverted"); | ||
| console2.log("createProposal accepted, proposalId:", abi.decode(ret, (uint256))); | ||
|
|
||
| simulatePreExecution(); | ||
|
|
||
| Action[] memory actions = buildDaoActions(); | ||
| for (uint256 i; i < actions.length; ++i) { | ||
| check( | ||
| actions[i].to.code.length > 0, | ||
| string.concat("action ", vm.toString(i + 1), " target has no code") | ||
| ); | ||
| vm.prank(L1.DAO); | ||
| (ok, ret) = actions[i].to.call{ value: actions[i].value }(actions[i].data); | ||
| logIfReverted(ok, ret); | ||
| check(ok, string.concat("action ", vm.toString(i + 1), " reverted")); | ||
| } | ||
|
|
||
| checkPostState(); | ||
| console2.log("Dryrun OK"); | ||
| } | ||
|
|
||
| // --------------------------------------------------------------- | ||
| // Helpers for subclasses | ||
| // --------------------------------------------------------------- | ||
|
|
||
| /// @dev The full `Multisig.createProposal` calldata (dryrun and direct-submission | ||
| /// fallback; the DAO UI assembles this call itself). | ||
| function buildCreateProposalCalldata( | ||
| string memory _metadataURI, | ||
| bool _approveProposal | ||
| ) | ||
| internal | ||
| view | ||
| returns (bytes memory) | ||
| { | ||
| return abi.encodeWithSignature( | ||
| "createProposal(bytes,(address,uint256,bytes)[],address,bool)", | ||
| bytes(_metadataURI), | ||
| buildDaoActions(), | ||
| L1.DAO_OPTIMISTIC_TOKEN_VOTING_PLUGIN, | ||
| _approveProposal | ||
| ); | ||
| } | ||
|
|
||
| function logIfReverted(bool _ok, bytes memory _returnData) internal pure { | ||
| if (!_ok) { | ||
| console2.log("revert data:"); | ||
| console2.logBytes(_returnData); | ||
| } | ||
| } | ||
|
|
||
| function check(bool _ok, string memory _what) internal pure { | ||
| if (!_ok) revert CheckFailed(_what); | ||
| } | ||
|
|
||
| function readUint(address _target, string memory _sig) internal view returns (uint256) { | ||
| return abi.decode(readRaw(_target, abi.encodeWithSignature(_sig)), (uint256)); | ||
| } | ||
|
|
||
| function readBool( | ||
| address _target, | ||
| string memory _sig, | ||
| address _arg | ||
| ) | ||
| internal | ||
| view | ||
| returns (bool) | ||
| { | ||
| return abi.decode(readRaw(_target, abi.encodeWithSignature(_sig, _arg)), (bool)); | ||
| } | ||
|
|
||
| function readAddr( | ||
| address _target, | ||
| string memory _sig, | ||
| address _arg | ||
| ) | ||
| internal | ||
| view | ||
| returns (address) | ||
| { | ||
| return abi.decode(readRaw(_target, abi.encodeWithSignature(_sig, _arg)), (address)); | ||
| } | ||
|
|
||
| /// @dev SignerList settings: (encryptionRegistry, minSignerListLength). | ||
| function readSignerListSettings() internal view returns (address, uint16) { | ||
| return abi.decode( | ||
| readRaw(L1.DAO_SIGNER_LIST, abi.encodeWithSignature("settings()")), (address, uint16) | ||
| ); | ||
| } | ||
|
|
||
| /// @dev Standard Multisig settings: | ||
| /// (onlyListed, minApprovals, destinationProposalDuration, signerList, expirationPeriod). | ||
| function readStandardMultisigSettings() | ||
| internal | ||
| view | ||
| returns (bool, uint16, uint32, address, uint32) | ||
| { | ||
| return abi.decode( | ||
| readRaw(L1.DAO_STANDARD_MULTISIG, abi.encodeWithSignature("multisigSettings()")), | ||
| (bool, uint16, uint32, address, uint32) | ||
| ); | ||
| } | ||
|
|
||
| /// @dev Emergency Multisig settings: (onlyListed, minApprovals, signerList, expirationPeriod). | ||
| function readEmergencyMultisigSettings() internal view returns (bool, uint16, address, uint32) { | ||
| return abi.decode( | ||
| readRaw(L1.DAO_EMERGENCY_MULTISIG, abi.encodeWithSignature("multisigSettings()")), | ||
| (bool, uint16, address, uint32) | ||
| ); | ||
| } | ||
|
|
||
| function nameOf(address _target) internal pure returns (string memory) { | ||
| if (_target == L1.DAO_SIGNER_LIST) return "SignerList"; | ||
| if (_target == L1.DAO_STANDARD_MULTISIG) return "Standard Multisig"; | ||
| if (_target == L1.DAO_EMERGENCY_MULTISIG) return "Emergency Multisig"; | ||
| if (_target == L1.DAO_ENCRYPTION_REGISTRY) return "EncryptionRegistry"; | ||
| if (_target == L1.DAO) return "DAO"; | ||
| return "?"; | ||
| } | ||
|
|
||
| function readRaw(address _target, bytes memory _data) private view returns (bytes memory) { | ||
| (bool ok, bytes memory ret) = _target.staticcall(_data); | ||
| check(ok, "staticcall reverted"); | ||
| return ret; | ||
| } | ||
| } |
38 changes: 38 additions & 0 deletions
38
packages/protocol/script/layer1/proposals/Proposal0020.action.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # Proposal0020 | ||
|
|
||
| Created via the Taiko DAO UI on the Standard Multisig `0xD7dA1C25E915438720692bC55eb3a7170cA90321`. | ||
| The UI pins the metadata and assembles `createProposal`; paste each action below into the | ||
| UI's custom-action (calldata) form, in order. After creation, run the `verify` mode | ||
| against the new proposal id before approving. | ||
|
|
||
| - Destination plugin (set by the UI): `0x989E348275b659d36f8751ea1c10D146211650BE` | ||
|
|
||
| ## Action 1 | ||
| - To: `0x0F95E6968EC1B28c794CF1aD99609431de5179c2` (SignerList) | ||
| - Value: `0` | ||
| - Data: `0xed19b9240000000000000000000000002efdb93a3b87b930e553d504db67ee41c69c42d10000000000000000000000000000000000000000000000000000000000000004` | ||
|
|
||
| ## Action 2 | ||
| - To: `0x0F95E6968EC1B28c794CF1aD99609431de5179c2` (SignerList) | ||
| - Value: `0` | ||
| - Data: `0xe8906a2d00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ac5898b0fffd23f4ef09f0e50fa1bc4896ef7163` | ||
|
|
||
| ## Action 3 | ||
| - To: `0x0F95E6968EC1B28c794CF1aD99609431de5179c2` (SignerList) | ||
| - Value: `0` | ||
| - Data: `0x8d361e4300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000436a1075099a145417ebfc74bbac9605e3e4f1a70000000000000000000000000f40268ec0dc8d88cf2f22e227a29a0b478b635100000000000000000000000025d3e89bace2040ed3af7c4c7b505cfbb72fd6f1000000000000000000000000a384e224a3f3d664f43ebe33395ef0dcce67e8940000000000000000000000006268d189e011aa53a2f09a1fe159445beb3d878e` | ||
|
|
||
| ## Action 4 | ||
| - To: `0xD7dA1C25E915438720692bC55eb3a7170cA90321` (Standard Multisig) | ||
| - Value: `0` | ||
| - Data: `0xcf94a86d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000d2f000000000000000000000000000f95e6968ec1b28c794cf1ad99609431de5179c20000000000000000000000000000000000000000000000000000000000127500` | ||
|
|
||
| ## Action 5 | ||
| - To: `0x2AffADEb2ef5e1F2a7F58964ee191F1e88317ECd` (Emergency Multisig) | ||
| - Value: `0` | ||
| - Data: `0x7dacfc00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000040000000000000000000000000f95e6968ec1b28c794cf1ad99609431de5179c20000000000000000000000000000000000000000000000000000000000127500` | ||
|
|
||
| ## Action 6 | ||
| - To: `0x2eFDb93a3B87b930E553d504db67Ee41c69C42d1` (EncryptionRegistry) | ||
| - Value: `0` | ||
| - Data: `0x3348803f` | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.