the core issue is that proposals requiring ETH transfers may fail because the system isn’t designed to easily receive or forward ETH. The Governance contract’s execute function is non-payable, so it can’t pass along any ETH when it triggers proposal execution. At the same time, the TimelockController—responsible for executing scheduled operations—has a payable executeBatch function but lacks a receive or fallback function to accept ETH deposits. This means that if a proposal needs to send ETH, there’s no built-in way to ensure that the timelock contract is properly funded, leading to potential execution failures.
The Governance contract defines its execute function like this:
Because this function is not marked as payable, it cannot forward any ETH that might be sent along with the transaction when calling the internal _executeProposal function which in turn calls the timelock's executeBatch function. This is acceptable if no ETH is needed, but becomes an issue if a proposal requires ETH transfers as we will see soon.
This is the timelock's executeBatch payable function that is called from the Governance contract:
This function is payable, meaning it can forward ETH to the target calls. However, the TimelockController itself has no function to receive ETH directly (no receive or fallback function). This means there is no built-in way to fund the timelock with ETH outside of calling its payable functions.
How it Fits Together:
If a proposal is meant to transfer ETH, the expectation is that the timelock contract should have enough ETH in its balance to cover those transfers.
However, because the Governance execute function is non-payable, it cannot contribute ETH when executing the proposal.
And since the timelock cannot be directly funded through plain ETH transfers (due to the lack of a deposit mechanism), proposals that require ETH may fail if the timelock contract is not already pre-funded by some external process.
Proposals that require ETH transfers will not execute successfully if the timelock lacks sufficient ETH. Hence, this flaw could halt or delay important governance actions, as proposals expecting ETH transfers would simply fail.
Manual code review
To mitigate this issue, I recommend making the Governance contract’s execute function payable so it can forward ETH when needed, and adding a receive or fallback function to the TimelockController so it can be directly funded with ETH.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.