Skip to content
Request an audit

‹ All findings

Borrower can prevent yield position repayment and closure by the bot

MediumBendDAO·Code4rena · NFT lending · 19th June, 2024CodebaseM-16

User may revoke the underlying asset approval from the yield contract and prevent the botAdmin from repaying and closing his position. As a result, the debt will not be repaid, leaving the protocol at a loss.

Proof of Concept

Let's take a closer look at the _repay function:

  function _repay(uint32 poolId, address nft, uint256 tokenId) internal virtual {
    RepayLocalVars memory vars;
    ---SNIP---
    // withdraw yield from protocol and repay if possible

    vars.claimedYield = protocolClaimWithdraw(sd);

    vars.nftDebt = _getNftDebtInUnderlyingAsset(sd);
    vars.nftDebtWithFine = vars.nftDebt + sd.unstakeFine;

    // compute repay value
    if (vars.claimedYield >= vars.nftDebtWithFine) {
      vars.remainAmount = vars.claimedYield - vars.nftDebtWithFine;
    } else {
>>    vars.extraAmount = vars.nftDebtWithFine - vars.claimedYield;
    }

    // transfer eth from sender
    if (vars.extraAmount > 0) {
>>    underlyingAsset.safeTransferFrom(vars.nftOwner, address(this), vars.extraAmount);
    }

    if (vars.remainAmount > 0) {
      underlyingAsset.safeTransfer(vars.nftOwner, vars.remainAmount);
    }

    // repay lending pool
    poolYield.yieldRepayERC20(poolId, address(underlyingAsset), vars.nftDebt);

    poolYield.yieldSetERC721TokenData(poolId, nft, tokenId, false, address(underlyingAsset));

    // update shares
    accountYieldInWithdraws[address(vars.yieldAccout)] -= sd.withdrawAmount;
    totalDebtShare -= sd.debtShare;

    delete stakeDatas[nft][tokenId];

    emit Repay(msg.sender, nft, tokenId, vars.nftDebt);
  }

If the yield collected by the position is not enough to cover the entire debt and fines, the protocol will attempt to collect extraAmount from the position owner, but he may revoke ERC20 approval and cancel the safeTransferFrom function call. As a result, nftDebt will not be repaid and will continue to collect interest, increasing the protocol's losses.

Recommended Mitigation Steps

It is recommended to separate the logic of debt repayment and NFT unlocking. The _repay function will reduce the debt of the position using the collected yield and unlock the NFT only if extraAmount is zero. In case extraAmount is greater than zero, leave the token locked and allow the NFT owner to unlock it only after he has repaid the remaining debt.