forked from Etheraffle/Solidity
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathetheraffle.sol
1594 lines (1586 loc) · 63.6 KB
/
etheraffle.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
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* Welcome to the 𝐄𝐭𝐡𝐞𝐫𝐚𝐟𝐟𝐥𝐞 Smart-Contract!
* The 𝐎𝐍𝐋𝐘 decentralized, charitable blockchain lottery!
*
* ##########################################
* ##########################################
* ### ###
* ### 𝐏𝐥𝐚𝐲 & 𝐖𝐢𝐧 𝐄𝐭𝐡𝐞𝐫 ###
* ### at ###
* ### 𝐄𝐓𝐇𝐄𝐑𝐀𝐅𝐅𝐋𝐄.𝐂𝐎𝐌 ###
* ### ###
* ##########################################
* ##########################################
*
* Etheraffle is designed to give 𝐡𝐮𝐠𝐞 𝐩𝐫𝐢𝐳𝐞𝐬 to
* players, sustainable 𝐄𝐓𝐇 𝐝𝐢𝐯𝐢𝐝𝐞𝐧𝐝𝐬 to 𝐋𝐎𝐓 token
* holders, and 𝐥𝐢𝐟𝐞-𝐜𝐡𝐚𝐧𝐠𝐢𝐧𝐠 𝐟𝐮𝐧𝐝𝐢𝐧𝐠 to charities.
*
* Learn more & get involved at 𝐞𝐭𝐡𝐞𝐫𝐚𝐟𝐟𝐥𝐞.𝐜𝐨𝐦/𝐈𝐂𝐎 to become a
* 𝐋𝐎𝐓 token holder today! Holding 𝐋𝐎𝐓 tokens automatically
* makes you a part of the decentralized, autonomous organisation
* that 𝐎𝐖𝐍𝐒 Etheraffle. Take your place in this decentralized,
* altruistic vision of the future!
*
* If you want to chat to us you have loads of options:
* On 𝐓𝐞𝐥𝐞𝐠𝐫𝐚𝐦 @ 𝐡𝐭𝐭𝐩𝐬://𝐭.𝐦𝐞/𝐞𝐭𝐡𝐞𝐫𝐚𝐟𝐟𝐥𝐞
* Or on 𝐓𝐰𝐢𝐭𝐭𝐞𝐫 @ 𝐡𝐭𝐭𝐩𝐬://𝐭𝐰𝐢𝐭𝐭𝐞𝐫.𝐜𝐨𝐦/𝐞𝐭𝐡𝐞𝐫𝐚𝐟𝐟𝐥𝐞
* Or on 𝐑𝐞𝐝𝐝𝐢𝐭 @ 𝐡𝐭𝐭𝐩𝐬://𝐞𝐭𝐡𝐞𝐫𝐚𝐟𝐟𝐥𝐞.𝐫𝐞𝐝𝐝𝐢𝐭.𝐜𝐨𝐦
*
*/
pragma solidity^0.4.20;
// import "github.com/oraclize/ethereum-api/oraclizeAPI.sol"; // Truffle uses the actual sol files
// import "github.com/Arachnid/solidity-stringutils/src/strings.sol"; // Truffle uses the actual sol files
import "./strings.sol";
import "installed_contracts/oraclize-api/contracts/usingOraclize.sol";
contract ReceiverInterface {
function receiveEther() external payable {}
}
contract EtheraffleUpgrade {
function manuallyAddToPrizePool() payable external {}
}
contract FreeLOTInterface {
function mint(address _to, uint _amt) external {}
function destroy(address _from, uint _amt) external {}
function balanceOf(address who) constant public returns (uint) {}
}
contract Etheraffle is usingOraclize {
using strings for *;
/**
* @title Etheraffle
* @author Greg Kapka (github.com/gskapka)
*
* ##########################################
* ### ###
* ### Variables ###
* ### ###
* ##########################################
*
*/
uint public week;
bool public paused;
uint public upgraded;
uint public prizePool;
address public ethRelief;
address public etheraffle;
address public upgradeAddr;
address public disburseAddr;
uint public take = 150; // ppt
uint public gasAmt = 500000;
uint public resultsDelay = 3600;
uint public matchesDelay = 3600;
uint public rafEnd = 500400; // 7:00pm Saturdays
uint public wdrawBfr = 6048000;
uint public gasPrc = 20000000000; // 20 gwei
uint public tktPrice = 2500000000000000;
uint public oracCost = 1500000000000000; // $1 @ $700
uint[] public pctOfPool = [520, 114, 47, 319]; // ppt...
uint[] public odds = [56, 1032, 54200, 13983816]; // Rounded down to nearest whole
uint constant public WEEKDUR = 604800;
uint constant public BIRTHDAY = 1500249600;//Etheraffle's birthday <3
// TODO: Can set strings after the fact to make deploy cheaper?
string public randomStr1 = "[URL] ['json(https://api.random.org/json-rpc/1/invoke).result.random[\"data\", \"serialNumber\"]','\\n{\"jsonrpc\": \"2.0\",\"method\":\"generateSignedIntegers\",\"id\":\"";
string public randomStr2 = "\",\"params\":{\"n\":\"6\",\"min\":1,\"max\":49,\"replacement\":false,\"base\":10,\"apiKey\":${[decrypt] BKM3j7tH7qBIQKuadP5kJ547Au1uB1Zo41u6tCfLPT3GDGJJCEpXLS87u1xlYsFu/i21zycQJgVFWzev+ZSjflQKsOCFbdN5oUSiR/GvD5nuLblzG6H+xq2lVdZ0lN/EZjrCmgMfaF0r3uo/FKcRdAnbf2wxKQ5Vfg==}}']";
string public apiStr1 = "[URL] ['json(https://etheraffle.com/api/a).m','{\"r\":\"";
string public apiStr2 = "\",\"k\":${[decrypt] BGQljYtTQ+yq9TZztMcWycMiaAezwNm3ppmcBvdh37ZJVJiTFbQw+h+WycbJtaklSFe2+S228NTf9eOh+6y06dlVpbJ3S28JhDOg50j4wqAIXdtCWDZLkAgyjXI3pOa3SJY3RV2b}}']";
FreeLOTInterface freeLOT;
/**
*
* ##########################################
* ### ###
* ### Data Structures ###
* ### ###
* ##########################################
*
*/
mapping (uint => rafStruct) public raffle;
struct rafStruct {
mapping (address => bytes32[]) entries;
uint tktPrice;
uint unclaimed;
uint[] winNums;
uint[] winAmts;
uint timeStamp;
bool wdrawOpen;
uint numEntries;
uint freeEntries;
}
mapping (bytes32 => qIDStruct) public qID;
struct qIDStruct {
uint weekNo;
bool isRandom;
bool isManual;
}
/**
*
* ##########################################
* ### ###
* ### Modifiers ###
* ### ###
* ##########################################
*
*/
/**
* @notice Modifier to prepend to functions adding the additional
* conditional requiring caller of the method to be the
* etheraffle address.
*/
modifier onlyEtheraffle() {
require(msg.sender == etheraffle);
_;
}
/**
* @notice Modifier to prepend to functions adding the additional
* conditional requiring the paused bool to be false.
*/
modifier onlyIfNotPaused() {
require(!paused);
_;
}
/**
* @notice Modifier to prepend to functions adding the additional
* conditional requiring caller of the method to be either
* the Oraclize or Etheraffle address.
*
*/
modifier onlyOraclize() {
require(msg.sender == oraclize_cbAddress() || msg.sender == etheraffle);
_;
}
/**
*
* ##########################################
* ### ###
* ### Events ###
* ### ###
* ##########################################
*
*/
event LogFunctionsPaused(uint identifier, uint atTime);
event LogQuerySent(bytes32 queryID, uint dueAt, uint sendTime);
event LogReclaim(uint indexed fromRaffle, uint amount, uint atTime);
event LogUpgrade(address newContract, uint ethTransferred, uint atTime);
event LogPrizePoolAddition(address fromWhom, uint howMuch, uint atTime);
event LogFreeLOTWin(uint indexed forRaffle, address toWhom, uint entryNumber, uint amount, uint atTime);
event LogOraclizeCallback(address functionCaller, bytes32 queryID, string result, uint indexed forRaffle, uint atTime);
event LogFundsDisbursed(uint indexed forRaffle, uint oraclizeTotal, uint amount, address indexed toAddress, uint atTime);
event LogWithdraw(uint indexed forRaffle, address indexed toWhom, uint forEntryNumber, uint matches, uint amountWon, uint atTime);
event LogWinningNumbers(uint indexed forRaffle, uint numberOfEntries, uint[] wNumbers, uint currentPrizePool, uint randomSerialNo, uint atTime);
event LogTicketBought(uint indexed forRaffle, uint indexed entryNumber, address indexed theEntrant, uint[] chosenNumbers, uint personalEntryNumber, uint tktCost, uint atTime, uint affiliateID);
event LogPrizePoolsUpdated(uint newMainPrizePool, uint indexed forRaffle, uint ticketPrice, uint unclaimedPrizePool, uint[] winningAmounts, uint atTime);
/**
*
* ##########################################
* ### ###
* ### Constructor ###
* ### ###
* ##########################################
*
*/
/**
* @notice Constructor. Sets the Etheraffle multisig address,
* the EthRelief & Disbursal contract addresses and
* instantiates the FreeLOT contract. Sets up an
* initial raffle struct.
*
* @param _etheraffle Address of the Etheraffle multisig.
*
* @param _disburse Address of the disbursal contract.
*
* @param _ethRelief Address of the EthRelief contract.
*
* @param _freeLOT Address of the freeLOT contract.
*
* 0x97f535e98cf250cdd7ff0cb9b29e4548b609a0bd - Etheraffle
* 0xb6a5f50b5ce5909a9c75ce27fec96e5de393af61 - Disbursal
* 0x7ee65fe55accd9430f425379851fe768270c6699 - EthRelief
* 0xc39f7bB97B31102C923DaF02bA3d1bD16424F4bb - FreeLOT
*/
function Etheraffle(address _etheraffle, address _disburse, address _ethRelief, address _freeLOT) public payable {
week = getWeek();
etheraffle = _etheraffle;
disburseAddr = _disburse;
ethRelief = _ethRelief;
freeLOT = FreeLOTInterface(_freeLOT);
setUpRaffleStruct(week, 2500000000000000, (week * WEEKDUR) + BIRTHDAY);
// TODO: Remove in prod!
OAR = OraclizeAddrResolverI(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475);
}
/**
*
* ##########################################
* ### ###
* ### Raffle Setup ###
* ### ###
* ##########################################
*
*/
/**
* @notice Function which gets current week number and if different
* from the global var week number, it updates that and sets
* up the new raffle struct. Should only be called once a
* week after the raffle is closed. Should it get called
* sooner, the contract is paused for inspection.
*
*/
function setUpNewRaffle() internal {
uint newWeek = getWeek();
if (newWeek == week) return pauseContract(true, 4);
setWeek(newWeek);
setUpRaffleStruct(newWeek, tktPrice, BIRTHDAY + (newWeek * WEEKDUR));
}
/**
* @notice Function using Etheraffle's birthday to calculate the
* week number since then.
*
* @return The current week number.
*
*/
function getWeek() public constant returns (uint) {
uint curWeek = (now - BIRTHDAY) / WEEKDUR;
return pastClosingTime(curWeek) ? curWeek + 1 : curWeek;
}
/**
* @notice Calculates if time function is called is past a raffle's
* designated end time.
*
* @return True if past closing else false.
*
*/
function pastClosingTime(uint _curWeek) internal view returns (bool) {
return now - ((_curWeek * WEEKDUR) + BIRTHDAY) > rafEnd;
}
/**
* @notice Sets up new raffle via creating a struct with the correct
* timestamp and ticket price.
*
* @param _week Desired week number for new raffle struct.
*
* @param _tktPrice Desired ticket price for the raffle
*
* @param _timeStamp Timestamp of Mon 00:00 of the week of this raffle
*
*/
function setUpRaffleStruct(uint _week, uint _tktPrice, uint _timeStamp) internal {
raffle[_week].tktPrice = _tktPrice;
raffle[_week].timeStamp = _timeStamp;
}
/**
* @notice Sets the withdraw status of a raffle.
*
* @param _week Week number for raffle in question.
*
* @param _status Desired withdraw status for raffle.
*
*/
function setWithdraw(uint _week, bool _status) internal {
raffle[_week].wdrawOpen = _status;
}
/**
* @notice Sets the global week variable.
*
* @param _week Desired week number.
*
*/
function setWeek(uint _week) internal {
week = _week;
}
/**
*
* ##########################################
* ### ###
* ### Pause Contract ###
* ### ###
* ##########################################
*
*/
/**
* @notice Sets the paused status of the contract to the bool
* passed in. Logs an event with a uint identifying the
* reason for pausing the contract to the front-end
* event watcher.
*
* @param _status Desired pause status.
*
* @param _id Uint identifing the reason function was called.
*/
function pauseContract(bool _status, uint _id) internal {
pauseContract(_status);
emit LogFunctionsPaused(_id, now);
}
/**
* @notice Sets the paused status of the contract to the bool
* passed in. This affects various of the contracts
* functions via the onlyIfNotPaused modifier.
*
* @param _status Desired pause status.
*
*/
function pauseContract(bool _status) internal {
paused = _status;
}
/**
*
* ##########################################
* ### ###
* ### Ticket Purchasing ###
* ### ###
* ##########################################
*
*/
/**
* @notice Function to enter a raffle. Checks for correct ticket price,
* then purchases ticket. Only callable when the contract is
* not paused.
*
* @param _cNums Ordered array of entrant's six selected numbers.
*
* @param _affID Affiliate ID of the source of this entry.
*
*/
function enterRaffle(uint[] _cNums, uint _affID) payable public onlyIfNotPaused {
require (validTktPrice(week, msg.value));
buyTicket(_cNums, msg.sender, msg.value, _affID);
}
/**
* @notice Function to enter the raffle on behalf of another address.
* Checks for correct ticket price. Only callable when the
* contract is not paused. In the event of a win, only the
* onBehalfOf address can claim it.
*
* @param _cNums Ordered array of entrant's six selected numbers.
*
* @param _affID Affiliate ID of the source of this entry.
*
* @param _onBehalfOf The address to be entered on behalf of.
*
*/
function enterOnBehalfOf(uint[] _cNums, uint _affID, address _onBehalfOf) payable public onlyIfNotPaused {
require (validTktPrice(week, msg.value));
buyTicket(_cNums, _onBehalfOf, msg.value, _affID);
}
/**
* @notice Function to enter the raffle for free. Requires the caller's
* balance of the Etheraffle freeLOT token to be greater than
* zero. Function destroys one freeLOT token, increments the
* freeEntries variable in the raffle struct then purchases the
* ticket. Only callable if contract is not paused.
*
* @param _cNums Ordered array of entrant's six selected numbers.
*
* @param _affID Affiliate ID of the source of this entry.
*
*/
function enterFreeRaffle(uint[] _cNums, uint _affID) payable public onlyIfNotPaused {
decrementFreeLOT(msg.sender, 1);
incremementEntries(week, true);
buyTicket(_cNums, msg.sender, msg.value, _affID);
}
/**
* @notice Function to enter the raffle on behalf of another address
* for free. Requires the caller's balance of the Etheraffle
* freeLOT token to be greater than zero. Function destroys
* one freeLOT token, increments the freeEntries variable in
* the raffle struct then purchases the ticket. Only callable
* when the contract is not paused. In the event of a win, only
* the onBehalfOf address can claim it.
*
* @param _cNums Ordered array of entrant's six selected numbers.
*
* @param _affID Affiliate ID of the source of this entry.
*
* @param _onBehalfOf The address to be entered on behalf of.
*
*/
function enterOnBehalfOfFree(uint[] _cNums, uint _affID, address _onBehalfOf) payable public onlyIfNotPaused {
decrementFreeLOT(msg.sender, 1);
incremementEntries(week, true);
buyTicket(_cNums, _onBehalfOf, msg.value, _affID);
}
/**
* @notice Checks that a raffle struct has a ticket price set and whether
* the caller's proposed price is GTE to it
*
* @param _week Week number for raffle in question.
*
* @param _price The price being paid for the ticket.
*
* @return True if msg.value greater than tktPrice, else false.
*
*/
function validTktPrice(uint _week, uint _price) internal view returns (bool) {
return (
raffle[_week].tktPrice > 0 &&
raffle[_week].tktPrice <= _price
);
}
/**
* @notice Decrement an address' FreeLOT token holdings by a specified
* amount.
*
* @param _address The address owning the FreeLOT token(s)
*
* @param _amt The amount of FreeLOT to destroy.
*
*/
function decrementFreeLOT(address _address, uint _amt) internal {
freeLOT.destroy(_address, _amt);
}
/**
* @notice Internal function that purchases raffle tickets. Requires the
* raffle be open for entry and the chosen numbers be valid.
* Increments number of entries in the raffle strut, adds the ticket
* price to the prize pool and stores a hash of the entrants chosen
* numbers before logging the purchase.
*
* @param _cNums Array of users selected numbers.
*
* @param _entrant Entrant's ethereum address.
*
* @param _value The ticket purchase price.
*
* @param _affID Affiliate ID of the source of this entry.
*
*/
function buyTicket(uint[] _cNums, address _entrant, uint _value, uint _affID) internal {
require (raffleOpenForEntry() && validNumbers(_cNums));
incremementEntries(week, false);
modifyPrizePool(true, _value);
storeEntry(week, _entrant, _cNums);
emit LogTicketBought(week, raffle[week].numEntries, _entrant, _cNums, raffle[week].entries[_entrant].length, _value, now, _affID);
}
/**
* @notice Stores a ticket purchase by hashing the chosen numbers
* and pushing them into an array mapped to the user's
* address in the relevant raffle's struct.
*
* @param _week Week number for raffle in question.
*
* @param _entrant The entrant's address.
*
* @param _cNums The entrant's chosen numbers.
*
*/
function storeEntry(uint _week, address _entrant, uint[] _cNums) internal {
raffle[_week].entries[_entrant].push(keccak256(_cNums));
}
/**
* @notice Increments the number of entries in a raffle struct.
* Increments free entries if bool passed is true, else
* normal entries otherwise.
*
* @param _week Week number for raffle in question
*
* @param _free Whether it is a free entry or not.
*
*/
function incremementEntries(uint _week, bool _free) internal {
_free ? raffle[week].freeEntries++ : raffle[_week].numEntries++;
}
/**
* @notice Temporal & raffle struct setup requirements that need to be
* satisfied before a raffle ticket can be purchased.
*
* @return True if raffle is open for entry, else false.
*
*/
function raffleOpenForEntry() internal view returns (bool) {
return (
raffle[week].timeStamp > 0 &&
now < raffle[week].timeStamp + rafEnd
);
}
/**
* @notice Series of requirements a raffle ticket's chosen numbers must
* pass in order to qualify as valid. Ensures that there are six
* numbers, in ascending order, between one and 49.
*
* @param _cNums Array of a ticket's proposed numbers in question.
*
* @return True if numbers are valid, else false.
*
*/
function validNumbers(uint[] _cNums) internal pure returns (bool) {
return (
_cNums.length == 6 &&
0 < _cNums[0] &&
_cNums[0] < _cNums[1] &&
_cNums[1] < _cNums[2] &&
_cNums[2] < _cNums[3] &&
_cNums[3] < _cNums[4] &&
_cNums[4] < _cNums[5] &&
_cNums[5] <= 49
);
}
/**
* @notice Modifies prizePool var. If true passed is in as first
* argument, prizePool is incremented by _amt, if false,
* decremented. In which latter case, it requires the minuend
* be smaller than the subtrahend.
*
* @param _bool Boolean signifying addtion or subtraction.
*
* @param _amt Amount to modify the prize pool by.
*
*/
function modifyPrizePool(bool _bool, uint _amt) private {
if (!_bool) require (_amt <= prizePool);
prizePool = _bool ? prizePool + _amt : prizePool - _amt;
// TODO: Remove temp event logging!
emit LogPrizePoolModification(_amt, _bool, now);
}
event LogPrizePoolModification(uint amount, bool wasPrizeAddition, uint atTime);
/**
*
* ##########################################
* ### ###
* ### Withdraw Winnings ###
* ### ###
* ##########################################
*
*/
/**
* @notice User calls this function in order to withdraw whatever
* winnings they are owed. It requires the entry be valid
* and the raffle open for withdraw. Function retrieves the
* number of matches then pays out accordingly. Only callable
* if the contract is not paused.
*
* @param _week Week number of the raffle the winning entry is from.
*
* @param _entryNum The entrant's entry number into this raffle.
*
*/
function withdrawWinnings(uint _week, uint _entryNum, uint[] _cNums) external onlyIfNotPaused {
require (validEntry(_week, _entryNum, _cNums, msg.sender) && openForWithdraw(_week));
uint matches = getMatches(_cNums, raffle[_week].winNums);
require (matches >= 2);
matches == 2
? winFreeGo(_week, _entryNum, msg.sender)
: payWinnings(_week, _entryNum, matches, msg.sender);
}
/*
* @notice Mints a FreeLOT coupon to a two match winner allowing them
* a free entry to Etheraffle. Function pausable by pause toggle.
*
* @param _week Week number of raffle whence the win originates.
*
* @param _entryNum Entry number of the win in question.
*
* @param _entrant Entry to whom the win belongs.
*
*/
function winFreeGo(uint _week, uint _entryNum, address _entrant) private onlyIfNotPaused {
invalidateEntry(_week, _entrant, _entryNum);
freeLOT.mint(_entrant, 1);
emit LogFreeLOTWin(_week, _entrant, _entryNum, 1, now);
}
/**
* @notice If ticket wins ETH this function first checks the eligibility
* for withdraw before invalidating the ticket, deducting the win
* from the unclaimed prizepool and finally transferring the winnings.
*
* @param _week Week number for raffle in question.
*
* @param _entryNum Entry number for ticket in question.
*
* @param _matches Number of matches ticket make to winning numbers.
*
* @param _entrant Address of the ticket holder.
*
*/
function payWinnings(uint _week, uint _entryNum, uint _matches, address _entrant) private {
require (eligibleForWithdraw(_week, _matches));
invalidateEntry(_week, _entrant, _entryNum);
modifyUnclaimed(false, _week, raffle[_week].winAmts[_matches - 3]);
transferWinnings(_entrant, raffle[_week].winAmts[_matches - 3]);
emit LogWithdraw(_week, _entrant, _entryNum, _matches, raffle[_week].winAmts[_matches - 3], now);
}
/**
* @notice Tranfers an amount of ETH to an address.
*
* @param _address Address to transger ETH to.
*
* @param _amt Amount of Wei to transfer.
*
*/
function transferWinnings(address _address, uint _amt) private {
_address.transfer(_amt);
}
/**
* @notice Modifies the unclaimed variable in a struct. If true passed
* in as first argument, unclaimed is incremented by _amt, if
* false, decremented. In which latter case, it requires the
* minuend is smaller than the subtrahend.
*
* @param _bool Boolean signifying addtion or subtraction.
*
* @param _week Week number for raffle in question.
*
* @param _amt Amount unclaimed is to be modified by.
*
*/
function modifyUnclaimed(bool _bool, uint _week, uint _amt) private {
if (!_bool) require (_amt <= raffle[_week].unclaimed);
raffle[_week].unclaimed = _bool ? raffle[_week].unclaimed + _amt : raffle[_week].unclaimed - _amt;
}
/**
* @notice Various requirements w/r/t number of matches, win amounts
* being set in the raffle struct and contract balance that
* need to be passed before withdrawal can be processed.
*
* @param _week Week number for raffle in question.
*
* @param _matches Number of matches the entry in question has made.
*
* @return True if eligible for withdraw, else false.
*
*/
function eligibleForWithdraw(uint _week, uint _matches) view internal returns (bool) {
return (
_matches >= 3 &&
raffle[_week].winAmts[_matches - 3] > 0 &&
raffle[_week].winAmts[_matches - 3] <= this.balance
);
}
/**
* @notice Compares hash of provided entry numbers to previously bought
* ticket's hashed entry numbers.
*
* @param _week Week number for raffle in question.
*
* @param _entryNum Entry number in question.
*
* @param _cNums Propsed entry numbers for entry in question.
*
* @param _entrant Address of entrant in question.
*
* @return True if entry is valid, else false.
*
*/
function validEntry(uint _week, uint _entryNum, uint[] _cNums, address _entrant) view internal returns (bool) {
return raffle[_week].entries[_entrant][_entryNum - 1] == keccak256(_cNums);
}
/**
* @notice Function zeroes the previously stored hash of an entrant's
* ticket's chosen numbers.
*
* @param _week Week number for raffle in question.
*
* @param _entrant Address of the entrant in question.
*
* @param _entryNum Entry number in question.
*
*/
function invalidateEntry(uint _week, address _entrant, uint _entryNum) private {
raffle[_week].entries[_entrant][_entryNum - 1] = 0;
}
/**
* @notice Various temporal requirements plus struct setup requirements
* that need to be met before a prize withdrawal can be processed.
* In order: Winning numbers need to be set, withdraw bool needs
* to be true, raffle's timestamp needs to be set, that the time
* the function is called needs to be before the withdraw deadline,
* and that the time the function is called needs to be six days
* beyond the timestamp.
*
* @param _week Week number for the raffle in question.
*
* @return True if raffle is open for withdraw, else false.
*
*/
function openForWithdraw(uint _week) view internal returns (bool) {
return (
winningNumbersSet(_week) && // Note: This means we don't need to check if getWeek GT raffle's week
winningAmountsSet(_week) && // Note: New bit here instead of old temporal req.
raffle[_week].wdrawOpen &&
raffle[_week].timeStamp > 0 &&
now - raffle[_week].timeStamp < wdrawBfr
);
}
/**
* @notice Function performs set intersection on two arrays
* and counts to see how many numbers they have in common.
*
* @param _cNums Array of entrant's chosen numbers.
*
* @param _wNums Array of winning numbers.
*
* @return Amount of numbers two arrays have in common.
*
*/
function getMatches(uint[] _cNums, uint[] _wNums) pure public returns (uint) {
require(_cNums.length == 6 && _wNums.length == 6);
uint matches;
for (uint i = 0; i < 6; i++) {
for (uint j = 0; j < 6; j++) {
if (_cNums[i] == _wNums[j]) {
matches++;
break;
}
}
}
return matches;
}
/**
*
* ##########################################
* ### ###
* ### Oraclize Callback ###
* ### ###
* ##########################################
*
*/
/**
* @notice The Oralize call back function. Only callable by Etheraffle
* or the Oraclize address. Emits an event detailing the callback,
* before running the relevant method that acts on the callback.
*
* @param _myID Unique id oraclize provides with their callbacks.
*
* @param _result The result of the api call.
*
*/
function __callback(bytes32 _myID, string _result) public onlyIfNotPaused onlyOraclize {
emit LogOraclizeCallback(msg.sender, _myID, _result, qID[_myID].weekNo, now);
queryIsRandom(_myID)
? randomCallback(_myID, _result)
: apiCallback(_myID, _result);
}
/**
* @notice Checks if an Oraclize query was made manually or not.
*
* @param _ID Bytes32 hash identifying the query in question.
*
* @return True if query was made manually, else false.
*
*/
function queryIsManual(bytes32 _ID) internal view returns (bool) {
return qID[_ID].isManual;
}
/**
* @notice Checks if an Oraclize query was to Random.org or not.
*
* @param _ID Bytes32 hash identifying the query in question.
*
* @return True if query pertains to a Random.org api call, else false.
*
*/
function queryIsRandom(bytes32 _ID) internal view returns (bool) {
return qID[_ID].isRandom;
}
/**
*
* ##########################################
* ### ###
* ### Random Callback ###
* ### ###
* ##########################################
*
*/
/**
* @notice Called when a random.org api callback comes in. It first
* reclaims unclaimed prizes from the raffle ten weeks previous,
* disburses this week's raffle's profits, sets the winning
* numbers from the callback in this raffle's struct and finally
* prepares the next Oraclize query to call the Etheraffle API.
* Function requires the winning numbers to not already have been
* set which stops Oraclize replays causing havoc!
*
* @param _myID The hash of the Oraclize query
*
* @param _result The result of the Oraclize query
*
*/
function randomCallback(bytes32 _myID, string _result) internal onlyOraclize {
require(!winningNumbersSet(qID[_myID].weekNo));
reclaimUnclaimed();
performAccounting(qID[_myID].weekNo);
setWinningNumbers(qID[_myID].weekNo, _result);
if (queryIsManual(_myID)) return;
sendQuery(matchesDelay, getQueryString(false, qID[_myID].weekNo), qID[_myID].weekNo, false, false);
}
/**
* @notice Returns bool depending on whether the winning numbers
* have been set in the struct or not.
*
* @param _week Week number for raffle in question.
*
* @return True if winning numbers are set correctly in raffle struct.
*
*/
function winningNumbersSet(uint _week) internal view returns (bool) {
return raffle[_week].winNums.length > 0;
}
/**
* @notice Called by the weekly Oraclize callback. Checks raffle 10
* weeks older than current raffle for any unclaimed prize
* pool. If any found, returns it to the main prizePool and
* zeros the amount.
*
*/
function reclaimUnclaimed() internal {
uint old = getWeek() - 11;
uint amt = getUnclaimed(old);
if (amt == 0) return;
modifyPrizePool(true, amt);
modifyUnclaimed(false, old, amt);
emit LogReclaim(old, amt, now);
}
//TODO: Check is this is used more than once - might be a refactor too far?
/**
* @notice Returns the unclaimed prize pool sequestered in a raffle's
* struct.
*
* @param _week Week number for raffle in question.
*
* @return Unclaimed prize pool sequestered in raffle's struct in Wei.
*
*/
function getUnclaimed(uint _week) public view returns (uint) {
return raffle[_week].unclaimed;
}
/**
* @notice Calculates and accounts for a raffle's costs and profits,
* before distributing the latter should there be any.
*
* @param _week Week number for raffle in question.
*
*/
function performAccounting(uint _week) internal {
uint cost = getOraclizeCost();
accountForCosts(cost);
uint profit = calcProfit(_week);
accountForProfit(profit);
distributeFunds(_week, cost, profit);
}
/**
* @notice Takes oraclize random.org api call result string and splits
* it at the commas into an array, parses those strings in that
* array as integers and pushes them into the winning numbers
* array in the raffle's struct. Fires event logging the data,
* including the serial number of the random.org callback so
* its veracity can be proven.
*
* @param _week The week number of the raffle in question.
*
* @param _result The results string from oraclize callback.
*
*/
function setWinningNumbers(uint _week, string _result) internal {
string[] memory arr = stringToArray(_result);
for (uint i = 0; i < 6; i++) {
raffle[_week].winNums.push(parseInt(arr[i]));
}
uint serialNo = parseInt(arr[6]);
emit LogWinningNumbers(_week, raffle[_week].numEntries, raffle[_week].winNums, prizePool, serialNo, now);
}
/**
* @notice Returns the cost of the Oraclize api calls
* (two per draw).
*
* @return The cost of the two Oraclize API calls per draw.
*
*/
function getOraclizeCost() internal view returns (uint) {
return ((gasAmt * gasPrc) + oracCost) * 2;
}
/**
* @notice Subtracts a given cost from the prize pool. Pauses contract
* instead if cost is greater than the prize pool.
*
* @param _cost Amount to be deducted from the prize pool.
*
*/
function accountForCosts(uint _cost) private {
if (_cost > prizePool) return pauseContract(true, 1);
if (_cost == 0) return; // TODO: Unnecessary? Will save a little gas except on deploy...
modifyPrizePool(false, _cost);
}
/**
* @notice Calculates profits earnt from a raffle. If there are
* no paid entries or if free entries outweigh paid
* entries, returns 0.
*
* @param _week Week number for raffle in question.
*
* @return The profit generated from a raffle's ticket sales.
*
*/
function calcProfit(uint _week) internal view returns (uint) {
return (raffle[_week].numEntries > 0 && raffle[_week].numEntries > raffle[_week].freeEntries)
? ((raffle[_week].numEntries - raffle[_week].freeEntries) * tktPrice * take) / 1000
: 0;
}
/**
* @notice Subtracts a given amount of profit from the prize pool, if
* said amount is greater than zero.
*
* @param _profit Amount to be deducted from the prize pool.
*
*/
function accountForProfit(uint _profit) private {
if (_profit == 0) return;
modifyPrizePool(false, _profit);
}
/**
* @notice Distributes any profit earnt from a raffle. Half goes to
* the disbursal contract for the DAO of token holders, and
* the remainder to the EthRelief contract for charitable
* donations.
*
* @param _week Week number for raffle in question.
*
* @param _cost Cost of running this raffle.
*
* @param _profit Profit from running this raffle.
*
*/
function distributeFunds(uint _week, uint _cost, uint _profit) private {
if (_profit == 0) return LogFundsDisbursed(_week, _cost, 0, 0, now); // Can't use emit keyword after return statement...
uint half = _profit / 2;
disburseFunds(_week, _cost, half, disburseAddr);
disburseFunds(_week, _cost, _profit - half, ethRelief);
}
/**
* @notice Sends funds via a given contract's "receiver" interface,
* which ensures an event is fired in the receiving contract,
* announcing the funds' arrival.
*
* @param _addr Address of receiving contract.
*
* @param _amt Amount of Wei to send.
*
*/
function disburseFunds(uint _week, uint _cost, uint _amt, address _addr) private {
ReceiverInterface(_addr).receiveEther.value(_amt)();
emit LogFundsDisbursed(_week, _cost, _amt, _addr, now);
}
/**
*
* ##########################################
* ### ###
* ### Etheraffle Callback ###
* ### ###
* ##########################################
*
*/
/**
* @notice Called when the Etheraffle API callback is received. It sets
* up the next raffle's struct, calculates this raffle's payouts
* then makes the next Oraclize query to call the Random.org api.
* Function requires the winning amounts to not already have been
* set which stops Oraclize replays causing havoc!
*
* @param _myID The hash of the Oraclize query
*
* @param _result The result of the Oraclize query
*
*/
function apiCallback(bytes32 _myID, string _result) internal onlyOraclize {
require (!winAmountsSet(qID[_myID].weekNo));
setUpNewRaffle();
setPayOuts(qID[_myID].weekNo, _result);
if (queryIsManual(_myID)) return;
sendQuery(getNextDeadline(), getQueryString(true, getWeek()), getWeek(), true, false);
}
/**
* @notice Returns bool depending on whether the win amounts have
* been set in the struct or not.
*
* @param _week Week number for raffle in question.
*
* @return True if the winning amounts are set correctly in the