MyCut

First Flight #23
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Lack of Duplicate Address Check in Pot Contract

Summary

Duplicate Player Addresses Allowed in Contest Creation, causes unfair distribution

Vulnerability Details

https://github.com/Cyfrin/2024-08-MyCut/blob/main/src/Pot.sol#L32-L34

The Pot contract, when initialized through the ContestManager createContest function, does not perform a check for duplicate addresses in the players array.
This oversight allows the same address to be listed multiple times as a contestant, potentially leading to loss of funds that could have been allotted to unique address.
Here is how amount alloted is handled in Pot contract:

....
for (uint256 i = 0; i < i_players.length; i++) {
@> playersToRewards[i_players[i]] = i_rewards[i];
}
...

here we can see if an address is added multiple times, it's reward amount will be overridden by latest one. That will lock the previously alloted amount in the contract.

POC

Add a following test in existing test suite:

function testDuplicatePlayerAddresses() public mintAndApproveTokens {
address[] memory duplicatePlayers = new address[](3);
duplicatePlayers[0] = makeAddr("player1");
duplicatePlayers[1] = makeAddr("player2");
duplicatePlayers[2] = makeAddr("player1"); // Duplicate address
uint256[] memory duplicateRewards = new uint256[](3);
duplicateRewards[0] = 500;
duplicateRewards[1] = 200;
duplicateRewards[2] = 100;
vm.startPrank(user);
address duplicateContest = ContestManager(conMan).createContest(
duplicatePlayers,
duplicateRewards,
IERC20(ERC20Mock(weth)),
800
);
ContestManager(conMan).fundContest(0);
vm.stopPrank();
// Verify that the contest was created successfully
address[] memory contests = ContestManager(conMan).getContests();
assertEq(
contests[contests.length - 1],
duplicateContest,
"Contest with duplicate addresses should be created"
);
// Verify that player1 can claim twice
vm.startPrank(player1);
Pot(duplicateContest).claimCut();
uint256 claimAmount = ERC20Mock(weth).balanceOf(player1);
vm.stopPrank();
console.log(
claimAmount,
"claimed by user 1"
);
// Fast forward 91 days
vm.warp(block.timestamp + 91 days);
// Close the contest
vm.prank(user);
ContestManager(conMan).closeContest(duplicateContest);
uint256 fundStucked = ERC20Mock(weth).balanceOf(address(duplicateContest));
console.log("fundStucked:", fundStucked);
}

run forge test --mt testDuplicatePlayerAddresses -vv in the terminal, it will return following output:

[⠊] Compiling...
[⠑] Compiling 1 files with Solc 0.8.20
[⠘] Solc 0.8.20 finished in 1.68s
Compiler run successful!
Ran 1 test for test/TestMyCut.t.sol:TestMyCut
[PASS] testDuplicatePlayerAddresses() (gas: 887300)
Logs:
User Address: 0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D
Contest Manager Address 1: 0x7BD1119CEC127eeCDBa5DCA7d1Bd59986f6d7353
Minting tokens to: 0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D
Approved tokens to: 0x7BD1119CEC127eeCDBa5DCA7d1Bd59986f6d7353
100 Player1 should be able to claim twice
fundStucked: 420
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.68ms (1.10ms CPU time)

Impact

Loss of funds / Unfair distribution

Tools Used

Manual Review

Recommendations

Add a check for duplicate address

constructor (.....) {
.... existing code
+ for (uint256 i = 0; i < players.length; i++) {
+ for (uint256 j = i + 1; j < players.length; j++) {
+ require(players[i] != players[j], "Duplicate player addresses are not allowed");
+ }
+ }
for (uint256 i = 0; i < i_players.length; i++) {
playersToRewards[i_players[i]] = i_rewards[i];
}
}
Updates

Lead Judging Commences

equious Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

incorrect handling of duplicate addresses

Support

FAQs

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