DeFiHardhatFoundry
250,000 USDC
View results
Submission Details
Severity: high
Valid

`L2ContractMigrationFacet.addMigratedDepositsToAccount()` doesn't update some global balances during the migration.

Github link

https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/9c7b9fd521ad7cbe65cc788df181887c0eb39c6d/protocol/contracts/beanstalk/silo/L2ContractMigrationFacet.sol#L194

Summary

While migrating deposits to L2, addMigratedDepositsToAccount() doesn't increase the global deposited and depositedBdv balances.

Vulnerability Details

Users can redeem their deposits and internal balances on L2 using redeemDepositsAndInternalBalances().

And redeemDepositsAndInternalBalances() calls addMigratedDepositsToAccount() to manage each deposit.

But in addMigratedDepositsToAccount(), it doesn't track totalDeposited and totalDepositedBdv at all.

function addMigratedDepositsToAccount(
address account,
AccountDepositData calldata depositData
) internal returns (uint256 accountStalk) {
int96 stemTip = LibTokenSilo.stemTipForToken(depositData.token);
uint256 stalkIssuedPerBdv = s.sys.silo.assetSettings[depositData.token].stalkIssuedPerBdv;
uint128 totalBdvForAccount;
uint128 totalDeposited;
uint128 totalDepositedBdv;
for (uint256 i; i < depositData.depositIds.length; i++) {
// verify that depositId is valid.
uint256 depositId = depositData.depositIds[i];
(address depositToken, int96 stem) = depositId.unpackAddressAndStem();
require(depositToken == depositData.token, "Migration: INVALID_DEPOSIT_ID");
require(stemTip >= stem, "Migration: INVALID_STEM");
// add deposit to account.
s.accts[account].deposits[depositId].amount = depositData.amounts[i];
s.accts[account].deposits[depositId].bdv = depositData.bdvs[i];
// increment totalBdvForAccount by bdv of deposit:
totalBdvForAccount += depositData.bdvs[i];
// increment by grown stalk of deposit.
accountStalk += uint96(stemTip - stem) * depositData.bdvs[i];
// emit events.
emit AddDeposit(
account,
depositData.token,
stem,
depositData.amounts[i],
depositData.bdvs[i]
);
emit TransferSingle(msg.sender, address(0), account, depositId, depositData.amounts[i]);
}
// update mowStatuses for account and token.
s.accts[account].mowStatuses[depositData.token].bdv = totalBdvForAccount;
s.accts[account].mowStatuses[depositData.token].lastStem = stemTip;
// set global state
s.sys.silo.balances[depositData.token].deposited = totalDeposited; //@audit wrong amount
s.sys.silo.balances[depositData.token].depositedBdv = totalDepositedBdv; //@audit wrong amount
// increment stalkForAccount by the stalk issued per BDV.
// placed outside of loop for gas effiency.
accountStalk += stalkIssuedPerBdv * totalBdvForAccount;
}

So the global deposited and depositedBdv won't be increased after the migration.

These global balances are used in several logics like the token withdrawal validation and gauge points calculations.

As an example, these balances are decreased during a withdrawal and some withdrawals would revert due to underflow as these global balances are less than the total balances of all users.

Impact

Several logics with the withdrawal wouldn't work properly with the wrong global balances.

Tools Used

Manual Review

Recommendations

We should increase the global balances in addMigratedDepositsToAccount().

function addMigratedDepositsToAccount(
address account,
AccountDepositData calldata depositData
) internal returns (uint256 accountStalk) {
int96 stemTip = LibTokenSilo.stemTipForToken(depositData.token);
uint256 stalkIssuedPerBdv = s.sys.silo.assetSettings[depositData.token].stalkIssuedPerBdv;
uint128 totalBdvForAccount;
uint128 totalDeposited;
uint128 totalDepositedBdv;
for (uint256 i; i < depositData.depositIds.length; i++) {
// verify that depositId is valid.
uint256 depositId = depositData.depositIds[i];
(address depositToken, int96 stem) = depositId.unpackAddressAndStem();
require(depositToken == depositData.token, "Migration: INVALID_DEPOSIT_ID");
require(stemTip >= stem, "Migration: INVALID_STEM");
// add deposit to account.
s.accts[account].deposits[depositId].amount = depositData.amounts[i];
s.accts[account].deposits[depositId].bdv = depositData.bdvs[i];
+ totalDeposited += depositData.amounts[i];
+ totalDepositedBdv += depositData.bdvs[i];
// increment totalBdvForAccount by bdv of deposit:
totalBdvForAccount += depositData.bdvs[i];
// increment by grown stalk of deposit.
accountStalk += uint96(stemTip - stem) * depositData.bdvs[i];
// emit events.
emit AddDeposit(
account,
depositData.token,
stem,
depositData.amounts[i],
depositData.bdvs[i]
);
emit TransferSingle(msg.sender, address(0), account, depositId, depositData.amounts[i]);
}
// update mowStatuses for account and token.
s.accts[account].mowStatuses[depositData.token].bdv = totalBdvForAccount;
s.accts[account].mowStatuses[depositData.token].lastStem = stemTip;
// set global state
- s.sys.silo.balances[depositData.token].deposited = totalDeposited;
- s.sys.silo.balances[depositData.token].depositedBdv = totalDepositedBdv;
+ s.sys.silo.balances[depositData.token].deposited = totalDeposited;
+ s.sys.silo.balances[depositData.token].depositedBdv = totalDepositedBdv;
// increment stalkForAccount by the stalk issued per BDV.
// placed outside of loop for gas effiency.
accountStalk += stalkIssuedPerBdv * totalBdvForAccount;
}
Updates

Lead Judging Commences

giovannidisiena Auditor
11 months ago
inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`addMigratedDepositsToAccount` Function doesn't properly aggregate the totalDeposited and totalDepositBdved

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.