Skip to content

Latest commit

 

History

History
96 lines (79 loc) · 3.49 KB

017.md

File metadata and controls

96 lines (79 loc) · 3.49 KB

Perfect Opal Boar

Medium

Insufficient Validation on First Deposit Leading to Silent Failures

Summary

The _deposit function in LendingPool contains a critical logic flaw in handling first-time deposits. The function performs validation before deducting the MINIMUM_YTOKEN_AMOUNT but fails to revalidate after the deduction, leading to silent failures and locked user funds.

Snippet

https://github.com/sherlock-audit/2025-02-yieldoor/blob/main/yieldoor/src/LendingPool.sol#L76 https://github.com/sherlock-audit/2025-02-yieldoor/blob/main/yieldoor/src/LendingPool.sol#L104

Description

The issue stems from three key issues in the deposit logic.

require(yTokenAmount > MINIMUM_YTOKEN_AMOUNT, "deposit is dust amount");

The check is performed before the MINIMUM_YTOKEN_AMOUNT deduction for first deposits.

if (IyToken(reserve.yTokenAddress).totalSupply() == 0) {
    IyToken(reserve.yTokenAddress).mint(DEAD_ADDRESS, MINIMUM_YTOKEN_AMOUNT);
    yTokenAmount -= MINIMUM_YTOKEN_AMOUNT;
}

The deduction occurs without subsequent validation.

uint256 exchangeRate = reserve.reserveToYTokenExchangeRate();
yTokenAmount = amount * exchangeRate / (PRECISION);

Exchange rate fluctuations could affect the final yTokenAmount.

This may cause, if the deposit is too small after the MINIMUM_YTOKEN_AMOUNT reduction, the transaction will fail without a clear error message.

Scenario

  1. Initial State: • MINIMUM_YTOKEN_AMOUNT = 1000 • Exchange rate = 1:1 (PRECISION)
  2. User Attempts First Deposit:
depositAmount = 1500 tokens
yTokenAmount = 1500 * PRECISION / PRECISION = 1500
  1. Sequence of Events: • Initial check passes: 1500 > 1000 (MINIMUM_YTOKEN_AMOUNT) • System identifies first deposit (totalSupply = 0) • Mints 1000 tokens to DEAD_ADDRESS • Deducts 1000 from yTokenAmount • Final yTokenAmount = 500 (below minimum) • Transaction fails silently or produces unusable tokens
  2. Result: • User's transaction fails or results in unusable amounts

Impact

DOS on first deposits within certain ranges

Recommendation

Implement proper validation flow.

function _deposit(address asset, uint256 amount, address onBehalfOf) internal returns (uint256 yTokenAmount) {
    DataTypes.ReserveData storage reserve = getReserve(asset);
    
    // Calculate initial amounts
    uint256 exchangeRate = reserve.reserveToYTokenExchangeRate();
    yTokenAmount = amount * exchangeRate / PRECISION;
    
    // Check if first deposit
    bool isFirstDeposit = IyToken(reserve.yTokenAddress).totalSupply() == 0;
    
    // Calculate minimum required amount
    uint256 requiredAmount = isFirstDeposit 
        ? MINIMUM_YTOKEN_AMOUNT * 2 
        : MINIMUM_YTOKEN_AMOUNT;
    
    // Validate initial amount
    require(yTokenAmount >= requiredAmount, 
        "Deposit too small. Required minimum: {requiredAmount}");
    
    // Process first deposit logic
    if (isFirstDeposit) {
        IyToken(reserve.yTokenAddress).mint(DEAD_ADDRESS, MINIMUM_YTOKEN_AMOUNT);
        yTokenAmount -= MINIMUM_YTOKEN_AMOUNT;
        
        // Revalidate after deduction
        require(yTokenAmount >= MINIMUM_YTOKEN_AMOUNT, 
            "Remaining amount after initial mint too small");
    }
    
    // Complete deposit
    IERC20(asset).safeTransferFrom(msg.sender, reserve.yTokenAddress, amount);
    IyToken(reserve.yTokenAddress).mint(onBehalfOf, yTokenAmount);
    
    emit Deposit(asset, msg.sender, onBehalfOf, amount, yTokenAmount);
    return yTokenAmount;
}