-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBoard.sol
349 lines (309 loc) · 11.6 KB
/
Board.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
pragma solidity 0.7.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@chainlink/contracts/src/v0.6/ChainlinkClient.sol";
import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol";
//
// The Board contract
//
contract Board is AccessControl, VRFConsumerBase {
struct Square {
address payable purchaser;
string squareTag;
uint row;
uint cell;
uint across;
uint down;
}
bytes32 internal keyHash;
uint256 internal linkFee;
uint256 internal randomResult;
uint gameId;
address internal payable host;
address internal payable admin;
uint boardFee;
uint forwardPrize;
uint backwardPrize;
uint availableSquarezCount;
enum GameState{STARTED, OVER};
enum BoardStatus{OPEN, SOLDOUT, CLOSED};
GameState game;
BoardStatus boardStatus;
uint gameStartTime;
uint gameEndTime;
string homeTeam;
string awayTeam;
uint[10] internal across;
uint[10] internal down;
Square[10][10] internal squarez;
// address[4] internal payable forwardWinnerAddresses;
mapping(address => uint) forwardWinnerAddresses;
// address[4] internal payable backwardWinnerAddresses;
mapping(addres => uint) backwardWinnerAddresses;
uint[4] internal homeTeamScores;
uint[4] internal awayTeamScores;
event PrizeCollected(address winner, uint totalAmount);
event SquarePurchased(address purchaser);
constructor(uint _gameId, address _host, uint _boardFee, string _homeTeam, string _awayTeam)
VRFConsumerBase(
0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B, // VRF Coordinator
0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token
) public {
keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311;
linkFee = 0.1 * 10 ** 18; // 0.1 LINK
// set gameId
gameId = _gameId;
// set host
host = _host;
// set admin
admin = msg.sender;
// set boardFee;
boardFee = _boardFee;
// TODO: allow board fee to be paid in Eth or Dai stablecoin
across = [0,1,2,3,4,5,6,7,8,9];
down = [0,1,2,3,4,5,6,7,8,9];
homeTeam = _homeTeam;
awayTeam = _awayTeam;
forwardPrize = boardFee * 15;
backwardPrize = forwardPrize / 2;
availableSquarezCount = 100;
boardStatus = BoardStatus.OPEN;
// create squarez
for (uint i = 0; i < 10; i++){
uint currentRow = i;
for(uint j = 0; j < 10; j++){
uint currentCell = j;
Square square;
square.row = currentRow;
square.cell = currentCell;
squarez[currentRow][currentCell] = square;
}
}
}
// modifiers
//adminOnly
modifier onlyAdmin {
require(msg.sender == admin);
_;
}
//hostOnly
modifier onlyHost {
require(msg.sender == host);
_;
}
//adminOrHostOnly
modifier adminOrHostOnly {
require(msg.sender == admin || msg.sender == host);
_;
}
//winnerOnly
modifier winnerOnly {
require(isWinner(msg.sender));
_;
}
// initialize board
// admin only
function initializeBoard(uint seedNum) adminOnly {
// transfer 2 link tokens
require(LINK.transfer(address(this), 2, "Unable to transfer");
// shuffle down and across array elements
shuffle(seedNum);
// update Sqaurez array
for (uint i = 0; i < down.lenth; i++){
uint currentRow = i;
for(uint j = 0; j < across.length; j++){
uint currentCell = j;
Square square = squarez[currentRow][currentCell];
square.across = across[i];
square.down = down[j];
squarez[currentRow][currentCell] = square;
}
}
// set board status to OPEN
boardStatus = BoardStatus.OPEN;
}
/**
* Withdraw LINK from this contract
*
* DO NOT USE THIS IN PRODUCTION AS IT CAN BE CALLED BY ANY ADDRESS.
* THIS IS PURELY FOR EXAMPLE PURPOSES.
*/
/* function withdrawLink() external {
require(LINK.transfer(msg.sender, LINK.balanceOf(address(this))), "Unable to transfer");
} */
// purchaseSquare method:
receive() external payable {
require(availableSquarezCount >= 1, "This BigBoard is SOLD OUT.");
require(msg.value == boardFee, "Please pay full board fee.");
Square purchasedSquare;
// selected square
purchasedSquare = nextAvailableSquare();
purchasedSquare.purchaser = msg.sender;
// purchasedSquarez.push(purchasedSquare);
purchasedSquarez[purchasedSquare.row][purchasedSquare.cell] = purchasedSquare;
// decrement available squarez count
availableSquarezCount--;
if(availableSquarezCount == 0){
boardStatus = BoardStatus.SOLDOUT;
}
// emit square purchased
emit SquarePurchased(msg.sender);
}
// buy a specific square
funtion buyThisSquare(uint row, uint cell) public payable {
require(isAvailable(row, cell), "This Square is NOT AVAILABLE.");
require(msg.value == boardFee, "Please pay full board fee.");
Square selectedSquare = squarez[row][cell];
selectedSquare.purcaser = msg.sender;
availableSquarezCount--;
if(availableSquarezCount == 0){
boardStatus = BoardStatus.SOLDOUT;
}
emit SquarePurchased(msg.sender);
}
// get next available square
function nextAvailableSquare() internal returns (Square){
Square selectedSquare;
// loop over squarez
for (uint i = 0; i < 10; i++){
uint currentRow = i;
for(uint j = 0; j < 10; j++){
uint currentCell = j;
selectedSquare = squarez[currentRow][currentCell];
// if purchaser address is not set
if(selectedSquare.purchaser == address(0x0)){
break;
}
}
// if purchaser address is not set
if(selectedSquare.purchaser == address(0x0)){
break;
}
}
// end loop
// return first square with no purchaser
return selectedSquare;
}
// is square available
function isAvailable(uint r, unit c) public returns (bool) {
selectedSquare = squarez[r][c];
// if purchaser address is not set
if(selectedSquare.purchaser == address(0x0)){
return true
} else{
return false;
}
}
// square purchasers or admin/host
function selectWinners() adminOrHostOnly {
// TODO: read current scores for this gameId from BigBoardz contract
// require game is over
require(game = GameState.OVER);
// get last digit of total score for each quarter
// for each quarter (4)
for (uint q = 0; q < 4; q++){
// get index of across digit matching last digit of homeTeam's score
uint selectedCell;
uint selectedRow;
for (uint c = 0; c < across.length ; c++){
if(across[c] != homeTeamScores[q]){
continue;
} else{
selectedCell = c;
}
}
// get index of down digit matching last digit of awayTeam's score
for (uint r = 0; r < down.length; r++){
if(down[r] != awayTeamScores[q]){
continue;
} else{
selectedRow = r;
}
}
// check for forward win
if( purchasedSquarez[selectedRow][selectedCell] != null ){
// get the purchased square that has a cell == across index and row == down index
PurchasedSquare winningSquare = purchasedSquarez[selectedRow][selectedCell];
// place selected square's purchaser address in the quarter's forward winner
// forwardWinnerAddresses[q] = winningSquare.purchaser;
uint quarter = q + 1;
forwardWinnerAddresses[winningSquare.purchaser] = quarter;
}
// check for backward win
if( purchasedSquarez[selectedCell][selectedRow] != null ){
// get the purchased square that has a cell == down index and row == across index
PurchasedSquare bWinningSquare = purchasedSquarez[selectedCell][selectedRow];
// place selected square's purchaser address in the quarter's backward winner
uint quarter = q + 1;
// backwardWinnerAddresses[q] = bWinningSquare.purchaser;
backwardWinnerAddresses[bWinningSquare.purchaser] = quarter;
}
}
}
function isWinner(address possibleWinner) public returns(bool){
if(forwardWinnerAddresses[possibleWinner] >= 1 || backwardWinnerAddresses[possibleWinner] >= 1){
return true;
} else {
return false;
}
}
// winner only
function collectPrize() winnerOnly {
// require msg.sender be listed as a winner
uint fMultiplier = 0;
uint bMultiplier = 0;
uint totalFPrize = 0;
uint totalBPrize = 0;
uint totalPrize;
// for each address in forwardWinnerAddresses
for (uint p = 0; p > forwardWinnerAddresses.length; p++){
if (msg.sender == forWardWinnderAddress[p]){
fMultiplier++
}
}
totalFPrize = forwardPrize * fMultiplier;
// for each address in forwardWinnerAddresses
for (uint bP = 0; bP > backwardWinnerAddresses.length; bP++){
if (msg.sender == forWardWinnderAddress[bP]){
bMultiplier++
}
}
totalBPrize = backwardPrize * bMultiplier;
uint total = totalFPrize + totalBPrize;
address(msg.sender).transfer(total);
emit PrizeCollected(msg.sender, total);
}
// host only
// TODO: finish this method
function collectProfits() hostOnly {
//
}
/**
* Requests randomness from a user-provided seed
*/
function getRandomNumber(uint256 userProvidedSeed) internal returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) > fee, "Not enough LINK - ");
return requestRandomness(keyHash, fee, userProvidedSeed);
}
/**
* Callback function used by VRF Coordinator
*/
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
require(msg.sender == vrfCoordinator, "Fulfillment only permitted by Coordinator");
randomResult = randomness;
}
// shuffle method
// admin only
// can only be called once
function shuffle(uint seed) internal adminOnly {
for(uint s = across.length -1; s > 0; s--){
getRandomNumber(seed);
uint randomIndex = randomResult % across.length;
across[s], across[randomIndex] = across[randomIndex], across[s];
}
for(uint s = down.length -1; s > 0; s--){
getRandomNumber(seed);
uint randomIndex = randomResult % down.length;
down[s], down[randomIndex] = [down[randomIndex], down[s];
}
}
}