Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: low
Invalid

Gas war for becoming Ram, no incentive to join/mint later

Summary

The protocol design favors early paricipation:

  • the earlier one joins the event by calling Dussehra::enterPeopleWhoLikeRam, the earlier one can start challenging others (ChoosingRam::increaseValuesOfParticipants) in the hope of increasing the value of one's NFT;

  • the earlier one starts to increase the value of one's NFT, the higher the chance one's NFT will be the first to reach the maximum possible value and as such, become the selected Ram for the event.

In contrast, there is no incentive to join the event in later stages.

Impact

  • Gas war for becoming the Ram for the event.

  • Declining participation / no participation at all in later stages (after Ram has been selected).

Tools Used

Manual review.

Recommendations

There are multiple possible solutions you can consider:

  • do not select Ram in ChoosingRam::increaseValuesOfParticipants. Instead, maintain a list all the NFTs that reached maximum value, and then randomly select Ram from that list (in ChoosingRam::selectRamIfNotSelected):

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import {RamNFT} from "./RamNFT.sol";
contract ChoosingRam {
error ChoosingRam__InvalidTokenIdOfChallenger();
error ChoosingRam__InvalidTokenIdOfPerticipent();
error ChoosingRam__TimeToBeLikeRamFinish();
error ChoosingRam__CallerIsNotChallenger();
error ChoosingRam__TimeToBeLikeRamIsNotFinish();
error ChoosingRam__EventIsFinished();
bool public isRamSelected;
RamNFT public ramNFT;
address public selectedRam;
+ uint256[] public maxValuedNFTs; // List of NFTs that reached maximum value
modifier RamIsNotSelected() {
require(!isRamSelected, "Ram is selected!");
_;
}
modifier OnlyOrganiser() {
require(ramNFT.organiser() == msg.sender, "Only organiser can call this function!");
_;
}
constructor(address _ramNFT) {
isRamSelected = false;
ramNFT = RamNFT(_ramNFT);
}
function increaseValuesOfParticipants(uint256 tokenIdOfChallenger, uint256 tokenIdOfAnyPerticipent)
public
RamIsNotSelected
{
if (tokenIdOfChallenger > ramNFT.tokenCounter()) {
revert ChoosingRam__InvalidTokenIdOfChallenger();
}
if (tokenIdOfAnyPerticipent > ramNFT.tokenCounter()) {
revert ChoosingRam__InvalidTokenIdOfPerticipent();
}
if (ramNFT.getCharacteristics(tokenIdOfChallenger).ram != msg.sender) {
revert ChoosingRam__CallerIsNotChallenger();
}
if (block.timestamp > 1728691200) {
revert ChoosingRam__TimeToBeLikeRamFinish();
}
uint256 random =
uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, msg.sender))) % 2;
if (random == 0) {
if (ramNFT.getCharacteristics(tokenIdOfChallenger).isJitaKrodhah == false){
ramNFT.updateCharacteristics(tokenIdOfChallenger, true, false, false, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfChallenger).isDhyutimaan == false){
ramNFT.updateCharacteristics(tokenIdOfChallenger, true, true, false, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfChallenger).isVidvaan == false){
ramNFT.updateCharacteristics(tokenIdOfChallenger, true, true, true, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfChallenger).isAatmavan == false){
ramNFT.updateCharacteristics(tokenIdOfChallenger, true, true, true, true, false);
} else if (ramNFT.getCharacteristics(tokenIdOfChallenger).isSatyavaakyah == false){
ramNFT.updateCharacteristics(tokenIdOfChallenger, true, true, true, true, true);
- selectedRam = ramNFT.getCharacteristics(tokenIdOfChallenger).ram;
+ maxValuedNFTs.push(tokenId); // Add to max valued NFTs list
}
} else {
if (ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).isJitaKrodhah == false){
ramNFT.updateCharacteristics(tokenIdOfAnyPerticipent, true, false, false, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).isDhyutimaan == false){
ramNFT.updateCharacteristics(tokenIdOfAnyPerticipent, true, true, false, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).isVidvaan == false){
ramNFT.updateCharacteristics(tokenIdOfAnyPerticipent, true, true, true, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).isAatmavan == false){
ramNFT.updateCharacteristics(tokenIdOfAnyPerticipent, true, true, true, true, false);
} else if (ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).isSatyavaakyah == false){
ramNFT.updateCharacteristics(tokenIdOfAnyPerticipent, true, true, true, true, true);
- selectedRam = ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).ram;
+ maxValuedNFTs.push(tokenId); // Add to max valued NFTs list
}
}
}
function selectRamIfNotSelected() public RamIsNotSelected OnlyOrganiser {
if (block.timestamp < 1728691200) {
revert ChoosingRam__TimeToBeLikeRamIsNotFinish();
}
if (block.timestamp > 1728777600) {
revert ChoosingRam__EventIsFinished();
}
- uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao))) % ramNFT.tokenCounter();
+ uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao))) % maxValuedNFTs.length;
- selectedRam = ramNFT.getCharacteristics(random).ram;
+ selectedRam = ramNFT.getCharacteristics(maxValuedNFTs[random]).ram;
isRamSelected = true;
}
}
  • implement a cool-off period for each tokenId in ChoosingRam::increaseValuesOfParticipants so that no single user can repeatedly call it at arbitrary frequency:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import {RamNFT} from "./RamNFT.sol";
