Skip to content
Request an audit

‹ All findings

Withdrawing ionic during liquidation has a flaw

MediumAutonomint·Sherlock · Hedged stablecoin · 4th December 2024CodebaseM-5

Summary

During liquidation only record yield accrued from IONIC, however those yields are not utilized/there is no way to withdraw them.

Root Cause

The case where we update treasury.updateInterestFromExternalProtocol we use treasury.withdrawFromExternalProtocolDuringLiq(user, index), which essentially gives us only the extra yield accrued:

solidity
    function withdrawFromExternalProtocolDuringLiq(address user, uint64 index) external onlyCoreContracts returns (uint256)
    {
        uint256 balanceBeforeWithdraw = address(this).balance;
        uint256 redeemAmount = withdrawFromIonicDuringLiq(user, index);
        if (address(this).balance < redeemAmount + balanceBeforeWithdraw) {
            revert Treasury_WithdrawExternalProtocolDuringLiqFailed();
        }
@>>     return (redeemAmount - (borrowing[user].depositDetails[index].depositedAmount * 50) / 100);
    } 

Reference during liquidation:

solidity
       // Update treasury data
        treasury.updateTotalVolumeOfBorrowersAmountinWei(depositDetail.depositedAmountInETH);
        treasury.updateTotalVolumeOfBorrowersAmountinUSD(depositDetail.depositedAmountUsdValue);
        treasury.updateDepositedCollateralAmountInWei(depositDetail.assetName, depositDetail.depositedAmountInETH);
        treasury.updateDepositedCollateralAmountInUsd(depositDetail.assetName, depositDetail.depositedAmountUsdValue);
@>>     treasury.updateTotalInterestFromLiquidation(totalInterestFromLiquidation);
        treasury.updateYieldsFromLiquidatedLrts(yields);
        treasury.updateDepositDetails(user, index, depositDetail);

        globalVariables.updateCollateralData(depositDetail.assetName, collateralData);
        globalVariables.setOmniChainData(omniChainData);

        // If the liquidation amount to get from other is greater than zero,
        // Get the amount from other chain.
        if (liqAmountToGetFromOtherChain > 0) {
            // Call the oftOrCollateralReceiveFromOtherChains function in global variables
            globalVariables.oftOrCollateralReceiveFromOtherChains{value: msg.value}(
                IGlobalVariables.FunctionToDo(3),
                IGlobalVariables.USDaOftTransferData(address(treasury), liqAmountToGetFromOtherChain),
                // Since we don't need ETH, we have passed zero params
                IGlobalVariables.CollateralTokenTransferData(address(0), 0, 0, 0),
                IGlobalVariables.CallingFunction.BORROW_LIQ,
                admin
            );
        }

        // If the collateral is ETH, withdraw the deposited ETH in external protocol
        if (depositDetail.assetName == IBorrowing.AssetName.ETH) {
@>>         treasury.updateInterestFromExternalProtocol(treasury.withdrawFromExternalProtocolDuringLiq(user, index));
        }

As it can be seen above updateTotalInterestFromLiquidation is also updated, but we have a way to withdraw it in Treasury.sol:

solidity
    function withdrawInterest(
        address toAddress,
        uint256 amount
    ) external onlyOwner {
        require(toAddress != address(0) && amount != 0,"Input address or amount is invalid");
        require(amount <= (totalInterest + totalInterestFromLiquidation),"Treasury don't have enough interest");
        totalInterest -= amount;
        bool sent = usda.transfer(toAddress, amount);
        require(sent, "Failed to send Ether");
    }

As mentioned unfortunately we don't handle the yield from IONIC in any way, as the interestFromExternalProtocolDuringLiquidation property in Treasury.sol is unused:

solidity
  function updateInterestFromExternalProtocol(uint256 amount) external onlyCoreContracts {
        interestFromExternalProtocolDuringLiquidation += amount;
    }

Impact

  • No way to withdraw or use the yield generated from IONIC, its just left unused.

Mitigation

Either integrate a way to use the yield from IONIC, or a way to be withdrawn.