Skip to content

Latest commit

 

History

History
65 lines (44 loc) · 2.18 KB

058.md

File metadata and controls

65 lines (44 loc) · 2.18 KB

Rapid Walnut Wasp

Medium

Incorrect use of L1 block.number on Arbitrum

Summary

According to Arbitrum's documentation A single Ethereum block could include multiple Arbitrum blocks within it; however, an Arbitrum block cannot span across multiple Ethereum blocks. Thus, any given Arbitrum transaction is associated with exactly one Ethereum block and one Arbitrum block.

https://github.com/sherlock-audit/2024-12-numa-audit/blob/ae1d7781efb4cb2c3a40c642887ddadeecabb97d/Numa/contracts/lending/CToken.sol#L741

Root Cause

The lending protocol incorrectly relies on block.number for critical timing operations in CToken.sol

function getBlockNumber() internal view virtual returns (uint) {
    return block.number;  // Returns L1 block number on Arbitrum
}

This affects interest rate calculations:

function accrueInterest() public virtual override returns (uint) {
    uint currentBlockNumber = getBlockNumber();
    uint accrualBlockNumberPrior = accrualBlockNumber;
    uint blockDelta = currentBlockNumber - accrualBlockNumberPrior;

Freshness Checks:

function borrowFreshNoTransfer(address payable borrower, uint borrowAmount) internal virtual {
    if (accrualBlockNumber != getBlockNumber()) {
        revert BorrowFreshnessCheck();
    }

Timing Mismatch:

  • L1 block.number updates every ~12 seconds
  • Arbitrum blocks occur every ~0.25 seconds
  • 48 Arbitrum blocks can exist within one L1 block
  • block.number returns L1 block number, not actual L2 block count

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

No response

Impact

Interest calculations will be severely underestimated wich will be ~48x longer than intended, while Multiple transactions within the same L1 block will share the same block number, the Freshness checks will fail for valid transactions.

PoC

No response

Mitigation

No response