contract ChoosingRam {
error ChoosingRam__InvalidTokenIdOfChallenger();
error ChoosingRam__InvalidTokenIdOfPerticipent();
error ChoosingRam__TimeToBeLikeRamFinish();
error ChoosingRam__CallerIsNotChallenger();
error ChoosingRam__TimeToBeLikeRamIsNotFinish();
error ChoosingRam__EventIsFinished();
+ error ChoosingRam__CoolOffPeriodNotFinished();
bool public isRamSelected;
RamNFT public ramNFT;
address public selectedRam;
+ uint256 public coolOffPeriod = 1 hours; // Cool-off period duration
+ mapping(uint256 => uint256) public lastChallengeTime; // Mapping to track last challenge time for each tokenId
modifier RamIsNotSelected() {
require(!isRamSelected, "Ram is selected!");
_;
}
modifier OnlyOrganiser() {
require(ramNFT.organiser() == msg.sender, "Only organiser can call this function!");
_;
}
constructor(address _ramNFT) {
isRamSelected = false;
ramNFT = RamNFT(_ramNFT);
}
function increaseValuesOfParticipants(uint256 tokenIdOfChallenger, uint256 tokenIdOfAnyPerticipent)
public
RamIsNotSelected
{
if (tokenIdOfChallenger > ramNFT.tokenCounter()) {
revert ChoosingRam__InvalidTokenIdOfChallenger();
}
if (tokenIdOfAnyPerticipent > ramNFT.tokenCounter()) {
revert ChoosingRam__InvalidTokenIdOfPerticipent();
}
if (ramNFT.getCharacteristics(tokenIdOfChallenger).ram != msg.sender) {
revert ChoosingRam__CallerIsNotChallenger();
}
if (block.timestamp > 1728691200) {
revert ChoosingRam__TimeToBeLikeRamFinish();
}
+ if (block.timestamp < lastChallengeTime[tokenIdOfChallenger] + coolOffPeriod) {
+ revert ChoosingRam__CoolOffPeriodNotFinished();
+ }
+ lastChallengeTime[tokenIdOfChallenger] = block.timestamp;
uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, msg.sender))) % 2;
if (random == 0) {
if (ramNFT.getCharacteristics(tokenIdOfChallenger).isJitaKrodhah == false) {
ramNFT.updateCharacteristics(tokenIdOfChallenger, true, false, false, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfChallenger).isDhyutimaan == false) {
ramNFT.updateCharacteristics(tokenIdOfChallenger, true, true, false, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfChallenger).isVidvaan == false) {
ramNFT.updateCharacteristics(tokenIdOfChallenger, true, true, true, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfChallenger).isAatmavan == false) {
ramNFT.updateCharacteristics(tokenIdOfChallenger, true, true, true, true, false);
} else if (ramNFT.getCharacteristics(tokenIdOfChallenger).isSatyavaakyah == false) {
ramNFT.updateCharacteristics(tokenIdOfChallenger, true, true, true, true, true);
selectedRam = ramNFT.getCharacteristics(tokenIdOfChallenger).ram;
}
} else {
if (ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).isJitaKrodhah == false) {
ramNFT.updateCharacteristics(tokenIdOfAnyPerticipent, true, false, false, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).isDhyutimaan == false) {
ramNFT.updateCharacteristics(tokenIdOfAnyPerticipent, true, true, false, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).isVidvaan == false) {
ramNFT.updateCharacteristics(tokenIdOfAnyPerticipent, true, true, true, false, false);
} else if (ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).isAatmavan == false) {
ramNFT.updateCharacteristics(tokenIdOfAnyPerticipent, true, true, true, true, false);
} else if (ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).isSatyavaakyah == false) {
ramNFT.updateCharacteristics(tokenIdOfAnyPerticipent, true, true, true, true, true);
selectedRam = ramNFT.getCharacteristics(tokenIdOfAnyPerticipent).ram;
}
}
}
function selectRamIfNotSelected() public RamIsNotSelected OnlyOrganiser {
if (block.timestamp < 1728691200) {
revert ChoosingRam__TimeToBeLikeRamIsNotFinish();
}
if (block.timestamp > 1728777600) {
revert ChoosingRam__EventIsFinished();
}
uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao))) % ramNFT.tokenCounter();
selectedRam = ramNFT.getCharacteristics(random).ram;
isRamSelected = true;
}
}
Updates

Lead Judging Commences

bube Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Info/Gas/Invalid according to docs

Support

FAQs

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