-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ZeroTrust - The withdrawValue calculation in _calculateValueOfWithdrawRequest is incorrect. #78
Comments
Escalate This should be considered a High issue. Although _calculateValueOfWithdrawRequest is just a view function, it is used by BaseStakingVault::convertStrategyToUnderlying to evaluate the value of a user’s shares in the staking Vault. function convertStrategyToUnderlying(
address account,
uint256 vaultShares,
uint256 /* maturity */
) public virtual override view returns (int256 underlyingValue) {
uint256 stakeAssetPrice = uint256(getExchangeRate(0));
WithdrawRequest memory w = getWithdrawRequest(account);
@> uint256 withdrawValue = _calculateValueOfWithdrawRequest(
w, stakeAssetPrice, BORROW_TOKEN, REDEMPTION_TOKEN
);
// This should always be zero if there is a withdraw request.
uint256 vaultSharesNotInWithdrawQueue = (vaultShares - w.vaultShares);
uint256 vaultSharesValue = (vaultSharesNotInWithdrawQueue * stakeAssetPrice * BORROW_PRECISION) /
(uint256(Constants.INTERNAL_TOKEN_PRECISION) * Constants.EXCHANGE_RATE_PRECISION);
@> return (withdrawValue + vaultSharesValue).toInt();
} In Notional V3, the function calculateAccountHealthFactors calculates a user’s health condition by calling getPrimaryUnderlyingValueOfShare, which in turn calls the vault's convertStrategyToUnderlying. /// @notice Calculates account health factors for liquidation.
function calculateAccountHealthFactors(
VaultConfig memory vaultConfig,
VaultAccount memory vaultAccount,
VaultState memory vaultState,
PrimeRate[2] memory primeRates
) internal view returns (
VaultAccountHealthFactors memory h,
VaultSecondaryBorrow.SecondaryExchangeRates memory er
) {
@>> h.vaultShareValueUnderlying = getPrimaryUnderlyingValueOfShare(
vaultState, vaultConfig, vaultAccount.account, vaultAccount.vaultShares
);
//-------skip-----
} /// @notice Returns the value in underlying of the primary borrow currency portion of vault shares.
function getPrimaryUnderlyingValueOfShare(
VaultState memory vaultState,
VaultConfig memory vaultConfig,
address account,
uint256 vaultShares
) internal view returns (int256) {
if (vaultShares == 0) return 0;
Token memory token = TokenHandler.getUnderlyingToken(vaultConfig.borrowCurrencyId);
return token.convertToInternal(
@>> IStrategyVault(vaultConfig.vault).convertStrategyToUnderlying(account, vaultShares, vaultState.maturity)
);
} Whether a user’s position should be liquidated is determined based on calculateAccountHealthFactors. Therefore, as stated in the scope of impact in my report, “Overestimation of user assets can lead to scenarios where users should have been liquidated but were not, resulting in losses for the protocol.” Also considering the widespread nature of the error, in this audit, the calculation in PendlePTEtherFiVault, PendlePTKelpVault, PendlePTStakedUSDeVault, and EthenaVault is incorrect. The impact of this error is High, and the likelihood of occurrence is between medium and high, according to Sherlock’s rules on how to identify a high issue: |
You've created a valid escalation! To remove the escalation from consideration: Delete your comment. You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final. |
Additional information: This issue has the same impact as issue 60 |
Yes I think this is correct. Definitely should be a high severity issue |
I still think medium rating will be more appropriate but will let the Sherlock judge decide on it: In case any of these incorrect values returned by the view functions are used as a part of a larger function which would result in loss of funds then it would be a valid medium/high depending on the impact. |
@0502lian I see that the functions mentioned in the escalation, which in the end call the |
1 About the Call ChainNotional is a very complex protocol. function deleverageAccount(
address account,
address vault,
address liquidator,
uint16 currencyIndex,
int256 depositUnderlyingInternal
) external payable nonReentrant override returns (
uint256 vaultSharesToLiquidator,
int256 depositAmountPrimeCash
) {
//......skip..........
// Currency Index is validated in this method
(
depositUnderlyingInternal,
vaultSharesToLiquidator,
pr
@>> ) = IVaultAccountHealth(address(this)).calculateDepositAmountInDeleverage(
currencyIndex, vaultAccount, vaultConfig, vaultState, depositUnderlyingInternal
);
//......skip..........
} You can see that the liquidation function deleverageAccount calls calculateDepositAmountInDeleverage(). function calculateDepositAmountInDeleverage(
uint256 currencyIndex,
VaultAccount memory vaultAccount,
VaultConfig memory vaultConfig,
VaultState memory vaultState,
int256 depositUnderlyingInternal
) external override returns (int256, uint256, PrimeRate memory) {
// This method is only intended for use during deleverage account
//............skip...........
@>> (h, er) = VaultValuation.calculateAccountHealthFactors(vaultConfig, vaultAccount, vaultState, primeRates);
//............skip...........
calculateDepositAmountInDeleverage() calls calculateAccountHealthFactors(), which I listed in my last comment. Finally, it calls the problematic function. 2 About the SponsorThe sponsor has supported this issue as high priority in the above comments. 3 About Severity ComparisonAs I have already expressed above, issue 60 also affects the _getValueOfSplitFinalizedWithdrawRequest view function(called by _calculateValueOfWithdrawRequest()), but its severity is rated as High. The evidence is so obvious. I understand that the Sherlock judge wants to get to the truth, but I don’t understand why the first judge ignored this evidence and still considered it to be of medium severity. |
Based on the comment above, I agree high severity is indeed appropriate here. Planning to accept the escalation and upgrade the severity.
The problem here is that all the functions mentioned in the report and messages above you just say where the formula is incorrect. From judge's point of view it would be easier if you would make vulnerability path which function would be called and how this sequence of calls would lead to the impact. Just as an advice try to write your reports in a different way so it's easier to judge it correctly initially. Also, try new report templates, I think with them it will be a lot easier for both you and judges :) |
Result: |
Escalations have been resolved successfully! Escalation status:
|
The protocol team fixed this issue in the following PRs/commits: |
The Lead Senior Watson signed off on the fix. |
* fix: adding post mint and redeem hooks * test: changes to base tests * config: changes to config * feat: changes to global * feat: changes to trading * feat: changes to utils * feat: changes to single sided lp * feat: vault storage * fix: misc fixes * fix: staking vaults * fix: solidity versions * fix: test build * fix: adding staking harness * fix: adding initialization * fix: initial test bugs * fix: weETH valuation * fix: deleverage collateral check * fix: initial harness compiling * fix: initial test running * fix: acceptance tests passing * test: migrated some tests * fix: withdraw tests * test: adding deleverage test * fix: adding liquidation tests * test: withdraw request * test: finalize withdraws manual * test: tests passing * fix: single sided lp tests with vault rewarder * fix: putting rewarder tests in * fix: reward tests running * fix: vault rewarder address * fix: initial staking harness * fix: adding staking harness * fix: initial PT vault build * fix: moving ethena vault code * fix: moving etherfi code * feat: adding pendle implementations * fix: staking harness to use USDC * fix: curve v2 adapter for trading * test: basic tests passing * fix: adding secondary trading on withdraw * fix tests * fix: trading on redemption * fix: ethena vault config * fix: switch ethena vault to sell sDAI * fix warnings * fix: more liquidation tests passing * fix: ethan liquidation tests * pendle harness build * fix: initial tests passing * fix: adding pendle oracle * fix: test deal token error * fix: changing pendle liquidation discount * fix: all tests passing * fix: etherfi borrow currency * fix: adding more documentation * change mainnet fork block * properly update data seed files * fix arbitrum tests * fix test SingleSidedLP:Convex:crvUSD/[USDT] * fix: can finalize withdraws * fix: refactor withdraw valuation * fix: pendle expiration tests * fix: pendle pt valuation * remove flag * fix: remove redundant code path * fix: initial commit * fix: vault changes * fix: vault changes * fix: some tests passing * fix: fixing more tests * fix: updated remaining tests * fix: split withdraw bug * fix: new test * fix: remaining tests * fix: split withdraw reqest bug * feat: add PendlePTKelp vault * update oracle address, fix tests * Address CR comments * add test_canTriggerExtraStep * fix tests * fix: run tests * feat: adding generic vault * feat: update generate tests * fix: changes from merge * fix: adding has withdraw requests * fix: update oracle address for network * fix: merge kelp harness * fix: base tests passing * fix: move generation config * fix: initial pendle test generation * fix: mainnet tests passing * fix: vault rewarder * fix: more pendle tests * fix: pendle dex test * fix: adding camelot dex * fix: update usde pt * fix: adding camelot adapter * fix: support configurable dex * fix: adding more PT vaults * fix: approval bug * fix: update dex information * fix: mainnet tests passing * fix: update arbitrum pendle tests * fix: update deployment addresses * test: add balancer v2 batch trade * fix: add given out batch trade * fix: remove trade amount filling * fix: add some comments * fix: audit issue #60 * fix: switch to using getDecimals * fix: sherlock-audit/2024-06-leveraged-vaults-judging#73 * fix: sherlock-audit/2024-06-leveraged-vaults-judging#72 * fix: sherlock-audit/2024-06-leveraged-vaults-judging#70 * fix: sherlock-audit/2024-06-leveraged-vaults-judging#66 * test: adding pendle oracle test * fix: sherlock-audit/2024-06-leveraged-vaults-judging#69 * fix: sherlock-audit/2024-06-leveraged-vaults-judging#64 * fix: sherlock-audit/2024-06-leveraged-vaults-judging#43 * fix: audit issue #18 * fix: move slippage check * fix: add comment back * fix: sherlock-audit/2024-06-leveraged-vaults-judging#56 * test: adding test that catches math underflow * fix: adding test for vault shares * fix: sherlock-audit/2024-06-leveraged-vaults-judging#44 * fix: sherlock-audit/2024-06-leveraged-vaults-judging#6 * test: adds test to check split withdraw request value * fix: sherlock-audit/2024-06-leveraged-vaults-judging#78 * fix: sherlock-audit/2024-06-leveraged-vaults-judging#80 * fix: updating valuations for tests * fix: update run tests * fix: remove stETH withdraws from Kelp in favor of ETH withdraws * fix: update tests for pendle rs eth * fix: resolve compile issues * fix: rsETH oracle price * fix: sherlock-audit/2024-06-leveraged-vaults-judging#87 * fix: sherlock-audit/2024-06-leveraged-vaults-judging#67 * fix: sherlock-audit/2024-06-leveraged-vaults-judging#6 * test: update tests for invalid splits * fix: sherlock fix review comments * merge: merged master into branch * fix: empty reward tokens * fix: claim rewards tests * fix: liquidation tests * fixing more tests * fix: allowing unused reward pools * test: migrating reward pools * fix: rewarder test * fix: claim rewards before withdrawing * fix: deployed vault rewarder lib on arbitrum * fix: deployed new tbtc vault * docs: adding deployment documentation * fix: update config --------- Co-authored-by: sbuljac <[email protected]>
ZeroTrust
Medium
The withdrawValue calculation in _calculateValueOfWithdrawRequest is incorrect.
The withdrawValue calculation in _calculateValueOfWithdrawRequest is incorrect.
Summary
The withdrawValue calculation in _calculateValueOfWithdrawRequest is incorrect.
Vulnerability Detail
We can see that for cases without hasSplit or with hasSplit but not finalized, the value is calculated using _getValueOfWithdrawRequest. Let’s take a look at
PendlePTEtherFiVault::_getValueOfWithdrawRequest()
.Here, tokenOutSY represents the total amount of WETH requested for withdrawal, not just the portion represented by the possibly split w.vaultShares. Therefore, _getValueOfWithdrawRequest returns the entire withdrawal value. If a WithdrawRequest has been split but not finalized, this results in an incorrect calculation.
This issue also occurs in
PendlePTKelpVault::_getValueOfWithdrawRequest()
.This issue also occurs in
PendlePTStakedUSDeVault::_getValueOfWithdrawRequest()
.The calculation is correct only in
EtherFiVault::_getValueOfWithdrawRequest()
.Impact
Overestimation of user assets can lead to scenarios where users should have been liquidated but were not, resulting in losses for the protocol.
Code Snippet
https://github.com/sherlock-audit/2024-06-leveraged-vaults/blob/14d3eaf0445c251c52c86ce88a84a3f5b9dfad94/leveraged-vaults-private/contracts/vaults/common/WithdrawRequestBase.sol#L86
https://github.com/sherlock-audit/2024-06-leveraged-vaults/blob/14d3eaf0445c251c52c86ce88a84a3f5b9dfad94/leveraged-vaults-private/contracts/vaults/staking/PendlePTEtherFiVault.sol#L46
https://github.com/sherlock-audit/2024-06-leveraged-vaults/blob/main/leveraged-vaults-private/contracts/vaults/staking/protocols/Kelp.sol#L114
https://github.com/sherlock-audit/2024-06-leveraged-vaults/blob/main/leveraged-vaults-private/contracts/vaults/staking/protocols/Ethena.sol#L77
Tool used
Manual Review
Recommendation
Modify the relevant calculation formulas.
The text was updated successfully, but these errors were encountered: