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;