# Provider could bypass the Bid Decay Mechanism and avoid slashing

Provider resets dispatchTimestamp on an unopened commitment past decayEndTimeStamp, zeroing residual bid percent so slashing costs nothing and bid decay is bypassed.

- Payout: Contest reward
- Severity: high
- Program: Primev
- Credited: sashik-eth
- Disclosed: 2025-08-27
- Write-up quality: 7/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_primev_oct2024.pdf#3.1.5
- Library page: https://bugbountylibrary.com/r/cantina%3Aprimev_oct2024%3A3.1.5
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:cantina sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:business-logic vuln:fee-bypass

**Target:** PreconfManager / ProviderRegistry smart contracts (storeUnopenedCommitment, openCommitment)
**Root cause:** Unopened commitment index excludes dispatchTimestamp and storeUnopenedCommitment does not reject overwriting an existing index, so the same digest/signature can be re-stored with a new dispatchTimestamp; no check that dispatchTimestamp <= decayEndTimeStamp.
**Steps:**
1. Provider obtains bidder's commitmentDigest and commitmentSignature.
2. Call `storeUnopenedCommitment(commitmentDigest, commitmentSignature, decayStartTimeStamp)` to lock in full reward/slash exposure.
3. If a better bid appears, call `storeUnopenedCommitment(commitmentDigest, commitmentSignature, ts > decayEndTimeStamp)` — same index, overwrites dispatchTimestamp.
4. Call `openCommitment`; Oracle computes residualBidPercentAfterDecay = 0, so reward and slash are both 0.
5. Repeat with the new bid; prior commitment costs nothing.
6. PoC: `test_StoreUnopenedCommitmentTwice` stores same digest/signature at 1000 then 1100, both return same commitmentIndex.
**How found:** Code review of storeUnopenedCommitment: noticed index derivation omits dispatchTimestamp and no duplicate-index guard exists, then traced how dispatchTimestamp feeds residualBidPercentAfterDecay in Oracle.processBuilderCommitmentForBlockNumber to reach zero-slash.
**Exploitation:** Provider stakes, stores commitment at decayStartTimeStamp for max reward, then front-runs bidder's openCommitment or waits for a better MEV opportunity, re-storing the commitment with dispatchTimestamp beyond decayEndTimeStamp so slashing resolves to zero; provider never risks funds.
**Escalation:** Resetting isOpened flag also permits re-calling openCommitment on an already-opened commitment, inflating commitmentsCount and blocking later withdrawals from ProviderRegistry.
**Insight:** When a timestamp or mutable field drives an economic penalty, check whether it is part of the storage key and whether re-storing can overwrite it; also check bounds against the decay window's end so penalty can't be driven to zero.
