forked from fomolife/fomolife
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fomolife.cpp
342 lines (260 loc) · 11.2 KB
/
fomolife.cpp
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
#include "fomolife.hpp"
// random number generator [0,10)
int64_t rand() {
checksum256 result;
auto mixedBlock = tapos_block_prefix() * tapos_block_num();
const char *mixedChar = reinterpret_cast<const char *>(&mixedBlock);
sha256( (char *)mixedChar, sizeof(mixedChar), &result );
const char *p64 = reinterpret_cast<const char *>(&result);
return abs( (int64_t)p64[0] ) % 10;
}
// price increment
uint64_t price_inc( uint64_t price ) {
return 1;
}
void fomolife::withdraw( const account_name account, asset quantity ) {
// find user
auto itr = _balance.find( account );
eosio_assert( itr != _balance.end(), ("user " + name{account}.to_string() + " does not have any remaining balance or doesn't exist").c_str() );
// set asset quantity
quantity.amount = itr->balance;
// remove user from balance table
_balance.erase( itr );
// complete user withdraw request
action(
permission_level{ _this_contract, N(active) },
N(eosio.token), N(transfer),
std::make_tuple( _this_contract, account, quantity, std::string("Successful Fomolife Withdraw") )
).send();
}
void fomolife::on( const currency::transfer& t, account_name code ) {
// ping game status
ping();
// local variables
const account_name username = t.from;
auto counter = _counter.begin();
const uint64_t transfer_balance = t.quantity.amount;
const uint64_t current_key_price = counter->current_key_price;
eosio_assert( counter->current_order + 1 > counter->current_order, "integer overflow adding counter order" );
uint64_t new_order = counter->current_order + 1;
// do nothing if transfer is outgoing
if ( username == _this_contract ) return;
// transfer check
eosio_assert( code == N(eosio.token), "transfer not from eosio.token" );
eosio_assert( t.to == _this_contract, "transfer not made to this contract" );
// if transfer balance is 0.0001 EOS then process user withdrawal request
if ( transfer_balance == 1 ) {
withdraw( username, t.quantity );
return;
}
// quantity check
eosio_assert( transfer_balance >= current_key_price, ("Not enought balance to purchase 1 key. Current key price: " + std::to_string( current_key_price ) + "/10000 EOS and you sent " + std::to_string( transfer_balance ) + "/10000 EOS" ).c_str() );
eosio_assert( t.quantity.is_valid(), "invalid transfer quantity" );
// reward and jackpot
uint64_t team_reward = transfer_balance * TEAM_REWARD_SIZE;
uint64_t last_jackpot_inc = transfer_balance * LAST_JACKPOT_SIZE;
uint64_t tenth_jackpot_inc = transfer_balance * TENTH_JACKPOT_SIZE;
uint64_t random_jackpot_inc = transfer_balance * RANDOM_JACKPOT_SIZE;
uint64_t key_reward = transfer_balance * KEY_REWARD_SIZE;
// move key reward to random jackpot if |player| == 0
if ( _player.begin() == _player.end() ) {
random_jackpot_inc += key_reward;
key_reward = 0;
}
// allocate user key reward
for ( auto player = _player.begin(); player != _player.end(); player++ ) {
uint64_t user_reward = key_reward * ( player->keys / counter->keys_sold );
auto itr = _balance.find( player->username );
if ( itr == _balance.end() ) {
itr = _balance.emplace( _this_contract, [&](auto& p) {
p.username = player->username;
});
}
// update user balance
_balance.modify( itr, _this_contract, [&](auto& p) {
eosio_assert( p.balance + user_reward >= p.balance, "integer overflow adding user reward to balance" );
p.balance += user_reward;
});
// update player reward
_player.modify( player, _this_contract, [&](auto& p) {
eosio_assert( p.reward + user_reward >= p.reward, "integer overflow adding player reward" );
p.reward += user_reward;
});
}
// allocate team reward
auto team = _balance.find( team_account );
if ( team == _balance.end() ) {
team = _balance.emplace( _this_contract, [&](auto& p) {
p.username = team_account;
});
}
_balance.modify( team, _this_contract, [&](auto& p) {
eosio_assert( p.balance + team_reward > p.balance, "integer overflow adding team reward balance" );
p.balance += team_reward;
});
// buy keys
double keys_bought = 0;
uint64_t balance = transfer_balance;
uint64_t key_price = current_key_price;
while ( balance > key_price ) {
keys_bought++;
eosio_assert( balance - key_price < balance, "integer underflow subtracting key price" );
balance -= key_price;
eosio_assert( key_price + price_inc( key_price ) > key_price, "integer overflow incrementing key price" );
key_price += price_inc( key_price );
}
eosio_assert( (double)balance / (double)key_price <= 1, "floating point underflow calculating keys bought" );
keys_bought += (double)balance / (double)key_price;
eosio_assert( key_price + price_inc( key_price ) > key_price, "integer overflow incrementing key_price" );
key_price += price_inc( key_price );
// update counter
_counter.modify( counter, _this_contract, [&](auto& p) {
p.end_time = std::min( p.end_time + TIME_INC * (int)keys_bought, now() + MAX_TIME_INC );
p.last_buyer = username;
p.last_buy_time = now();
eosio_assert( p.revenue + transfer_balance > p.revenue, "integer overflow adding counter revenue" );
p.revenue += transfer_balance;
eosio_assert( p.last_jackpot + last_jackpot_inc > p.last_jackpot, "integer overflow adding last jackpot" );
p.last_jackpot += last_jackpot_inc;
eosio_assert( p.tenth_jackpot + tenth_jackpot_inc > p.tenth_jackpot, "integer overflow adding tenth jackpot" );
p.tenth_jackpot += tenth_jackpot_inc;
eosio_assert( p.random_jackpot + random_jackpot_inc > p.random_jackpot, "integer overflow adding random jackpot" );
p.random_jackpot += random_jackpot_inc;
eosio_assert( p.keys_sold + keys_bought > p.keys_sold, "integer overflow adding keys sold" );
p.keys_sold += keys_bought;
p.current_key_price = key_price;
p.current_order = new_order;
});
// find player
auto player = _player.find( username );
if ( player == _player.end() ) {
player = _player.emplace( _this_contract, [&](auto& p) {
p.username = username;
});
}
// update player
bool is_tenth = new_order % 10 == 0;
uint64_t r = rand();
double rate = (double)(20 * transfer_balance) / (double)counter->random_jackpot;
bool is_random_winner = r <= rate;
uint64_t instant_reward = 0;
if ( is_tenth ) {
eosio_assert( instant_reward + counter->tenth_jackpot >= instant_reward, "integer overflow adding tenth jackpot to instant reward" );
instant_reward += counter->tenth_jackpot;
}
if ( is_random_winner ) {
eosio_assert( instant_reward + counter->random_jackpot / 2 >= instant_reward, "integer overflow adding random jackpot to instant reward" );
instant_reward += counter->random_jackpot / 2;
}
_player.modify( player, _this_contract, [&](auto& p) {
eosio_assert( p.keys + keys_bought > p.keys, "integer overflow adding new keys" );
p.keys += keys_bought;
eosio_assert( p.invested + transfer_balance > p.invested, "integer overflow adding invested balance" );
p.invested += transfer_balance;
p.last_order = new_order;
p.last_buy_in_price = current_key_price;
if ( is_tenth || is_random_winner ) {
eosio_assert( p.reward + instant_reward >= p.reward, "integer overflow adding instant reward to player reward" );
p.reward += instant_reward;
}
if ( is_tenth ) p.tenth_reward = counter->tenth_jackpot;
if ( is_random_winner ) p.random_reward = counter->random_jackpot / 2;
});
if ( is_tenth || is_random_winner ) {
// update balance for player reward
auto user = _balance.find( username );
if ( user == _balance.end() ) {
user = _balance.emplace( _this_contract, [&](auto& p) {
p.username = username;
});
}
_balance.modify( user, _this_contract, [&](auto& p) {
eosio_assert( p.balance + instant_reward > p.balance, "integer overflow adding instant reward to player balance" );
p.balance += instant_reward;
});
// update counter for player reward
_counter.modify( counter, _this_contract, [&](auto& p) {
if ( is_tenth ) p.tenth_jackpot = 0;
if ( is_random_winner ) p.random_jackpot = p.random_jackpot - p.random_jackpot / 2;
});
}
// send receipt
asset quantity = t.quantity;
quantity.amount = 1;
auto msg = ( "Order:" + std::to_string( new_order ) + ".Buy-inPrice:"
+ std::to_string( current_key_price ) + ".Rand:"
+ std::to_string( r ) + ".Bound:"
+ std::to_string( (uint64_t)rate ) + ".WonRand:"
+ std::to_string( is_random_winner ) + ".InstantReward:"
+ std::to_string( instant_reward ) + ".TotalReward:"
+ std::to_string( player->reward ) + ".TenthJackpot:"
+ std::to_string( counter->tenth_jackpot ) + ".RandomJackpot:"
+ std::to_string( counter->random_jackpot ) + ".LastJackpot:"
+ std::to_string( counter->last_jackpot ) + ".EndTime:"
+ std::to_string( counter->end_time ) + ".Game:"
+ std::to_string( counter->game_number ) + ".Unit:/10000EOS" ).c_str();
action(
permission_level{ _this_contract, N(active) },
N(eosio.token), N(transfer),
std::make_tuple( _this_contract, username, quantity, std::string( msg ) )
).send();
}
void fomolife::ping() {
auto counter = _counter.begin();
// game ends
if ( counter->end_time <= now() ) {
// get winner account
const account_name winner = counter->last_buyer;
// find winner entry in _balance
auto user = _balance.find( winner );
if ( user == _balance.end() ) {
user = _balance.emplace( _this_contract, [&](auto& p) {
p.username = winner;
});
}
// update winner entry in _balance
_balance.modify( user, _this_contract, [&](auto& p) {
eosio_assert( p.balance + counter->last_jackpot >= p.balance, "integer overflow adding last jackpot to winner balance" );
p.balance += counter->last_jackpot;
});
// clear _player
for (auto itr = _player.begin(); itr != _player.end();)
itr = _player.erase(itr);
// add record to _history
_history.emplace( _this_contract, [&](auto& p) {
p.number = counter->game_number;
p.winner = counter->last_buyer;
p.revenue = counter->revenue;
p.start_time = counter->start_time;
p.end_time = counter->end_time;
});
// start new game
_counter.emplace( _this_contract, [&](auto& p) {
p.game_number = counter->game_number + 1;
p.last_buyer = _this_contract;
// move remaining balance to new random_jackpot
p.random_jackpot = counter->tenth_jackpot + counter->random_jackpot;
});
// remove old counter
_counter.erase(counter);
}
}
void fomolife::apply( account_name contract, account_name act ) {
if ( act == N(transfer) ) {
on( unpack_action_data<currency::transfer>(), contract );
return;
}
if ( contract != _this_contract )
return;
auto& thiscontract = *this;
switch( act ) {
EOSIO_API( fomolife, (ping) );
};
}
extern "C" {
[[noreturn]] void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
fomolife fomo( receiver );
fomo.apply( code, action );
eosio_exit(0);
}
}