I'm working on a token that has a lottery payout function. I'm currently using for loops to manage address entries in an array that defines a users eligibility for the lottery. The transfer function loops through the array and based on their holdings after token transfer either adds or removes the address to or from the array. Tests work fine with a small number of array items but my concern is as the array of eligible holders increases the contract will break due to gas costs.
mapping (address => bool) isExemptFromFees;
address[] private eligibleForLotto;
function _transferNormal(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
require(!isPaused, "Transfers are paused, check official announcements for info");
require(amount <= maxSupply / 200, "Tx amount cant be more than 0.5% of total supply");
(uint _tAmount, uint _dFee, uint _mFee, uint _lFee) = getTaxAmount(amount);
require(_balances[recipient] + _tAmount <= maxSupply / 100, "Cant hold more than 1% of maxSupply");
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += _tAmount;
_balances[devWallet] += _dFee;
_balances[managementWallet] += _mFee;
_balances[address(this)] += _lFee;
emit Transfer(sender, recipient, _tAmount);
emit Transfer(sender, devWallet, _dFee);
emit Transfer(sender, managementWallet, _mFee);
emit Transfer(sender, address(this), _lFee);
if (!isExemptFromFees[sender] && _balances[sender] < holdRequirementForLottoEligibility) {
for (uint i = 0; i < eligibleForLotto.length; i++) {
if (eligibleForLotto[i] == sender) {
eligibleForLotto[i] = eligibleForLotto[eligibleForLotto.length - 1];
eligibleForLotto.pop();
break;
}
}
}
if (!isExemptFromFees[recipient] && _balances[recipient] >= holdRequirementForLottoEligibility) {
bool exists;
for (uint i = 0; i < eligibleForLotto.length; i++) {
if (eligibleForLotto[i] == recipient){
exists = true;
break;
}
}
if (!exists) {
eligibleForLotto.push(recipient);
}
}
if (lotteryEnabled) {
txCounter++;
if (txCounter == txNumForDraw) {
uint jackpot = _balances[address(this)];
uint modulus = eligibleForLotto.length * 2;
uint rand = uint(keccak256(abi.encodePacked(block.timestamp + _tAmount + jackpot)));
uint lottoResult = rand % modulus;
if (lottoResult > eligibleForLotto.length) { //Lotto rollover occurs if result does not match an eligible wallet
txCounter = 0; //Reset the txCounter for the next lotto draw
} else { //distribute jackpot to winner and reset the txCounter
address winner = eligibleForLotto[lottoResult];
_balances[address(this)] -= jackpot;
_balances[winner] += jackpot;
emit Transfer(address(this), winner, jackpot);
}
}
}
txCounter = 0;
if (autoTxNumForDraw) {
txNumForDraw = (rand % 10) + 10;
}
}
Ideally I'd use mappings as opposed to an array, but can't seem to get my head around implementing it without complicating the lottery draw. Is there an obvious solution I'm overlooking?