Weather Witness

First Flight #40
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

Potential Front-Running in `WeatherNft::requestMintWeatherNFT`

Description

In the WeatherNft contract, the WeatherNft::requestMintWeatherNFT function performs two key operations in this order:

Price Increment: increases s_currentMintPrice by s_stepIncreasePerMint

NFT Minting: after validating that the sent value covers the new price, issues the NFT

Although this design is atomic, it exposes a front-running window: an attacker can observe the victim’s pending transaction in the mempool, inject their own with higher gas price to execute first, capture the mint at the “old” price, and force the victim to pay the now-increased price.

Vulnerability Details

Legitimate user (victim) calls WeatherNft::requestMintWeatherNFT with {value: P}, expecting to pay price P and receive an NFT

Their transaction remains pending in the mempool.

Attacker monitors the mempool and spots the victim’s tx with value = P.

Submits their own WeatherNft::requestMintWeatherNFT {value: P} with a higher gas price, guaranteeing their call is mined first.

Impact

  • Financial

Attacker acquires mint at the “cheap” price and may resell or manipulate downstream pricing.

Victim incurs unexpected extra cost or an aborted transaction.

  • Trust & Reputation

Users lose confidence when mints consistently fail or cost more.

Negative perception of protocol integrity.

  • Availability

Under high demand, repeated exploits can escalate mint costs, deterring legitimate users.

Proof-of-Concept

  • Victim Submits transaction.

Victim calls:
Tx_Victim: WeatherNft::requestMintWeatherNFT {value: P, gasPrice: 50 gwei}
It remains pending in the mempool.

  • Attacker Detects It.

Uses a mempool watcher to spot Tx_Victim.

  • Attacker Front-Runs

Sends a transaction with higher gas price.
Tx_Attacker: WeatherNft::requestMintWeatherNFT {value: P, gasPrice: 100 gwei}
Miners include it before Tx_Victim.

  • Attacker’s transaction Executes

s_currentMintPrice goes from P to P + s_stepIncreasePerMint.
The contract still reads the old price P for the payment check and mints the NFT to the attacker.

  • State after execution:

s_currentMintPrice = P + s_stepIncreasePerMint
Attacker owns tokenId N.

  • Victim’s transaction fails.

When Tx_Victim executes, it sees s_currentMintPrice = P + s_stepIncreasePerMint but msg.value = P, so it reverts with “Insufficient payment”.

  • Consequences

Victim must resend a transaction paying P + Δ to mint their NFT.

Recommended Mitigation

Consider implementing one of the following front-running mitigation strategies:

  1. Commit–Reveal via Commit Hash
    Introduce a two-phase process where users first submit a hashed commitment of their mint intent (including a secret salt and block reference).Only after the commitment is recorded do they reveal the secret and mint at the price snapshot. This ensures the intended mint parameters remain hidden in the mempool until it’s too late to front-run.

  2. Off-Chain Vouchers with Chainlink Functions
    Use a Chainlink Functions job to calculate and sign each user’s mint details (user address, price, a one-time nonce) off-chain. The signed voucher is submitted on-chain when minting; since the price and nonce are only revealed in that final transaction, observers cannot frontrun based on mempool data.

Updates

Appeal created

bube Lead Judge 23 days ago
Submission Judgement Published
Validated
Assigned finding tags:

The price of the token is increased before the token is minted

Support

FAQs

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