Governance: reject duplicate actions before proposal creation - #6590
Governance: reject duplicate actions before proposal creation#6590timeless-hayoka wants to merge 4 commits into
Conversation
🦋 Changeset detectedLatest commit: 24eea91 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Walkthrough
Possibly related issues
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
contracts/governance/extensions/GovernorTimelockCompound.sol (1)
71-82: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winHoist/precompute calldata hashes to avoid redundant keccak256 in the inner loop.
keccak256(calldatas[i])is recomputed on everyjiteration. Precomputing each calldata hash once reduces hashing from O(n²) to O(n) while preserving the cheap length short-circuit.♻️ Proposed optimization
) internal pure returns (bool) { - for (uint256 i = 0; i < targets.length; ++i) { - for (uint256 j = i + 1; j < targets.length; ++j) { + bytes32[] memory calldataHashes = new bytes32[](calldatas.length); + for (uint256 i = 0; i < calldatas.length; ++i) { + calldataHashes[i] = keccak256(calldatas[i]); + } + for (uint256 i = 0; i < targets.length; ++i) { + for (uint256 j = i + 1; j < targets.length; ++j) { if ( targets[i] == targets[j] && values[i] == values[j] && calldatas[i].length == calldatas[j].length && - keccak256(calldatas[i]) == keccak256(calldatas[j]) + calldataHashes[i] == calldataHashes[j] ) { return true; } } }Note: since calldata-hash equality already implies equal length, the
calldatas[i].length == calldatas[j].lengthcheck becomes redundant once hashes are compared, and could be dropped.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@contracts/governance/extensions/GovernorTimelockCompound.sol` around lines 71 - 82, In GovernorTimelockCompound’s duplicate-check loop, `keccak256(calldatas[i])` is being recomputed for every inner-loop comparison, causing unnecessary O(n²) hashing. Precompute and cache each calldata hash once in the surrounding logic, then compare the cached hashes inside the nested loops; you can keep the cheap equality checks on `targets` and `values`, and drop the `calldatas[i].length == calldatas[j].length` check because hash equality already covers it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@contracts/governance/extensions/GovernorTimelockCompound.sol`:
- Around line 71-82: In GovernorTimelockCompound’s duplicate-check loop,
`keccak256(calldatas[i])` is being recomputed for every inner-loop comparison,
causing unnecessary O(n²) hashing. Precompute and cache each calldata hash once
in the surrounding logic, then compare the cached hashes inside the nested
loops; you can keep the cheap equality checks on `targets` and `values`, and
drop the `calldatas[i].length == calldatas[j].length` check because hash
equality already covers it.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 6048d330-f5e6-4606-a4f2-1dccb6eb7103
📒 Files selected for processing (4)
.changeset/green-teams-taste.mdcontracts/governance/extensions/GovernorTimelockCompound.solcontracts/mocks/governance/GovernorTimelockCompoundMock.soltest/governance/extensions/GovernorTimelockCompound.test.js
|
happy to help |
Summary
Reject duplicate actions in
GovernorTimelockCompoundat proposal creation time instead of letting them fail later during queueing. This keeps the failure closer to the source and avoids creating proposals that are guaranteed to fail queue-time validation.Details
GovernorTimelockCompoundGovernorAlreadyQueuedProposalfor the revert pathpropose()Verification
npm exec -- hardhat test test/governance/extensions/GovernorTimelockCompound.test.jsforge fmt --check contracts/governance/extensions/GovernorTimelockCompound.sol test/governance/extensions/GovernorTimelockCompound.test.js