@@ -410,4 +410,96 @@ contract SuccinctGovernorTest is SuccinctStakingTest {
410410 assertTrue (requests[0 ].resolved);
411411 assertTrue (requests[1 ].resolved);
412412 }
413+
414+ // Test governance proposal lifecycle using time-based delays and periods.
415+ function test_Propose_WithTimeBasedGovernance () public {
416+ // The parameter to be updated.
417+ uint256 newDispenseRate = DISPENSE_RATE * 2 ;
418+
419+ // Staker stakes to Alice's prover.
420+ _stake (STAKER_1, ALICE_PROVER, STAKER_PROVE_AMOUNT);
421+
422+ // It takes a block for voting power to update.
423+ vm.roll (block .number + 1 );
424+
425+ // Alice makes a proposal through her prover contract.
426+ address [] memory targets = new address [](1 );
427+ targets[0 ] = STAKING;
428+ uint256 [] memory values = new uint256 [](1 );
429+ values[0 ] = 0 ;
430+ bytes [] memory calldatas = new bytes [](1 );
431+ calldatas[0 ] =
432+ abi.encodeWithSelector (SuccinctStaking.updateDispenseRate.selector , newDispenseRate);
433+ string memory description =
434+ string .concat ("Time-based: Update dispense rate to " , Strings.toString (newDispenseRate));
435+ bytes32 descriptionHash = keccak256 (bytes (description));
436+
437+ // Record the initial timestamp.
438+ uint256 proposalTimestamp = block .timestamp ;
439+
440+ // Alice proposes through her prover contract.
441+ vm.prank (ALICE);
442+ uint256 proposalId =
443+ SuccinctProver (ALICE_PROVER).propose (targets, values, calldatas, description);
444+
445+ // Proposal should be in Pending state.
446+ IGovernor.ProposalState state = SuccinctGovernor (payable (GOVERNOR)).state (proposalId);
447+ assertEq (uint8 (state), uint8 (IGovernor.ProposalState.Pending));
448+
449+ // Get the voting delay in seconds.
450+ uint256 votingDelay = SuccinctGovernor (payable (GOVERNOR)).votingDelay ();
451+
452+ // Advance time just before voting delay expires.
453+ vm.warp (proposalTimestamp + votingDelay - 1 );
454+
455+ // Proposal should still be in Pending state.
456+ state = SuccinctGovernor (payable (GOVERNOR)).state (proposalId);
457+ assertEq (uint8 (state), uint8 (IGovernor.ProposalState.Pending));
458+
459+ // Advance time to exactly when voting starts.
460+ vm.warp (proposalTimestamp + votingDelay + 1 );
461+
462+ // Proposal should now be in Active state.
463+ state = SuccinctGovernor (payable (GOVERNOR)).state (proposalId);
464+ assertEq (uint8 (state), uint8 (IGovernor.ProposalState.Active));
465+
466+ // Alice votes FOR the proposal.
467+ vm.prank (ALICE);
468+ SuccinctProver (ALICE_PROVER).castVote (proposalId, 1 );
469+
470+ // Check the votes were recorded.
471+ (uint256 againstVotes , uint256 forVotes , uint256 abstainVotes ) =
472+ SuccinctGovernor (payable (GOVERNOR)).proposalVotes (proposalId);
473+ assertEq (forVotes, IERC20 (I_PROVE).balanceOf (ALICE_PROVER));
474+ assertEq (againstVotes, 0 );
475+ assertEq (abstainVotes, 0 );
476+
477+ // Get the voting period in seconds.
478+ uint256 votingPeriod = SuccinctGovernor (payable (GOVERNOR)).votingPeriod ();
479+
480+ // Advance time just before voting period expires.
481+ vm.warp (proposalTimestamp + votingDelay + votingPeriod - 1 );
482+
483+ // Proposal should still be in Active state.
484+ state = SuccinctGovernor (payable (GOVERNOR)).state (proposalId);
485+ assertEq (uint8 (state), uint8 (IGovernor.ProposalState.Active));
486+
487+ // Advance time to after voting period expires.
488+ vm.warp (proposalTimestamp + votingDelay + votingPeriod + 1 );
489+
490+ // Proposal should be in Succeeded state.
491+ state = SuccinctGovernor (payable (GOVERNOR)).state (proposalId);
492+ assertEq (uint8 (state), uint8 (IGovernor.ProposalState.Succeeded));
493+
494+ // Execute the proposal.
495+ vm.prank (STAKER_1);
496+ SuccinctGovernor (payable (GOVERNOR)).execute (targets, values, calldatas, descriptionHash);
497+
498+ // Proposal should be in Executed state.
499+ state = SuccinctGovernor (payable (GOVERNOR)).state (proposalId);
500+ assertEq (uint8 (state), uint8 (IGovernor.ProposalState.Executed));
501+
502+ // Verify the dispense rate was updated.
503+ assertEq (SuccinctStaking (STAKING).dispenseRate (), newDispenseRate);
504+ }
413505}
0 commit comments