58754 sc high missing mytsharesdeposited decrements in alchemistv3 forcerepay doliquidation
Previous57152 sc high assets permanently locked due to killswitch flagNext57812 sc medium no function to claim aave incentives
Was this helpful?
Was this helpful?
_mytSharesDeposited -= creditToYield; // Adjust for repaid MYT// _forceRepay()
if (creditToYield > 0) {
// Transfer the repaid tokens from the account to the transmuter.
TokenUtils.safeTransfer(myt, address(transmuter), creditToYield);
// ✅ Adjust internal accounting to reflect decreased MYT holdings
_mytSharesDeposited -= creditToYield;
}
// _doLiquidation()
if (liquidatedAmount > 0) {
TokenUtils.safeTransfer(myt, address(transmuter), liquidatedAmount);
// ✅ Decrement MYT shares deposited
_mytSharesDeposited -= liquidatedAmount;
} function testAudit_MytSharesDepositedNotDecrementedOnLiquidation() external {
vm.startPrank(someWhale);
IMockYieldToken(mockStrategyYieldToken).mint(whaleSupply, someWhale);
vm.stopPrank();
// Setup: Create healthy account to maintain system collateralization
vm.startPrank(yetAnotherExternalUser);
SafeERC20.safeApprove(
address(vault),
address(alchemist),
depositAmount * 2
);
alchemist.deposit(depositAmount, yetAnotherExternalUser, 0);
vm.stopPrank();
// Setup: Create undercollateralized position
vm.startPrank(address(0xbeef));
SafeERC20.safeApprove(
address(vault),
address(alchemist),
depositAmount + 100e18
);
alchemist.deposit(depositAmount, address(0xbeef), 0);
uint256 tokenId = AlchemistNFTHelper.getFirstTokenId(
address(0xbeef),
address(alchemistNFT)
);
alchemist.mint(
tokenId,
(alchemist.totalValue(tokenId) * FIXED_POINT_SCALAR) /
minimumCollateralization,
address(0xbeef)
);
vm.stopPrank();
// Drop yield token price to make position undercollateralized
uint256 initialVaultSupply = IERC20(address(mockStrategyYieldToken))
.totalSupply();
IMockYieldToken(mockStrategyYieldToken).updateMockTokenSupply(
initialVaultSupply
);
uint256 modifiedVaultSupply = ((initialVaultSupply * 590) / 10_000) +
initialVaultSupply;
IMockYieldToken(mockStrategyYieldToken).updateMockTokenSupply(
modifiedVaultSupply
);
// Record state before liquidation
uint256 beforeBalance = IERC20(address(vault)).balanceOf(
address(alchemist)
);
uint256 beforeTVL = alchemist.getTotalUnderlyingValue();
// Perform liquidation (transfers MYT out but doesn't decrement _mytSharesDeposited)
vm.startPrank(externalUser);
alchemist.liquidate(tokenId);
vm.stopPrank();
// Record state after liquidation
uint256 afterBalance = IERC20(address(vault)).balanceOf(
address(alchemist)
);
uint256 afterTVL = alchemist.getTotalUnderlyingValue();
// Calculate how much MYT was actually transferred out
uint256 mytTransferredOut = beforeBalance - afterBalance;
uint256 expectedTVLDecrease = alchemist.convertYieldTokensToUnderlying(
mytTransferredOut
);
uint256 actualTVLDecrease = beforeTVL - afterTVL;
console.log("MYT balance before liquidation:", beforeBalance);
console.log("MYT balance after liquidation:", afterBalance);
console.log("MYT transferred out:", mytTransferredOut);
console.log(
"Expected TVL decrease (based on MYT transferred):",
expectedTVLDecrease
);
console.log("Actual TVL decrease:", actualTVLDecrease);
console.log("Mismatch (bug):", expectedTVLDecrease - actualTVLDecrease);
// BUG PROOF: TVL doesn't decrease by the amount of MYT transferred out
// because _mytSharesDeposited wasn't decremented
assertLt(
actualTVLDecrease,
expectedTVLDecrease,
"BUG: TVL decrease is less than expected - _mytSharesDeposited not decremented during liquidation"
);
}
// EXPECTED OUTPUT:
// MYT balance before liquidation: 400000000000000000000000
// MYT balance after liquidation: 290985999999999998125641
// MYT transferred out: 109014000000000001874359
// Expected TVL decrease (based on MYT transferred): 102940509915014165977264
// Actual TVL decrease: 0
// Mismatch (bug): 102940509915014165977264
// Suite result: ok. 1 passed; 0 failed; 0 skipped;