Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Depositing assets doesn't contain a slippage check #434

Open
c4-bot-8 opened this issue Mar 15, 2024 · 5 comments
Open

Depositing assets doesn't contain a slippage check #434

c4-bot-8 opened this issue Mar 15, 2024 · 5 comments
Labels
bug Something isn't working downgraded by judge Judge downgraded the risk level of this issue duplicate-281 grade-a Q-07 QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax 🤖_143_group AI based duplicate group recommendation sufficient quality report This report is of sufficient quality

Comments

@c4-bot-8
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol?plain=1#L877-L917
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol?plain=1#L1281-L1295

Vulnerability details

Impact

When a user calls V3Vault.mint() or V3Vault.deposit(), the user is unable to provide slippage arguments to ensure that they receive at least X amount of lending shares. If their transaction is delayed before it's executed or is frontrun, the user may receive less lending shares than expected.

Proof of Concept

Below is the functionality for _deposit() which calculates how many lending shares to mint and contains no slippage checks:

function _deposit(address receiver, uint256 amount, bool isShare, bytes memory permitData)
    internal
    returns (uint256 assets, uint256 shares)
{
    (, uint256 newLendExchangeRateX96) = _updateGlobalInterest();

    _resetDailyLendIncreaseLimit(newLendExchangeRateX96, false);

    if (isShare) {
        shares = amount;
        assets = _convertToAssets(shares, newLendExchangeRateX96, Math.Rounding.Up);
    } else {
        assets = amount;
        shares = _convertToShares(assets, newLendExchangeRateX96, Math.Rounding.Down);
    }

    if (permitData.length > 0) {
        (ISignatureTransfer.PermitTransferFrom memory permit, bytes memory signature) =
            abi.decode(permitData, (ISignatureTransfer.PermitTransferFrom, bytes));
        permit2.permitTransferFrom(
            permit, ISignatureTransfer.SignatureTransferDetails(address(this), assets), msg.sender, signature
        );
    } else {
        // fails if not enough token approved
        SafeERC20.safeTransferFrom(IERC20(asset), msg.sender, address(this), assets);
    }

    _mint(receiver, shares);

    if (totalSupply() > globalLendLimit) {
        revert GlobalLendLimit();
    }

    if (assets > dailyLendIncreaseLimitLeft) {
        revert DailyLendIncreaseLimit();
    } else {
        dailyLendIncreaseLimitLeft -= assets;
    }

    emit Deposit(msg.sender, receiver, assets, shares);
}

To determine the amount of shares to mint for the user in _deposit(), the protocol utilizes the newLendExchangeRateX96 to calculate how many shares to mint:

if (isShare) {
    shares = amount;
    assets = _convertToAssets(shares, newLendExchangeRateX96, Math.Rounding.Up);
} else {
    assets = amount;
    shares = _convertToShares(assets, newLendExchangeRateX96, Math.Rounding.Down);
}

function _convertToShares(uint256 amount, uint256 exchangeRateX96, Math.Rounding rounding)
    internal
    pure
    returns (uint256)
{
    return amount.mulDiv(Q96, exchangeRateX96, rounding);
}

function _convertToAssets(uint256 shares, uint256 exchangeRateX96, Math.Rounding rounding)
    internal
    pure
    returns (uint256)
{
    return shares.mulDiv(exchangeRateX96, Q96, rounding);
}

Since the newLendExchangeRateX96 will update over time (see _calculateGlobalInterest() code snippet below), the amount of shares minted will decrease over time. This leads to a user experiencing slippage.

// always growing or equal
uint256 lastRateUpdate = lastExchangeRateUpdate;

if (lastRateUpdate > 0) {
    newDebtExchangeRateX96 = oldDebtExchangeRateX96
        + oldDebtExchangeRateX96 * (block.timestamp - lastRateUpdate) * borrowRateX96 / Q96;
    // lend exchange rate changes over time
    newLendExchangeRateX96 = oldLendExchangeRateX96
        + oldLendExchangeRateX96 * (block.timestamp - lastRateUpdate) * supplyRateX96 / Q96;
} else {
    newDebtExchangeRateX96 = oldDebtExchangeRateX96;
    newLendExchangeRateX96 = oldLendExchangeRateX96;
}

Tools Used

Manual inspection.

Recommended Mitigation Steps

Allow the user to provide minimum slippage checks when calling mint() (slippage check against assets required to create X amount of shares) or deposit() (slippage checks against shares minted). This will ensure that the user receives at least X amount of lending shares when calling mint() or deposit().

Assessed type

Timing

@c4-bot-8 c4-bot-8 added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Mar 15, 2024
c4-bot-1 added a commit that referenced this issue Mar 15, 2024
@c4-bot-11 c4-bot-11 added the 🤖_143_group AI based duplicate group recommendation label Mar 15, 2024
@c4-pre-sort c4-pre-sort added the sufficient quality report This report is of sufficient quality label Mar 18, 2024
@c4-pre-sort
Copy link

0xEVom marked the issue as sufficient quality report

@c4-pre-sort
Copy link

0xEVom marked the issue as duplicate of #281

@c4-judge c4-judge added downgraded by judge Judge downgraded the risk level of this issue QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax and removed 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value labels Mar 31, 2024
@c4-judge
Copy link

jhsagd76 changed the severity to QA (Quality Assurance)

@c4-judge
Copy link

c4-judge commented Apr 3, 2024

This previously downgraded issue has been upgraded by jhsagd76

@c4-judge c4-judge reopened this Apr 3, 2024
@c4-judge c4-judge added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value and removed downgraded by judge Judge downgraded the risk level of this issue QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax labels Apr 3, 2024
@c4-judge
Copy link

c4-judge commented Apr 3, 2024

jhsagd76 changed the severity to QA (Quality Assurance)

@c4-judge c4-judge added downgraded by judge Judge downgraded the risk level of this issue QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax and removed 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value labels Apr 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working downgraded by judge Judge downgraded the risk level of this issue duplicate-281 grade-a Q-07 QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax 🤖_143_group AI based duplicate group recommendation sufficient quality report This report is of sufficient quality
Projects
None yet
Development

No branches or pull requests

5 participants