Platform: Code4rena
Start Date: 07/07/2023
Pot Size: $121,650 USDC
Total HM: 36
Participants: 111
Period: 7 days
Judge: Picodes
Total Solo HM: 13
Id: 258
League: ETH
Rank: 12/111
Findings: 1
Award: $1,896.90
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: rvierdiiev
Also found by: Brenzee
1896.9014 USDC - $1,896.90
https://github.com/GenerationSoftware/pt-v5-vault/blob/main/src/Vault.sol#L1176
The vault exchange rate is directly dependent on the _yieldVault.maxWithdraw()
value.
The issue occurs if _yieldVault
withdrawals are paused. Per ERC-4626 standard rules - maxWithdraw
"MUST factor in both global and user-specific limits, like if withdrawals are entirely disabled (even temporarily) it MUST return 0." Link to ERC-4626 documentation
This means that if _yieldVault
withdrawals are paused, maxWithdraw
value will be 0 and it is going deflate/inflate the exchange rate of the Vault, which may cause loss of funds for protocol or for user.
Vault._getExchangeRate
function calls _yieldVault.maxWithdraw
function. Value from it is used in the logic that happens next
uint256 _withdrawableAssets = _yieldVault.maxWithdraw(address(this)); // @audit - Can return 0 if (_withdrawableAssets > _totalSupplyToAssets) { // @audit - Will never be true if _withdrawableAssets is 0, go to next step _withdrawableAssets = _withdrawableAssets - (_withdrawableAssets - _totalSupplyToAssets); } if (_totalSupplyAmount != 0 && _withdrawableAssets != 0) { // @audit - Will never be true if _withdrawableAssets is 0, go to next step return _withdrawableAssets.mulDiv(_assetUnit, _totalSupplyAmount, Math.Rounding.Down); } return _assetUnit; // @audit - Return _assetUnit as exchange rate
Users interact with the Vault
- they deposit and withdraw tokens, which means that the exchange rate has shifted from _assetUnit
(which is the exchange rate when nothing is deposited in the _yieldVault
)
All of a sudden _yieldVault
owner has paused the withdrawals, which makes the maxWithdraw
function return 0.
Now instead of having the exchange rate based on deposited/maxWithdraw values, the exchange rate will be 1, because the maxWithdraw
function returns 0.
As a result - the exchange rate is either inflated or deflated, which may cause:
Manual Review
Consider that maxWithdraw
can return 0 even after the assets are deposited in the _yieldVault
Invalid Validation
#0 - c4-sponsor
2023-07-18T23:26:15Z
asselstine marked the issue as sponsor confirmed
#1 - c4-judge
2023-08-08T13:21:19Z
Picodes marked the issue as duplicate of #79
#2 - Picodes
2023-08-08T13:22:12Z
I consider this as another instance of #79, where users can lose funds because of maxWithdraw
of the underlying not returning the value of the shares but what can actually be redeemed.
#3 - c4-judge
2023-08-08T13:22:19Z
Picodes marked the issue as satisfactory
#4 - c4-judge
2023-08-08T14:39:51Z
Picodes changed the severity to 3 (High Risk)
#5 - PierrickGT
2023-08-14T22:30:32Z
Fixed in the following PR: https://github.com/GenerationSoftware/pt-v5-vault/pull/18/commits/32b1b6127726d27935119868cf347a8cbb74cc73#diff-97c974f5c3c03a0cfcbc52a5b8b9ae2196d535754ff2034e2903de1fec23508aR1233
And I've added a comment to explain when maxWithdraw
could return 0
for the YieldVault.