Description
Risk
Likelihood: High
Impact: High
-
MEV & Flashbots can be used to automate collecting
-
DoS - all participants can be locked from receiving free Snow
-
Poor UX - Participants must
Proof of Concept
With no restriction the same user can claim every time in the correct time, we need to adjust or remove the first to call logic
function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimer = block.timestamp;
}
Recommended Mitigation
Adding a track for the last receiver of Snow
and preventing him from claiming every reward
This will stop the automated claiming from a single actor.
+ error S_ClaimedPrevReward();
+ address prevClaimer;
function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) { // @> the first caller will receive the snow in a specific time each time
revert S__Timer();
}
+ if (prevClaimer == msg.sender) {
+ revert S_ClaimedPrevReward();
+ }
_mint(msg.sender, 1);
s_earnTimer = block.timestamp;
+ prevClaimer = msg.sender;
}
Another way, and more fair can be using lottery principle.
This can be achieved with adding 2 functions:
enterSnowFight
- function for entering the lottery for Snow
drawWinner
- function that can be called only by collector
+ uint256 public participantCount;
+ mapping(uint256 => address) public participants;
+ mapping(address => bool) public registered;
+ function enterSnowFight() external canFarmSnow {
+ require(!registered[msg.sender], "Already in!");
+
+ participants[participantCount] = msg.sender;
+ participantCount++;
+ registered[msg.sender] = true;
+ }
+ function drawWinner() external onlyCollector {
+ if (block.timestamp < s_earnTimer + 1 weeks) revert("Too early");
+ if (participantCount == 0) revert("No participants");
+ uint256 winnerIndex = uint256(
+ keccak256(abi.encodePacked(block.timestamp, block.prevrandao, participantCount))
+ ) % participantCount;
// @> Will be good to integrate Chainlink VRF for randomness
+ address winner = participants[winnerIndex];
+ _mint(winner, 1);
+ for (uint256 i = 0; i < participantCount; i++) {
+ delete registered[participants[i]];
+ delete participants[i];
+ }
+ participantCount = 0;
+ s_earnTimer = block.timestamp;
+ }