Skip to content

Latest commit

 

History

History
112 lines (85 loc) · 4.73 KB

File metadata and controls

112 lines (85 loc) · 4.73 KB

Brave Saffron Rooster

High

Price of pTKN decreases all the time and prior bond user will debond for more cheaper price.

Summary

The price of pTKN decreases all the time and previous bond users may debond at a lower price.

Root Cause

Within WeightedIndex.sol, the _bond function wraps index tokens to pTKN token.

https://github.com/sherlock-audit/2025-01-peapods-finance/blob/main/contracts/contracts/WeightedIndex.sol#L139-L171

     function _bond(address _token, uint256 _amount, uint256 _amountMintMin, address _user) internal {
        require(_isTokenInIndex[_token], "IT");
        uint256 _tokenIdx = _fundTokenIdx[_token];

        bool _firstIn = _isFirstIn();
        uint256 _tokenAmtSupplyRatioX96 =
            _firstIn ? FixedPoint96.Q96 : (_amount * FixedPoint96.Q96) / _totalAssets[_token];
        uint256 _tokensMinted;
        if (_firstIn) {
@>            _tokensMinted = (_amount * FixedPoint96.Q96 * 10 ** decimals()) / indexTokens[_tokenIdx].q1;
        } else {
@>            _tokensMinted = (_totalSupply * _tokenAmtSupplyRatioX96) / FixedPoint96.Q96;
        }
        uint256 _feeTokens = _canWrapFeeFree(_user) ? 0 : (_tokensMinted * _fees.bond) / DEN;
        require(_tokensMinted - _feeTokens >= _amountMintMin, "M");
@>  _totalSupply += _tokensMinted;
        _mint(_user, _tokensMinted - _feeTokens);
        if (_feeTokens > 0) {
            _mint(address(this), _feeTokens);
@>      _processBurnFee(_feeTokens);
        }
       [...]
    }

When multiple users bond _token with amounts of x1, x2, x3, etc.,we can analyze _totalSupply and _totalAssets[_token] as follows.

bondorder  _totalAssets[_token]       _tokensMinted                                  _totalSupply  
0(firstbond)            x1                   y1                                          z1(=y1)
1                     x1+x2             y2=z1/x1*x2                     z2=z1+z1/x1*x2*(1-_fees.burn / DEN)
2                  x1+x2+x3             y3=z2/x2*x3                     z3=z2+z2/x2*x3*(1-_fees.burn / DEN)                       
3               x1+x2+x3+x4             y4=z3/x3*x4                     z4=z3+z3/x3*x4*(1-_fees.burn / DEN)    
4             x1+x2+x3+x4+x5            y5=z4/x4*x5                     z5=z4+z4/x4*x5*(1-_fees.burn / DEN)    
5          x1+x2+x3+x4+x5+x6            y6=z5/x5*x6                     z6=z5+z5/x5*x6*(1-_fees.burn / DEN)
...

From this, we can derive that:

  `z2/(x1+x2)=(z1+z1/x1*x2*(1-_fees.burn / DEN))/(x1+x2)<z1/x1`

  `z3/(x1+x2+x3)<z2/(x1+x2)<z1/x1`

...

Thus, the ratio _totalSupply/_totalAssets[_token] decreases with each bond. In practice, as users also debond tokens and the rate _totalSupply/_totalAssets[_token] also decreases witheach debond.

https://github.com/sherlock-audit/2025-01-peapods-finance/blob/main/contracts/contracts/WeightedIndex.sol#L175-L196

function debond(uint256 _amount, address[] memory, uint8[] memory) external override lock noSwapOrFee {
        uint256 _amountAfterFee = _isLastOut(_amount) || REWARDS_WHITELIST.isWhitelistedFromDebondFee(_msgSender())
            ? _amount
            : (_amount * (DEN - _fees.debond)) / DEN;
@>  uint256 _percSharesX96 = (_amountAfterFee * FixedPoint96.Q96) / _totalSupply;
        super._transfer(_msgSender(), address(this), _amount);
@>  _totalSupply -= _amountAfterFee;
        _burn(address(this), _amountAfterFee);
@>  _processBurnFee(_amount - _amountAfterFee);
        uint256 _il = indexTokens.length;
        for (uint256 _i; _i < _il; _i++) {
@>      uint256 _debondAmount = (_totalAssets[indexTokens[_i].token] * _percSharesX96) / FixedPoint96.Q96;
            if (_debondAmount > 0) {
@>          _totalAssets[indexTokens[_i].token] -= _debondAmount;
                IERC20(indexTokens[_i].token).safeTransfer(_msgSender(), _debondAmount);
            }
        }
        [...]
    }

If the _totalSupply is low, the rate _totalSupply/_totalAssets[_token] decreases more rapidly. This results in a continuous decline, potentially approaching zero. Mathematically, it is evident that the first bonder can debond pTKN at a cheaper price since the ratio is significantly lower than at the initial bonding stage, allowing them to increase their tokens.

Internal Pre-conditions

No response

External Pre-conditions

No response

Attack Path

No response

Impact

Users can increase their tokens by taking advantage of the decreasing rate _totalSupply/_totalAssets[_token].

PoC

It is evident that continuous bonding (wrapping) and debonding (unwrapping) of tokens will decrease the rate _totalSupply/_totalAssets[_token].

Mitigation

Please introduce a parameter to control the rate _totalSupply/_totalAssets[_token] to prevent it from reaching excessively low values.