Vanguard

First Flight #56
Beginner FriendlyDeFiFoundry
100 EXP
View results
Submission Details
Impact: medium
Likelihood: high
Invalid

Use of block.number causes inconsistent phase durations across different EVM chains

Root + Impact

Description

The project scope explicitly states support for "Any EVM-compatible chain." However, the contract relies on block.number to calculate the duration of protection phases.Block production times vary significantly across EVM networks:

  • Ethereum: ~12 seconds.

  • BSC/Polygon: ~2-3 seconds.

  • Optimism/Base: ~2 seconds.

  • Arbitrum: Returns L2 block number (different mechanism).



    Therefore, this results in different phase cycles on different chains.

// Root cause in the codebase with @> marks to highlight the relevant section
function _beforeSwap(address sender, PoolKey calldata key, SwapParams calldata params, bytes calldata)
internal
override
returns (bytes4, BeforeSwapDelta, uint24)
{
if (launchStartBlock == 0) revert PoolNotInitialized();
if (initialLiquidity == 0) {
uint128 liquidity = StateLibrary.getLiquidity(poolManager, key.toId());
initialLiquidity = uint256(liquidity);
}
@> uint256 blocksSinceLaunch = block.number - launchStartBlock;
uint256 newPhase;
@> if (blocksSinceLaunch <= phase1Duration) {
newPhase = 1;
@> } else if (blocksSinceLaunch <= phase1Duration + phase2Duration) {
newPhase = 2;
} else {
newPhase = 3;
}
if (newPhase != currentPhase) {
_resetPerAddressTracking();
currentPhase = newPhase;
lastPhaseUpdateBlock = block.number;
}
//......
}

Risk

Likelihood:

The README explicitly requires support for multiple chains, and since each chain has a different block production interval, it easily causes inconsistencies in the cycle across chains.

Impact:

On Ethereum mainnet, if the first phase takes 10 minutes, on BSC/Polygon it might be under 2 minutes, in which case the protective effect becomes ineffective.

Proof of Concept

Use Foundry to simulate a fast-chain environment (e.g., Base with 2s block time) and compare the elapsed duration. Relying on block.number causes the protection phase to expire significantly earlier in wall-clock time than intended, effectively bypassing the protection mechanism

function test_Poc_blocknumberError() public {
uint256 startTimestamp = block.timestamp;
// then simulate Base/Optimism (2s per block)
// Ether 12S
vm.roll(block.number + phase1Duration + 1);
vm.warp(block.timestamp + (phase1Duration + 1) * 2);
assertEq(antiBotHook.getCurrentPhase(), 2);
assertLt(block.timestamp - startTimestamp, phase1Duration * 12);
}

Recommended Mitigation

Replace block.number with block.timestamp for all duration measurements. block.timestamp provides a consistent wall-clock time standard across all EVM-compatible chains.

- block.number
+ block.timestamp
Updates

Lead Judging Commences

chaossr Lead Judge
17 days ago
chaossr Lead Judge
17 days ago
chaossr Lead Judge
17 days ago
chaossr Lead Judge
17 days ago

Appeal created

chaossr Lead Judge 17 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!