I have a betting contract, such that each week the prior week's bets are no longer relevant. I'm thinking about the most efficient way to store and process these bets. The alternatives are many, and so I just want to present my favorite and see if there are any obvious deficiencies. I have simplified/omitted things to be clear, hopefully not too much. I'm leaning towards a method that puts new bets into separate dynamic arrays, and then deleting them each week.
Dynamic arrays capture the relevant data: when the bettor puts 1.45 ETH on the Chicago Bears to beat the point spread. The are dynamic arrays, state variable:
address[] public bettorAddress; // eg, 0x123...
uint[] public betAmount; // eg, 145e16
uint[] public betPick; // eg, 7
For any bet the relevant data needed to process are all in the same slot, as each bet is processed with a method such as the following:
function takeBet(bet1, team)
external
{
betAmount.push(bet1);
bettorAddress.push(msg.sender);
betPick.push(team);
}
I can process the winners and losers in a loop by running it in chunks (say, 100 max). A variable (loopCounter in this example) is set to zero each week. At the end of the week, each team corresponds to a slot in the bool array resultsVector to indicate whether the bettor won or not (eg, if the Chicago Bears are team #7, 'resultsVector[7] = true' if the Bears beat the spread).
Thus, if there are 589 bets, and processing 100 bets costs 4e6 in gas, that's 6 separate transactions if I break it into chunks of 100. The settlement function would work as follows:
function settleBook()
external
{
uint top = min(loopCounter + 100, betAmount.length);
// other code...
uint i;
/** state variable loopCounter = 0 at start
**/
for (i = loopCounter; i < top; i++) {
/** results for 32 teams updated outside this function, in state variable: bool public resultsVector[32]
**/
if (resultsVector[betPick[i]]) {
/** processes the results by giving bettor[i] appropriate credit for his successful pick. State variable bettorBalance defined as: mapping(address => uint) public bettorBalance
**/
bookiePot -= betAmount[i];
/** state variable bettorBalance defined as: mapping(address => uint) public bettorBalance;
**/
bettorBalance[bettorAddress[i]] += betAmount[i];
}
}
/** advance the loopCounter to start at the next unprocessed bettor on the next function call
**/
loopCounter = i;
/** if all done, delete and reset loopCounter for the next week
**/
if (i == betAmount.length) {
// other code....
loopCounter = 0;
delete betAmount;
delete bettorAddress;
delete betPick;
}
}
It works, but my biggest concern is that while it seems linear, I'm not sure it is: if I had 8765 bets, requiring 88 function calls to ultimately process, would this still work?
I could require users to process their bets, which breaks it up into single calls. Yet, some would not respond, and then I would need a method to purge lazy/distracted user data that creates problems. Further, it's more gas efficient to process them all at once.
Criticisms and suggestions welcome.