Summary
The idle capital will be deposited back into the transmuter, when claimAndSwap
is called.
Vulnerability Details
Idle capital occurs because the vault should process withdrawals without unstaking from the yield-bearing protocol. When claimAndSwap
is called, underlying tokens are claimed from the transmuter, swapped at a premium via DEX for alAsset
, and then should be deposited back into the transmuter. However, the deposit function is called with the entire alAsset
balance, not just the amount received after the swap.
StrastegyOp.sol:
function claimAndSwap(uint256 _amountClaim, uint256 _minOut, IVeloRouter.route[] calldata _path ) external onlyKeepers {
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
_swapUnderlyingToAsset(_amountClaim, _minOut, _path);
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Slippage too high");
@> transmuter.deposit(asset.balanceOf(address(this)), address(this));
}
StrategyArb.sol:
function claimAndSwap(uint256 _amountClaim, uint256 _minOut, IRamsesRouter.route[] calldata _path) external onlyKeepers {
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
_swapUnderlyingToAsset(_amountClaim, _minOut, _path);
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Slippage too high");
@> transmuter.deposit(asset.balanceOf(address(this)), address(this));
}
StrategyMainnet.sol:
function claimAndSwap(
uint256 _amountClaim,
uint256 _minOut,
uint256 _routeNumber
) external onlyKeepers {
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
require(_minOut > _amountClaim, "minOut too low");
router.exchange(
routes[_routeNumber],
swapParams[_routeNumber],
_amountClaim,
_minOut,
pools[_routeNumber],
address(this)
);
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Slippage too high");
@> transmuter.deposit(asset.balanceOf(address(this)), address(this));
}
Impact
The idle balance will be deposited back into the transmuter, instead of only the swapped amount (as expected from the function).
Tools Used
Manual review
Recommendations
Re-deposit only the swapped amount.