Skip to content

Commit 2092fb1

Browse files
committed
gas optimization and adding a method to remove the expired proposals
1 parent f43ddaf commit 2092fb1

File tree

1 file changed

+42
-17
lines changed

1 file changed

+42
-17
lines changed

contracts/governance/GovernanceModule.sol

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ abstract contract GovernanceModule is
7676
mapping(address => ProposalTypes.TimeConfig) public timeConfigs;
7777
mapping(bytes32 => ProposalTypes.RoleConfig) public roleConfigs;
7878
mapping(address => bytes32) public upgradeProposals;
79+
mapping(bytes32 => uint256) public proposalIndex;
7980

8081
/// @notice Proposal tracking
8182
uint256 public proposalCount;
@@ -179,7 +180,7 @@ abstract contract GovernanceModule is
179180
}
180181

181182
/// @notice Get pending owner address
182-
function pendingOwner() public view virtual returns (address) {
183+
function pendingOwner() external view virtual returns (address) {
183184
return _pendingOwnerRequest;
184185
}
185186

@@ -196,7 +197,7 @@ abstract contract GovernanceModule is
196197
}
197198

198199
/// @notice checks if an account has already approved a specific proposal
199-
function hasProposalApproval(bytes32 proposalId, address account) public view returns (bool) {
200+
function hasProposalApproval(bytes32 proposalId, address account) external view returns (bool) {
200201
return proposals[proposalId].hasApproved[account];
201202
}
202203

@@ -272,6 +273,7 @@ abstract contract GovernanceModule is
272273

273274
// Register proposal
274275
proposalRegistry[proposalCount] = proposalId;
276+
proposalIndex[proposalId] = proposalCount;
275277
proposalCount += 1;
276278
pendingProposals[target].proposalType = uint8(proposalType);
277279

@@ -362,7 +364,6 @@ abstract contract GovernanceModule is
362364
// Delete entire record
363365
delete pendingProposals[proposal.target];
364366
delete proposals[proposalId];
365-
if (proposalCount > 0) proposalCount -= 1;
366367
_removeFromRegistry(proposalId);
367368

368369
emit ProposalExpired(proposalId, proposal.proposalType, proposal.target);
@@ -371,18 +372,46 @@ abstract contract GovernanceModule is
371372
return false; // Indicate that the proposal has not expired
372373
}
373374

374-
function _removeFromRegistry(bytes32 proposalId) internal {
375-
uint256 i;
376-
while(i < proposalCount && gasleft() > GAS_BUFFER) {
377-
if (proposalRegistry[i] == proposalId) {
378-
if (i != --proposalCount) {
379-
proposalRegistry[i] = proposalRegistry[proposalCount];
380-
}
381-
delete proposalRegistry[proposalCount];
382-
break;
375+
/// @notice Cleans up expired proposals in batches to avoid gas limits
376+
/// @param maxProposalsToCheck Maximum number of proposals to check in this batch
377+
function cleanupExpiredProposals(uint256 maxProposalsToCheck)
378+
external
379+
whenNotPaused
380+
nonReentrant
381+
onlyRole(ProposalTypes.ADMIN_ROLE)
382+
{
383+
uint256 checked = 0;
384+
uint256 index = proposalCount;
385+
386+
// Process from the end of the registry to minimize storage changes
387+
while (checked < maxProposalsToCheck && index > 0) {
388+
unchecked { index--; } // Gas optimization: unchecked as index > 0
389+
checked++;
390+
391+
bytes32 proposalId = proposalRegistry[index];
392+
if (proposals[proposalId].target == address(0)) {
393+
continue; // Skip already cleaned proposals
383394
}
384-
unchecked { ++i; }
395+
396+
// Check expiration and automatically clean if needed
397+
_checkExpiredProposal(proposalId);
385398
}
399+
_updateActivityTimestamp();
400+
}
401+
402+
function _removeFromRegistry(bytes32 proposalId) internal {
403+
uint256 index = proposalIndex[proposalId];
404+
if (index >= proposalCount) return;
405+
406+
// Swap with the last element
407+
bytes32 lastProposalId = proposalRegistry[proposalCount - 1];
408+
proposalRegistry[index] = lastProposalId;
409+
proposalIndex[lastProposalId] = index;
410+
411+
// Delete the last element
412+
delete proposalRegistry[proposalCount - 1];
413+
delete proposalIndex[proposalId];
414+
proposalCount -= 1;
386415
}
387416

388417
// Virtual function for contract-specific proposals
@@ -483,7 +512,6 @@ abstract contract GovernanceModule is
483512
// Mark proposal as executed
484513
proposal.config.status = 1;
485514
delete proposals[proposalId];
486-
if (proposalCount > 0) proposalCount -= 1;
487515
_removeFromRegistry(proposalId);
488516
} else if (proposal.proposalType == uint8(ProposalTypes.ProposalType.Upgrade)) {
489517
// Nothing
@@ -500,7 +528,6 @@ abstract contract GovernanceModule is
500528
// Add cleanup
501529
delete pendingProposals[proposal.target];
502530
delete proposals[proposalId];
503-
if (proposalCount > 0) proposalCount -= 1;
504531
_removeFromRegistry(proposalId);
505532
emit ProposalExecuted(proposalId, proposal.proposalType, proposal.target);
506533
} else {
@@ -512,7 +539,6 @@ abstract contract GovernanceModule is
512539
// Mark proposal as executed
513540
proposal.config.status = 1;
514541
delete proposals[proposalId];
515-
if (proposalCount > 0) proposalCount -= 1;
516542
_removeFromRegistry(proposalId);
517543
}
518544
}
@@ -663,7 +689,6 @@ abstract contract GovernanceModule is
663689
// Delete pending proposals if all flags are cleared
664690
delete pendingProposals[target];
665691
delete proposals[currentId];
666-
if (proposalCount > 0) proposalCount -= 1;
667692
_removeFromRegistry(currentId);
668693
_updateActivityTimestamp();
669694
emit ProposalExecuted(currentId, currentProposal.proposalType, target);

0 commit comments

Comments
 (0)