Skip to content

Latest commit

 

History

History
83 lines (55 loc) · 2.92 KB

File metadata and controls

83 lines (55 loc) · 2.92 KB

Fast Khaki Raccoon

High

VotingPool will mint the same amount of voting shares for 2 different valued pods

Summary

VotingPool will mint the same amount of voting shares even if one pod is 10x the price of the other

Root Cause

_updateUserState relying on conversion factor to decide the pod value. However the conversion factor always starts at 1 and it's the share value increase, not it's intrinsic asset value.

https://github.com/sherlock-audit/2025-01-peapods-finance/blob/main/contracts/contracts/voting/VotingPool.sol#L62-L85

    function _updateUserState(address _user, address _asset, uint256 _addAmt)
        internal
        returns (uint256 _convFctr, uint256 _convDenom)
    {
        require(assets[_asset].enabled, "E");

        // _totalAssets[indexTokens[0].token] * 1e18 / _totalSupply && 1e18
        (_convFctr, _convDenom) = _getConversionFactorAndDenom(_asset);

This is further shown in _calculateCbrWithDen which gets that share value increase:

https://github.com/sherlock-audit/2025-01-peapods-finance/blob/main/contracts/contracts/voting/ConversionFactorPTKN.sol#L19-L23

    function _calculateCbrWithDen(address _pod) internal view returns (uint256, uint256) {
        require(IDecentralizedIndex(_pod).unlocked() == 1, "OU");
        uint256 _den = 10 ** 18;
        
        // _totalAssets[indexTokens[0].token] * 1e18 / _totalSupply
        return (IDecentralizedIndex(_pod).convertToAssets(_den), _den);
    }

Internal Pre-conditions

No response

External Pre-conditions

No response

Attack Path

  1. Alice mints voting 10 voting shares from a L2 pod - ARB, OP, Base (cheap tokens)
  2. Bob mints 10 voting shares from ETH staking pod - cbETH, stETH, rETH
  3. Both have 10 voting shares, but Bob's shares costed 3000 times more

Any user can use this to find the cheapest pod ever and mint a ton of voting shares to use them in governance, or just farm the rewards, as they distribute per share (without taking into account the pod these shares come from):

    function _update(address _from, address _to, uint256 _value) internal override {
        super._update(_from, _to, _value);
        require(_from == address(0) || _to == address(0), "NT");

        if (_from != address(0)) {
            TokenRewards(REWARDS).setShares(_from, _value, true);
        }

        if (_to != address(0) && _to != address(0xdead)) {
            TokenRewards(REWARDS).setShares(_to, _value, false);
        }
    }

Impact

Voting shares do not have the same value, meaning malicious users can find the cheapest pods to mint a ton of voting shares.

This would also generate more rewards for users as these pod shares are used in TokenRewards.

PoC

No response

Mitigation

Consider using oracles to get the price of each pod share and mint a voting shares based on that, instead of relying on the pod share value percentage increases to measure pod share value.