From 47a14e2702a3e93de5850ee65c4f2a02bbe76797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8r=E2=88=82=C2=A1?= Date: Thu, 6 Jun 2024 10:27:54 +0200 Subject: [PATCH 1/3] Indexing optimistic proposal ID's --- src/OptimisticTokenVotingPlugin.sol | 7 ++++- test/OptimisticTokenVotingPlugin.t.sol | 39 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/OptimisticTokenVotingPlugin.sol b/src/OptimisticTokenVotingPlugin.sol index b220c1a..7e27507 100644 --- a/src/OptimisticTokenVotingPlugin.sol +++ b/src/OptimisticTokenVotingPlugin.sol @@ -99,6 +99,9 @@ contract OptimisticTokenVotingPlugin is /// @notice A mapping between proposal IDs and proposal information. mapping(uint256 => Proposal) internal proposals; + /// @notice A mapping to enumerate proposal ID's by index + mapping(uint256 => uint256) public proposalIds; + /// @notice Emitted when the vetoing settings are updated. /// @param minVetoRatio The minimum veto ratio needed to defeat the proposal, as a fraction of 1_000_000. /// @param minDuration The minimum duration of the proposal vote in seconds. @@ -358,6 +361,8 @@ contract OptimisticTokenVotingPlugin is _actions: _actions, _allowFailureMap: _allowFailureMap }); + // Index the ID to make it enumerable. Proposal ID's contain timestamps and cannot be iterated + proposalIds[proposalCount() - 1] = proposalId; // Store proposal related information Proposal storage proposal_ = proposals[proposalId]; @@ -549,5 +554,5 @@ contract OptimisticTokenVotingPlugin is } /// @notice This empty reserved space is put in place to allow future versions to add new variables without shifting down storage in the inheritance chain (see [OpenZeppelin's guide about storage gaps](https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps)). - uint256[45] private __gap; + uint256[44] private __gap; } diff --git a/test/OptimisticTokenVotingPlugin.t.sol b/test/OptimisticTokenVotingPlugin.t.sol index 872ce20..c57b141 100644 --- a/test/OptimisticTokenVotingPlugin.t.sol +++ b/test/OptimisticTokenVotingPlugin.t.sol @@ -862,6 +862,45 @@ contract OptimisticTokenVotingPluginTest is AragonTest { assertEq(proposalId, expectedPid, "Should have created proposal 2"); } + function test_CreateProposalIncrementsTheProposalCounter() public { + IDAO.Action[] memory actions = new IDAO.Action[](0); + assertEq(optimisticPlugin.proposalCount(), 0); + optimisticPlugin.createProposal("", actions, 0, 10 days); + assertEq(optimisticPlugin.proposalCount(), 1); + optimisticPlugin.createProposal("ipfs://", actions, 0, 10 days); + assertEq(optimisticPlugin.proposalCount(), 2); + optimisticPlugin.createProposal("", actions, 255, 15 days); + assertEq(optimisticPlugin.proposalCount(), 3); + optimisticPlugin.createProposal("", actions, 127, 20 days); + assertEq(optimisticPlugin.proposalCount(), 4); + optimisticPlugin.createProposal("ipfs://meta", actions, 0, 10 days); + assertEq(optimisticPlugin.proposalCount(), 5); + optimisticPlugin.createProposal("", actions, 0, 100 days); + assertEq(optimisticPlugin.proposalCount(), 6); + } + + function test_CreateProposalIndexesThePid() public { + uint256 expectedPid = uint256(block.timestamp) << 128 | uint256(block.timestamp + 10 days) << 64; + + IDAO.Action[] memory actions = new IDAO.Action[](0); + // 1 + assertEq(optimisticPlugin.proposalIds(0), 0); + optimisticPlugin.createProposal("", actions, 0, 10 days); + assertEq(optimisticPlugin.proposalIds(0), expectedPid); + + // 2 + expectedPid = uint256(block.timestamp) << 128 | uint256(block.timestamp + 100 days) << 64 | 1; + assertEq(optimisticPlugin.proposalIds(1), 0); + optimisticPlugin.createProposal("ipfs://meta", actions, 0, 100 days); + assertEq(optimisticPlugin.proposalIds(1), expectedPid); + + // 3 + expectedPid = uint256(block.timestamp) << 128 | uint256(block.timestamp + 50 days) << 64 | 2; + assertEq(optimisticPlugin.proposalIds(2), 0); + optimisticPlugin.createProposal("", actions, 0, 50 days); + assertEq(optimisticPlugin.proposalIds(2), expectedPid); + } + function test_CreateProposalEmitsAnEvent() public { uint256 expectedPid = uint256(block.timestamp) << 128 | uint256(block.timestamp + 10 days) << 64; From 118b5b6578b51139a5ab871eddacf61b0fb5531a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8r=E2=88=82=C2=A1?= Date: Thu, 6 Jun 2024 14:19:32 +0100 Subject: [PATCH 2/3] Making the registry and wall iterable --- DEPLOYMENTS.md | 11 +++++ README.md | 2 +- src/DelegationWall.sol | 8 ++++ src/PublicKeyRegistry.sol | 17 ++++++-- test/DelegationWall.t.sol | 18 ++++++++ test/PublicKeyRegistry.t.sol | 83 +++++++++++++++++++++++++++++++----- 6 files changed, 124 insertions(+), 15 deletions(-) create mode 100644 DEPLOYMENTS.md diff --git a/DEPLOYMENTS.md b/DEPLOYMENTS.md new file mode 100644 index 0000000..2960764 --- /dev/null +++ b/DEPLOYMENTS.md @@ -0,0 +1,11 @@ +# Deployment list + +## Mainnet + +## Sepolia + +On June 6th 2024: + +``` +``` + diff --git a/README.md b/README.md index 1dbdc4d..2e41a47 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ The Security Council has a standard multisig plugin and an emergency variant. Th [Learn more about Aragon OSx](#protocol-overview). -See [Deploying the DAO](#deploying-the-dao) below. +See [Deploying the DAO](#deploying-the-dao) below and check out the [latest deployments](./DEPLOYMENTS.md). ## Optimistic Token Voting plugin diff --git a/src/DelegationWall.sol b/src/DelegationWall.sol index bbe7f0f..8da9301 100644 --- a/src/DelegationWall.sol +++ b/src/DelegationWall.sol @@ -12,6 +12,7 @@ contract DelegationWall { /// @dev Stores the data registered by the delegate candidates mapping(address => Candidate) public candidates; + /// @dev Keeps track of the addresses that have been already registered, used to enumerate. address[] public candidateAddresses; @@ -33,6 +34,13 @@ contract DelegationWall { emit CandidateRegistered(msg.sender, _contentUrl); } + /// @notice Returns the list of candidate addresses registered + /// @dev Use this function to get all addresses in a single call. You can still call candidateAddresses[idx] to resolve them one by one. + function getCandidateAddresses() public view returns (address[] memory) { + return candidateAddresses; + } + + /// @notice Returns the number of candidate entries available function candidateCount() public view returns (uint256) { return candidateAddresses.length; } diff --git a/src/PublicKeyRegistry.sol b/src/PublicKeyRegistry.sol index f9df687..581ff4a 100644 --- a/src/PublicKeyRegistry.sol +++ b/src/PublicKeyRegistry.sol @@ -6,7 +6,10 @@ pragma solidity ^0.8.17; /// @author Aragon Association - 2024 /// @notice A smart contract where any wallet can register its own libsodium public key for encryption purposes contract PublicKeyRegistry { - mapping(address => bytes32) internal publicKeys; + mapping(address => bytes32) public publicKeys; + + /// @dev Allows to enumerate the wallets that have a public key registered + address[] public registeredWallets; /// @notice Emitted when a public key is registered event PublicKeyRegistered(address wallet, bytes32 publicKey); @@ -19,9 +22,17 @@ contract PublicKeyRegistry { publicKeys[msg.sender] = _publicKey; emit PublicKeyRegistered(msg.sender, _publicKey); + registeredWallets.push(msg.sender); + } + + /// @notice Returns the list of wallets that have registered a public key + /// @dev Use this function to get all addresses in a single call. You can still call registeredWallets[idx] to resolve them one by one. + function getRegisteredWallets() public view returns (address[] memory) { + return registeredWallets; } - function getPublicKey(address _wallet) public view returns (bytes32) { - return publicKeys[_wallet]; + /// @notice Returns the number of publicKey entries available + function registeredWalletCount() public view returns (uint256) { + return registeredWallets.length; } } diff --git a/test/DelegationWall.t.sol b/test/DelegationWall.t.sol index 2a73f2c..72082cb 100644 --- a/test/DelegationWall.t.sol +++ b/test/DelegationWall.t.sol @@ -216,6 +216,24 @@ contract EmergencyMultisigTest is AragonTest { assertEq(wall.candidateAddresses(3), david, "Incorrect candidate address"); } + function test_ShouldLoadTheRegisteredAddresses() public { + vm.startPrank(alice); + wall.register("https://"); + vm.startPrank(bob); + wall.register("https://taiko.xyz"); + vm.startPrank(carol); + wall.register("https://x.com/carol"); + vm.startPrank(david); + wall.register("https://defeat-goliath.org"); + + address[] memory candidates = wall.getCandidateAddresses(); + assertEq(candidates.length, 4); + assertEq(candidates[0], alice); + assertEq(candidates[1], bob); + assertEq(candidates[2], carol); + assertEq(candidates[3], david); + } + function test_ShouldEmitAnEventWhenRegistering() public { // Alice vm.startPrank(alice); diff --git a/test/PublicKeyRegistry.t.sol b/test/PublicKeyRegistry.t.sol index 9841c5e..f7fbd7f 100644 --- a/test/PublicKeyRegistry.t.sol +++ b/test/PublicKeyRegistry.t.sol @@ -18,37 +18,37 @@ contract EmergencyMultisigTest is AragonTest { } function test_ShouldRegisterAPublicKey() public { - assertEq(registry.getPublicKey(alice), 0x0000000000000000000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(alice), 0x0000000000000000000000000000000000000000000000000000000000000000); // Alice vm.startPrank(alice); registry.setPublicKey(0x1234000000000000000000000000000000000000000000000000000000000000); - assertEq(registry.getPublicKey(alice), 0x1234000000000000000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(alice), 0x1234000000000000000000000000000000000000000000000000000000000000); // Bob vm.startPrank(bob); registry.setPublicKey(0x0000567800000000000000000000000000000000000000000000000000000000); - assertEq(registry.getPublicKey(alice), 0x1234000000000000000000000000000000000000000000000000000000000000); - assertEq(registry.getPublicKey(bob), 0x0000567800000000000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(alice), 0x1234000000000000000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(bob), 0x0000567800000000000000000000000000000000000000000000000000000000); // Carol vm.startPrank(carol); registry.setPublicKey(0x0000000090ab0000000000000000000000000000000000000000000000000000); - assertEq(registry.getPublicKey(alice), 0x1234000000000000000000000000000000000000000000000000000000000000); - assertEq(registry.getPublicKey(bob), 0x0000567800000000000000000000000000000000000000000000000000000000); - assertEq(registry.getPublicKey(carol), 0x0000000090ab0000000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(alice), 0x1234000000000000000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(bob), 0x0000567800000000000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(carol), 0x0000000090ab0000000000000000000000000000000000000000000000000000); // David vm.startPrank(david); registry.setPublicKey(0x000000000000cdef000000000000000000000000000000000000000000000000); - assertEq(registry.getPublicKey(alice), 0x1234000000000000000000000000000000000000000000000000000000000000); - assertEq(registry.getPublicKey(bob), 0x0000567800000000000000000000000000000000000000000000000000000000); - assertEq(registry.getPublicKey(carol), 0x0000000090ab0000000000000000000000000000000000000000000000000000); - assertEq(registry.getPublicKey(david), 0x000000000000cdef000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(alice), 0x1234000000000000000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(bob), 0x0000567800000000000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(carol), 0x0000000090ab0000000000000000000000000000000000000000000000000000); + assertEq(registry.publicKeys(david), 0x000000000000cdef000000000000000000000000000000000000000000000000); } function test_ShouldEmitARegistrationEvent() public { @@ -102,4 +102,65 @@ contract EmergencyMultisigTest is AragonTest { vm.expectRevert(abi.encodeWithSelector(AlreadySet.selector)); registry.setPublicKey(0x000000000000cdef000000000000000000000000000000000000000000000000); } + + function test_ShouldCountRegisteredCandidates() public { + assertEq(registry.registeredWalletCount(), 0, "Incorrect count"); + + // Alice + vm.startPrank(alice); + registry.setPublicKey(bytes32(uint256(1234))); + assertEq(registry.registeredWalletCount(), 1, "Incorrect count"); + + // Bob + vm.startPrank(bob); + registry.setPublicKey(bytes32(uint256(2345))); + assertEq(registry.registeredWalletCount(), 2, "Incorrect count"); + + // Carol + vm.startPrank(carol); + registry.setPublicKey(bytes32(uint256(3456))); + assertEq(registry.registeredWalletCount(), 3, "Incorrect count"); + + // David + vm.startPrank(david); + registry.setPublicKey(bytes32(uint256(4567))); + assertEq(registry.registeredWalletCount(), 4, "Incorrect count"); + } + + function test_ShouldEnumerateRegisteredCandidates() public { + // Register + vm.startPrank(alice); + registry.setPublicKey(bytes32(uint256(1234))); + vm.startPrank(bob); + registry.setPublicKey(bytes32(uint256(2345))); + vm.startPrank(carol); + registry.setPublicKey(bytes32(uint256(3456))); + vm.startPrank(david); + registry.setPublicKey(bytes32(uint256(4567))); + + assertEq(registry.registeredWalletCount(), 4, "Incorrect count"); + + assertEq(registry.registeredWallets(0), alice); + assertEq(registry.registeredWallets(1), bob); + assertEq(registry.registeredWallets(2), carol); + assertEq(registry.registeredWallets(3), david); + } + + function test_ShouldLoadTheRegisteredAddresses() public { + vm.startPrank(alice); + registry.setPublicKey(bytes32(uint256(1234))); + vm.startPrank(bob); + registry.setPublicKey(bytes32(uint256(2345))); + vm.startPrank(carol); + registry.setPublicKey(bytes32(uint256(3456))); + vm.startPrank(david); + registry.setPublicKey(bytes32(uint256(4567))); + + address[] memory candidates = registry.getRegisteredWallets(); + assertEq(candidates.length, 4); + assertEq(candidates[0], alice); + assertEq(candidates[1], bob); + assertEq(candidates[2], carol); + assertEq(candidates[3], david); + } } From c3982f33a8a3ac0ff2e485244364bad50f9de648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8r=E2=88=82=C2=A1?= Date: Thu, 6 Jun 2024 16:54:12 +0100 Subject: [PATCH 3/3] Sepolia deployment info --- DEPLOYMENTS.md | 18 ++++++++++++++++++ script/Deploy.s.sol | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/DEPLOYMENTS.md b/DEPLOYMENTS.md index 2960764..a7498aa 100644 --- a/DEPLOYMENTS.md +++ b/DEPLOYMENTS.md @@ -7,5 +7,23 @@ On June 6th 2024: ``` +Chain ID: 11155111 + +Deploying from: 0x424797Ed6d902E17b9180BFcEF452658e148e0Ab +Test voting token: 0xf7A8F99a1d0AFB3C95f80770223b00e062C6Ec19 +Factory contract: 0x8c99CDb5567206660CA9b77C3B26C13F6C674952 + +DAO contract: 0x6C477915CC803518723d4Bdd5B2170cf38A57203 + +- Multisig plugin: 0x0fC611670228A61824c317926f30e8a2615aa1A3 +- Emergency multisig plugin: 0x619d6661eA06b917e26694f23c5Bb32fa0456773 +- Optimistic token voting plugin: 0xC9304930f6a4fB2DAe74A17032426Aa1E817897A + +- Multisig plugin repository: 0x841E3dA30697C8FC7224a43952041001545a2443 +- Emergency multisig plugin repository: 0x6E8578B1519a04BA9262CB633B06624f636D4795 +- Optimistic token voting plugin repository: 0x58CA6f90edB98f9213353f456c685ABF253edAA7 + +Public key registry 0xadAb459A189AAaa17D4807805e6Fab55d3fb5C44 +Delegation wall 0x0cE7f031BA69abFB404fE148dD09F597db8AB3a0 ``` diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 6e120c1..430e700 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -36,8 +36,8 @@ contract Deploy is Script { ); console.log("Chain ID:", block.chainid); - console.log("Deploying from:", vm.addr(vm.envUint("DEPLOYMENT_PRIVATE_KEY"))); console.log(""); + console.log("Deploying from:", vm.addr(vm.envUint("DEPLOYMENT_PRIVATE_KEY"))); TaikoDaoFactory.DeploymentSettings memory settings; if (block.chainid == 1) { @@ -55,6 +55,7 @@ contract Deploy is Script { // Print summary console.log("Factory contract:", address(factory)); + console.log(""); console.log("DAO contract:", address(daoDeployment.dao)); console.log(""); @@ -110,7 +111,6 @@ contract Deploy is Script { address votingToken = createTestToken(multisigMembers, taikoBridgeAddress); console.log("Test voting token:", votingToken); - console.log(""); settings = TaikoDaoFactory.DeploymentSettings({ // Taiko contract settings