MyCut

AI First Flight #8
Beginner FriendlyFoundry
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Unsafe Array Index Access Allows Out-of-Bounds Pot Access


  • The fundContest() function allows the contract owner to fund a specific contest by providing an array index.

  • The function directly accesses contests[index] without verifying that the index exists within the valid range of deployed contests, causing the transaction to revert with a generic panic error when an invalid index is provided.

Risk

Likelihood: Medium

  • Automated scripts may use outdated indices after contests are removed or reorganized

  • This will occur whenever the owner attempts to fund a contest using an index that exceeds the current contests.length

Impact: Medium

  • Transactions revert with uninformative "panic" errors, making debugging difficult

  • Gas is wasted on failed transactions

  • May cause temporary denial-of-service for legitimate funding attempts

https://github.com/CodeHawks-Contests/ai-mycut/blob/819134663950999ca5ab29a91eeceddb80274743/src/ContestManager.sol#L28-38

Proof of Concept

contract OutOfBoundsDemo {
ContestManager public manager;
constructor() {
manager = new ContestManager();
}
function testOutOfBounds() public returns (bool) {
try manager.fundContest(0) {
return false;
} catch Error(string memory reason) {
return false;
} catch Panic(uint errorCode) {
return true;
}
}
function testValidIndex() public {
// Create a contest first
address[] memory players = new address[](1);
uint256[] memory rewards = new uint256[](1);
players[0] = address(0x1);
rewards[0] = 100 ether;
IERC20 token = IERC20(address(0x123));
manager.createContest(players, rewards, token, 100 ether);
manager.fundContest(0);
}
}

Recommended Mitigation

- // REMOVE THIS UNSAFE IMPLEMENTATION
function fundContest(uint256 index) public onlyOwner {
Pot pot = Pot(contests[index]); // No bounds check
// ... rest of function
}
+ // ADD THIS SECURE IMPLEMENTATION
function fundContest(uint256 contestId) public onlyOwner {
// 1. Validate contest exists
require(contestId < contests.length, "ContestManager: invalid contest ID");
// 2. Get contest information
address potAddress = contests[contestId];
require(potAddress != address(0), "ContestManager: contest not initialized");
// 3. Validate contest hasn't been closed/funded already
// (Requires adding contest state tracking)
Pot pot = Pot(potAddress);
IERC20 token = pot.getToken();
uint256 totalRewards = contestToTotalRewards[potAddress];
// 4. Check sender has sufficient balance
require(
token.balanceOf(msg.sender) >= totalRewards,
"ContestManager: insufficient token balance"
);
// 5. Check allowance
uint256 allowance = token.allowance(msg.sender, address(this));
require(
allowance >= totalRewards,
"ContestManager: insufficient token allowance"
);
// 6. Safe transfer with error handling
bool success = token.transferFrom(
msg.sender,
potAddress,
totalRewards
);
require(success, "ContestManager: token transfer failed");
// 7. Update contest state (if tracking)
// contestStates[contestId] = ContestState.FUNDED;
// 8. Emit event
emit ContestFunded(contestId, potAddress, totalRewards, msg.sender);
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!