Summary
The protocol after after the L2 migration it doesn't support External -> Internal ERC20 Tokens transfer,
after migration Tokens must not transferred from External -> Internal, this option must be disabled,
protocol added this line of code to prevent External -> INTERNAL:
function transferToken(IERC20 token, address recipient, uint256 amount, LibTransfer.From fromMode, LibTransfer.To toMode) external payable {
checkBeanAsset(address(token));
@>>
@>> if (fromMode != LibTransfer.From.INTERNAL) {
@>> require(
@>> toMode == LibTransfer.To.EXTERNAL,
@>> "TokenFacet: EXTERNAL->INTERNAL transfers are disabled."
@>> );
}
LibTransfer.transferToken(token, msg.sender, recipient, amount, fromMode, toMode);
}
But when it comes to WETH EXTERNAL -> INTERNAL transfer can be made in L1TokenFacet.sol::wrapEth
if LibTransfer.To is INTERNAL the WETH will be transferred from EXTERNAL to INTERNAL,
in this line: if (mode == To.INTERNAL) LibBalance.increaseInternalBalance(recipient, token, amount);
Link: https:
function wrapEth(uint256 amount, LibTransfer.To mode) external payable {
LibWeth.wrap(amount, mode);
LibEth.refundEth();
}
Link: https:
function wrap(uint256 amount, LibTransfer.To mode) internal {
deposit(amount);
LibTransfer.sendToken(IERC20(WETH), amount, msg.sender, mode);
}
Link: https:
function sendToken(IERC20 token, uint256 amount, address recipient, To mode) internal {
if (amount == 0) return;
if (mode == To.INTERNAL) LibBalance.increaseInternalBalance(recipient, token, amount);
else token.safeTransfer(recipient, amount);
}
Vulnerability Details
Impact
calculations that depend on INTERNAL balance can be tricked because it assumes after L2 migration Internal balance cannot be increased.
Tools Used
Recommendations
https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/main/protocol/contracts/beanstalk/migration/L1TokenFacet.sol#L194-L197
funct
ion wrapEth(uint256 amount, LibTransfer.To mode) external payable {
require(
mode == LibTransfer.To.EXTERNAL,
"TokenFacet: EXTERNAL->INTERNAL transfers are disabled."
);
LibWeth.wrap(amount, mode);
LibEth.refundEth();
}