-
Notifications
You must be signed in to change notification settings - Fork 8
/
Zethell.sol
291 lines (233 loc) · 9.36 KB
/
Zethell.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
pragma solidity ^0.4.23;
/*
* Zethell.
*
* Written June 2018 for Zethr (https://www.zethr.io) by Norsefire.
* Special thanks to oguzhanox and Etherguy for assistance with debugging.
*
*/
contract ZTHReceivingContract {
function tokenFallback(address _from, uint _value, bytes _data) public returns (bool);
}
contract ZTHInterface {
function transfer(address _to, uint _value) public returns (bool);
function approve(address spender, uint tokens) public returns (bool);
}
contract Zethell is ZTHReceivingContract {
using SafeMath for uint;
address private owner;
address private bankroll;
// How much of the current token balance is reserved as the house take?
uint private houseTake;
// How many tokens are currently being played for? (Remember, this is winner takes all)
uint public tokensInPlay;
// The token balance of the entire contract.
uint public contractBalance;
// Which address is currently winning?
address public currentWinner;
// What time did the most recent clock reset happen?
uint public gameStarted;
// What time will the game end if the clock isn't reset?
uint public gameEnds;
// Is betting allowed? (Administrative function, in the event of unforeseen bugs)
bool public gameActive;
address private ZTHTKNADDR;
address private ZTHBANKROLL;
ZTHInterface private ZTHTKN;
mapping (uint => bool) validTokenBet;
mapping (uint => uint) tokenToTimer;
// Fire an event whenever the clock runs out and a winner is determined.
event GameEnded(
address winner,
uint tokensWon,
uint timeOfWin
);
// Might as well notify everyone when the house takes its cut out.
event HouseRetrievedTake(
uint timeTaken,
uint tokensWithdrawn
);
// Fire an event whenever someone places a bet.
event TokensWagered(
address _wagerer,
uint _wagered,
uint _newExpiry
);
modifier onlyOwner {
require(msg.sender == owner);
_;
}
modifier onlyBankroll {
require(msg.sender == bankroll);
_;
}
modifier onlyOwnerOrBankroll {
require(msg.sender == owner || msg.sender == bankroll);
_;
}
constructor(address ZethrAddress, address BankrollAddress) public {
// Set Zethr & Bankroll address from constructor params
ZTHTKNADDR = ZethrAddress;
ZTHBANKROLL = BankrollAddress;
// Set starting variables
owner = msg.sender;
bankroll = ZTHBANKROLL;
currentWinner = ZTHBANKROLL;
// Approve "infinite" token transfer to the bankroll, as part of Zethr game requirements.
ZTHTKN = ZTHInterface(ZTHTKNADDR);
ZTHTKN.approve(ZTHBANKROLL, 2**256 - 1);
// To start with, we only allow bets of 5, 10, 25 or 50 ZTH.
validTokenBet[5e18] = true;
validTokenBet[10e18] = true;
validTokenBet[25e18] = true;
validTokenBet[50e18] = true;
// Logarithmically decreasing time 'bonus' associated with higher amounts of ZTH staked.
tokenToTimer[5e18] = 24 hours;
tokenToTimer[10e18] = 18 hours;
tokenToTimer[25e18] = 10 hours;
tokenToTimer[50e18] = 6 hours;
// Set the initial timers to contract genesis.
gameStarted = now;
gameEnds = now;
gameActive = true;
}
// Zethr dividends gained are sent to Bankroll later
function() public payable { }
// If the contract receives tokens, bundle them up in a struct and fire them over to _stakeTokens for validation.
struct TKN { address sender; uint value; }
function tokenFallback(address _from, uint _value, bytes /* _data */) public returns (bool){
TKN memory _tkn;
_tkn.sender = _from;
_tkn.value = _value;
_stakeTokens(_tkn);
return true;
}
// First, we check to see if the tokens are ZTH tokens. If not, we revert the transaction.
// Next - if the game has already ended (i.e. your bet was too late and the clock ran out)
// the staked tokens from the previous game are transferred to the winner, the timers are
// reset, and the game begins anew.
// If you're simply resetting the clock, the timers are reset accordingly and you are designated
// the current winner. A 1% cut will be taken for the house, and the rest deposited in the prize
// pool which everyone will be playing for. No second place prizes here!
function _stakeTokens(TKN _tkn) private {
require(gameActive);
require(_zthToken(msg.sender));
require(validTokenBet[_tkn.value]);
if (now > gameEnds) { _settleAndRestart(); }
address _customerAddress = _tkn.sender;
uint _wagered = _tkn.value;
uint rightNow = now;
uint timePurchased = tokenToTimer[_tkn.value];
uint newGameEnd = rightNow.add(timePurchased);
gameStarted = rightNow;
gameEnds = newGameEnd;
currentWinner = _customerAddress;
contractBalance = contractBalance.add(_wagered);
uint houseCut = _wagered.div(100);
uint toAdd = _wagered.sub(houseCut);
houseTake = houseTake.add(houseCut);
tokensInPlay = tokensInPlay.add(toAdd);
emit TokensWagered(_customerAddress, _wagered, newGameEnd);
}
// In the event of a game restart, subtract the tokens which were being played for from the balance,
// transfer them to the winner (if the number of tokens is greater than zero: sly edge case).
// If there is *somehow* any Ether in the contract - again, please don't - it is transferred to the
// bankroll and reinvested into Zethr at the standard 33% rate.
function _settleAndRestart() private {
gameActive = false;
uint payment = tokensInPlay/2;
contractBalance = contractBalance.sub(payment);
if (tokensInPlay > 0) { ZTHTKN.transfer(currentWinner, payment);
if (address(this).balance > 0){
ZTHBANKROLL.transfer(address(this).balance);
}}
emit GameEnded(currentWinner, payment, now);
// Reset values.
tokensInPlay = tokensInPlay.sub(payment);
gameActive = true;
}
// How many tokens are in the contract overall?
function balanceOf() public view returns (uint) {
return contractBalance;
}
// Administrative function for adding a new token-time pair, should there be demand.
function addTokenTime(uint _tokenAmount, uint _timeBought) public onlyOwner {
validTokenBet[_tokenAmount] = true;
tokenToTimer[_tokenAmount] = _timeBought;
}
// Administrative function to REMOVE a token-time pair, should one fall out of use.
function removeTokenTime(uint _tokenAmount) public onlyOwner {
validTokenBet[_tokenAmount] = false;
tokenToTimer[_tokenAmount] = 232 days;
}
// Function to pull out the house cut to the bankroll if required (i.e. to seed other games).
function retrieveHouseTake() public onlyOwnerOrBankroll {
uint toTake = houseTake;
houseTake = 0;
contractBalance = contractBalance.sub(toTake);
ZTHTKN.transfer(bankroll, toTake);
emit HouseRetrievedTake(now, toTake);
}
// If, for any reason, betting needs to be paused (very unlikely), this will freeze all bets.
function pauseGame() public onlyOwner {
gameActive = false;
}
// The converse of the above, resuming betting if a freeze had been put in place.
function resumeGame() public onlyOwner {
gameActive = true;
}
// Administrative function to change the owner of the contract.
function changeOwner(address _newOwner) public onlyOwner {
owner = _newOwner;
}
// Administrative function to change the Zethr bankroll contract, should the need arise.
function changeBankroll(address _newBankroll) public onlyOwner {
bankroll = _newBankroll;
}
// Is the address that the token has come from actually ZTH?
function _zthToken(address _tokenContract) private view returns (bool) {
return _tokenContract == ZTHTKNADDR;
}
}
// And here's the boring bit.
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint a, uint b) internal pure returns (uint) {
if (a == 0) {
return 0;
}
uint c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint a, uint b) internal pure returns (uint) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint a, uint b) internal pure returns (uint) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint a, uint b) internal pure returns (uint) {
uint c = a + b;
assert(c >= a);
return c;
}
}