Front-running rewardDistribution() allows reward sniping without real staking
Pending rewards are computed as the delta of stakingRewardPerGL with no time-based factor. A watcher can stake right before rewardDistribution() and unstake immediately after to capture the full reward.
Description
The current design of the GenesisLicenseStaking contract allows users to perform a sandwich-style attack around the rewardDistribution() function. Since staking rewards are not distributed proportionally to time staked, but rather through the delta of stakingRewardPerGL, a user can exploit the following behaviour:
- Stay unstaked until a reward distribution is about to happen.
- Detect an incoming call to
rewardDistribution()(from the knownrewardDistributor). - Front-run the transaction with a
stake()call. - Wait for the distribution (which increases
stakingRewardPerGL[...]). - Immediately call
unstake()to receive the entire reward without meaningful time staked.
The core issue stems from the reward calculation logic:
uint256 pendingReward = stakingRewardPerGL - stakeInfo.rewardPaid;
Where stakeInfo.rewardPaid is assigned upon stake() and stakingRewardPerGL is updated via rewardDistribution(). There is no time-based factor involved.
Impact
A bot watching the mempool for rewardDistribution() calls can capture rewards intended for genuine long-term stakers. Long-term stakers see their reward share diluted by these short-lived stakes.
Recommendation
Reward distribution should be adjusted to take into account the time each NFT has been staked to prevent zero-duration stake/unstake loops and reduce the effectiveness of front-running or sandwiching the rewardDistribution() call.

