Omniscia Pareto Audit

VotesAggregator Manual Review Findings

VotesAggregator Manual Review Findings

VAR-01M: Absence of EIP-5805 Compatibility

Description:

Per the EIP-5805 standard, the nonces(address) function MUST be implemented in RFC-2119 terminology.

Impact:

The VotesAggregator is presently not compatible with the EIP-5805 standard in full due to missing a single function.

Example:

src/governance/VotesAggregator.sol
16/// @title VotesAggregator
17/// @notice Aggregates voting power from PAR (ERC20Votes) and ve8020 via the VeVotesAdapter
18/// Implements the IERC5805 interface for compatibility with OpenZeppelin governors
19/// Voting power from each source is weighted via basis points to allow tuning of influence
20/// @dev Voting power is expressed in timestamps (EIP-6372 timestamp clock)
21contract VotesAggregator is IERC5805, Ownable {

Recommendation:

We advise it to be implemented, ensuring the codebase is compatible with EIP-5805 in full per its specification.

Alleviation:

The Pareto team clarified that they do not intend to have full EIP-5805 compliancy but rather be partially compliant with it, rendering this exhibit to be safely acknowledged.

VAR-02M: Insecure Default Value of Total Supply Power

Description:

The VotesAggregator contains a custom mechanism for querying individual and total voting powers that will ignore any revert error and default to the 0 value.

While underestimating the voting power of a user due to an error is acceptable, underestimating the total voting power of the system when the individual voting power of a user may have succeeded is inherently dangerous as the user's voting power proportion would be overestimated.

Impact:

In case the historical total supply lookup of a token fails whilst its individual history lookup succeeds, the user's voting power would be significantly overestimated proportionally.

Example:

src/governance/VotesAggregator.sol
121/// @notice Return historical voting power for an account
122/// @param token The voting token to query
123/// @param account Address to query voting power for
124/// @param timepoint Timestamp to evaluate
125/// @return value Historical voting units
126function _pastVotes(IVotes token, address account, uint256 timepoint) internal view returns (uint256 value) {
127 try token.getPastVotes(account, timepoint) returns (uint256 out) {
128 value = out;
129 } catch {}
130}
131
132/// @notice Return historical total voting supply
133/// @param token The voting token to query
134/// @param timepoint Timestamp to evaluate
135/// @return value Historical total voting units
136function _pastTotalSupply(IVotes token, uint256 timepoint) internal view returns (uint256 value) {
137 try token.getPastTotalSupply(timepoint) returns (uint256 out) {
138 value = out;
139 } catch {}
140}

Recommendation:

We advise the code to not ignore errors in total voting power calculations, or to ensure that both individual and total voting powers are ignored.

Alleviation:

The try-catch clause has been removed entirely from all statements, ensuring they fail properly if the underlying token does not properly respond to IVotes function calls.

VAR-03M: Retroactive Update of Voting Powers

Description:

The VotesAggregator::updateWeights function is insecurely implemented as it will retroactively affect all voting power measurements in the system based on the updated weights.

This results in any process that relied on the pre-update weights becoming corrupted, f.e. a governance process that snapshots the total voting power meant for a proposal.

Impact:

The current weight update mechanism is insecure as it corrupts all on-chain processes that relied on previous voting power measurements of the VotesAggregator.

Example:

src/governance/VotesAggregator.sol
142/// @notice Update the weighting applied to PAR and ve votes.
143/// @param newParWeightBps The new weight applied to PAR votes, expressed in basis points.
144/// @param newVeWeightBps The new weight applied to ve votes, expressed in basis points.
145function updateWeights(uint256 newParWeightBps, uint256 newVeWeightBps) external onlyOwner {
146 if (newParWeightBps + newVeWeightBps == 0) revert VotesAggregatorWeightsZero();
147 parWeightBps = newParWeightBps;
148 veWeightBps = newVeWeightBps;
149 emit WeightsUpdated(newParWeightBps, newVeWeightBps);
150}

Recommendation:

We advise these BPS values to remain immutable, ensuring the system behaves as expected.

Alternatively, we advise an update to gradually roll out and to solely apply for new power measurements, preventing an update from affecting previously established voting powers.

Alleviation:

The code was significantly revised to utilize a time-based weight BPS configuration system that allows historical changes to be properly fetched, ensuring weight updates do not retroactively apply.

As such, we consider this exhibit fully alleviated.