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 {
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);
}
}
- // 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);
}