52865 sc high inconsistency in how stake cooldown is handled due to off by one error
Submitted on Aug 13th 2025 at 19:12:10 UTC by @silver_eth for Attackathon | Plume Network
Report ID: #52865
Report Type: Smart Contract
Report severity: High
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/facets/StakingFacet.sol
Description
Brief/Intro Inconsistency in how the cooldown / slash logic is handled causes a stake to be simultaneously withdrawable and slashable in the same block due to an off-by-one style comparison mismatch.
Vulnerability Details
The staking logic treats a stake as withdrawable when:
block.timestamp >= cooldownEndTimeHowever, the cooldown is considered fully processed only if:
cooldownEndTime < slashTimestampThis creates a conflict when:
cooldownEndTime == slashTimestampIn that case:
Because
block.timestamp == cooldownEndTime, the cooldown is considered fulfilled and the stake can be withdrawn.But since
cooldownEndTime !< slashTimestamp, the slash logic still considers the stake slashable.
Thus a stake can be concurrently withdrawable and slashable within the same block.
Impact Details
Two possible undesirable outcomes exist depending on how transactions are ordered within the same block:
Premature withdrawals / protocol loss: If the intended invariant is
cooldownEnd < slashTimestamp, a user can withdraw a stake that is still slashable, effectively removing tokens that the protocol intended to keep available for slashing (protocol loss/theft).Permanent lock-up: If the intended invariant is
block.timestamp >= cooldownEndTimefor withdrawal, a user’s stake can become permanently locked because the slash logic still treats it as slashable and the user cannot withdraw if the slash occurs in the same block.
These outcomes can lead to permanent freezing of funds or direct theft of user funds (other than unclaimed yield).
Proof of Concept
Transaction ordering in block B
If User A’s withdrawal transaction is processed before the slash transaction:
User A successfully withdraws their stake (because
block.timestamp >= cooldownEndTime).The subsequent slash cannot affect User A’s withdrawn tokens, resulting in protocol loss / theft.
If User B’s withdrawal transaction is processed after the slash transaction:
The slash marks the stake as slashable and the withdrawal may be prevented, potentially leaving User B’s stake locked or lost.
Outcome
Both users had identical cooldownEndTime, yet only one could successfully withdraw depending on intra-block ordering. This results in either:
A user withdrawing tokens that were still slashable (stealing from the protocol), or
A user being unable to withdraw despite their cooldown being reached (permanent lock-up).
References
Source code reference: https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/plume/src/facets/StakingFacet.sol#L917
Notes / Suggested focus for remediation
Was this helpful?