Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
rex.cpp
Go to the documentation of this file.
4
5namespace sysiosystem {
6
7 using sysio::current_time_point;
8 using sysio::token;
9 using sysio::seconds;
10
11 void system_contract::deposit( const name& owner, const asset& amount )
12 {
13 require_auth( owner );
14
15 check( amount.symbol == core_symbol(), "must deposit core token" );
16 check( 0 < amount.amount, "must deposit a positive amount" );
17 // inline transfer from owner's token balance
18 {
19 token::transfer_action transfer_act{ token_account, { owner, active_permission } };
20 transfer_act.send( owner, rex_account, amount, "deposit to REX fund" );
21 }
22 transfer_to_fund( owner, amount );
23 }
24
25 void system_contract::withdraw( const name& owner, const asset& amount )
26 {
27 require_auth( owner );
28
29 check( amount.symbol == core_symbol(), "must withdraw core token" );
30 check( 0 < amount.amount, "must withdraw a positive amount" );
31 update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) );
32 transfer_from_fund( owner, amount );
33 // inline transfer to owner's token balance
34 {
36 transfer_act.send( rex_account, owner, amount, "withdraw from REX fund" );
37 }
38 }
39
40 void system_contract::buyrex( const name& from, const asset& amount )
41 {
42 require_auth( from );
43
44 check( amount.symbol == core_symbol(), "asset must be core token" );
45 check( 0 < amount.amount, "must use positive amount" );
46 check_voting_requirement( from );
47 transfer_from_fund( from, amount );
48 const asset rex_received = add_to_rex_pool( amount );
49 const asset delta_rex_stake = add_to_rex_balance( from, amount, rex_received );
50 runrex(2);
51 update_rex_account( from, asset( 0, core_symbol() ), delta_rex_stake );
52 // dummy action added so that amount of REX tokens purchased shows up in action trace
53 rex_results::buyresult_action buyrex_act( rex_account, std::vector<sysio::permission_level>{ } );
54 buyrex_act.send( rex_received );
55 }
56
57 void system_contract::unstaketorex( const name& owner, const name& receiver, const asset& from_net, const asset& from_cpu )
58 {
59 require_auth( owner );
60
61 check( from_net.symbol == core_symbol() && from_cpu.symbol == core_symbol(), "asset must be core token" );
62 check( (0 <= from_net.amount) && (0 <= from_cpu.amount) && (0 < from_net.amount || 0 < from_cpu.amount),
63 "must unstake a positive amount to buy rex" );
64 check_voting_requirement( owner );
65
66 {
67 del_bandwidth_table dbw_table( get_self(), owner.value );
68 auto del_itr = dbw_table.require_find( receiver.value, "delegated bandwidth record does not exist" );
69 check( from_net.amount <= del_itr->net_weight.amount, "amount exceeds tokens staked for net");
70 check( from_cpu.amount <= del_itr->cpu_weight.amount, "amount exceeds tokens staked for cpu");
71 dbw_table.modify( del_itr, same_payer, [&]( delegated_bandwidth& dbw ) {
72 dbw.net_weight.amount -= from_net.amount;
73 dbw.cpu_weight.amount -= from_cpu.amount;
74 });
75 if ( del_itr->is_empty() ) {
76 dbw_table.erase( del_itr );
77 }
78 }
79
80 update_resource_limits( name(0), receiver, -from_net.amount, -from_cpu.amount );
81
82 const asset payment = from_net + from_cpu;
83 // inline transfer from stake_account to rex_account
84 {
86 transfer_act.send( stake_account, rex_account, payment, "buy REX with staked tokens" );
87 }
88 const asset rex_received = add_to_rex_pool( payment );
89 auto rex_stake_delta = add_to_rex_balance( owner, payment, rex_received );
90 runrex(2);
91 update_rex_account( owner, asset( 0, core_symbol() ), rex_stake_delta - payment, true );
92 // dummy action added so that amount of REX tokens purchased shows up in action trace
93 rex_results::buyresult_action buyrex_act( rex_account, std::vector<sysio::permission_level>{ } );
94 buyrex_act.send( rex_received );
95 }
96
97 void system_contract::sellrex( const name& from, const asset& rex )
98 {
99 require_auth( from );
100
101 runrex(2);
102
103 auto bitr = _rexbalance.require_find( from.value, "user must first buyrex" );
104 check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol,
105 "asset must be a positive amount of (REX, 4)" );
106 process_rex_maturities( bitr );
107 check( rex.amount <= bitr->matured_rex, "insufficient available rex" );
108
109 const auto current_order = fill_rex_order( bitr, rex );
110 if ( current_order.success && current_order.proceeds.amount == 0 ) {
111 check( false, "proceeds are negligible" );
112 }
113 asset pending_sell_order = update_rex_account( from, current_order.proceeds, current_order.stake_change );
114 if ( !current_order.success ) {
115 if ( from == "b1"_n ) {
116 check( false, "b1 sellrex orders should not be queued" );
117 }
122 auto oitr = _rexorders.find( from.value );
123 if ( oitr == _rexorders.end() ) {
124 oitr = _rexorders.emplace( from, [&]( auto& order ) {
125 order.owner = from;
126 order.rex_requested = rex;
127 order.is_open = true;
128 order.proceeds = asset( 0, core_symbol() );
129 order.stake_change = asset( 0, core_symbol() );
130 order.order_time = current_time_point();
131 });
132 } else {
133 _rexorders.modify( oitr, same_payer, [&]( auto& order ) {
134 order.rex_requested.amount += rex.amount;
135 });
136 }
137 pending_sell_order.amount = oitr->rex_requested.amount;
138 }
139 check( pending_sell_order.amount <= bitr->matured_rex, "insufficient funds for current and scheduled orders" );
140 // dummy action added so that sell order proceeds show up in action trace
141 if ( current_order.success ) {
142 rex_results::sellresult_action sellrex_act( rex_account, std::vector<sysio::permission_level>{ } );
143 sellrex_act.send( current_order.proceeds );
144 }
145 }
146
148 {
149 require_auth( owner );
150
151 auto itr = _rexorders.require_find( owner.value, "no sellrex order is scheduled" );
152 check( itr->is_open, "sellrex order has been filled and cannot be canceled" );
153 _rexorders.erase( itr );
154 }
155
156 void system_contract::rentcpu( const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund )
157 {
158 require_auth( from );
159
160 rex_cpu_loan_table cpu_loans( get_self(), get_self().value );
161 int64_t rented_tokens = rent_rex( cpu_loans, from, receiver, loan_payment, loan_fund );
162 update_resource_limits( from, receiver, 0, rented_tokens );
163 }
164
165 void system_contract::rentnet( const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund )
166 {
167 require_auth( from );
168
169 rex_net_loan_table net_loans( get_self(), get_self().value );
170 int64_t rented_tokens = rent_rex( net_loans, from, receiver, loan_payment, loan_fund );
171 update_resource_limits( from, receiver, rented_tokens, 0 );
172 }
173
174 void system_contract::fundcpuloan( const name& from, uint64_t loan_num, const asset& payment )
175 {
176 require_auth( from );
177
178 rex_cpu_loan_table cpu_loans( get_self(), get_self().value );
179 fund_rex_loan( cpu_loans, from, loan_num, payment );
180 }
181
182 void system_contract::fundnetloan( const name& from, uint64_t loan_num, const asset& payment )
183 {
184 require_auth( from );
185
186 rex_net_loan_table net_loans( get_self(), get_self().value );
187 fund_rex_loan( net_loans, from, loan_num, payment );
188 }
189
190 void system_contract::defcpuloan( const name& from, uint64_t loan_num, const asset& amount )
191 {
192 require_auth( from );
193
194 rex_cpu_loan_table cpu_loans( get_self(), get_self().value );
195 defund_rex_loan( cpu_loans, from, loan_num, amount );
196 }
197
198 void system_contract::defnetloan( const name& from, uint64_t loan_num, const asset& amount )
199 {
200 require_auth( from );
201
202 rex_net_loan_table net_loans( get_self(), get_self().value );
203 defund_rex_loan( net_loans, from, loan_num, amount );
204 }
205
206 void system_contract::updaterex( const name& owner )
207 {
208 require_auth( owner );
209
210 runrex(2);
211
212 auto itr = _rexbalance.require_find( owner.value, "account has no REX balance" );
213 const asset init_stake = itr->vote_stake;
214
215 auto rexpool_itr = _rexpool.begin();
216 const int64_t total_rex = rexpool_itr->total_rex.amount;
217 const int64_t total_lendable = rexpool_itr->total_lendable.amount;
218 const int64_t rex_balance = itr->rex_balance.amount;
219
220 asset current_stake( 0, core_symbol() );
221 if ( total_rex > 0 ) {
222 current_stake.amount = ( uint128_t(rex_balance) * total_lendable ) / total_rex;
223 }
224 _rexbalance.modify( itr, same_payer, [&]( auto& rb ) {
225 rb.vote_stake = current_stake;
226 });
227
228 update_rex_account( owner, asset( 0, core_symbol() ), current_stake - init_stake, true );
229 process_rex_maturities( itr );
230 }
231
232 void system_contract::setrex( const asset& balance )
233 {
234 require_auth( "sysio"_n );
235
236 check( balance.amount > 0, "balance must be set to have a positive amount" );
237 check( balance.symbol == core_symbol(), "balance symbol must be core symbol" );
238 check( rex_system_initialized(), "rex system is not initialized" );
239 _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& pool ) {
240 pool.total_rent = balance;
241 });
242 }
243
244 void system_contract::rexexec( const name& user, uint16_t max )
245 {
246 require_auth( user );
247
248 runrex( max );
249 }
250
252 {
253 require_auth( owner );
254
255 runrex(2);
256
257 auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" );
258 asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) );
259 consolidate_rex_balance( bitr, rex_in_sell_order );
260 }
261
262 void system_contract::mvtosavings( const name& owner, const asset& rex )
263 {
264 require_auth( owner );
265
266 runrex(2);
267
268 auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" );
269 check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" );
270 const asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) );
271 const int64_t rex_in_savings = read_rex_savings( bitr );
272 check( rex.amount + rex_in_sell_order.amount + rex_in_savings <= bitr->rex_balance.amount,
273 "insufficient REX balance" );
274 process_rex_maturities( bitr );
275 _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) {
276 int64_t moved_rex = 0;
277 while ( !rb.rex_maturities.empty() && moved_rex < rex.amount) {
278 const int64_t d_rex = std::min( rex.amount - moved_rex, rb.rex_maturities.back().second );
279 rb.rex_maturities.back().second -= d_rex;
280 moved_rex += d_rex;
281 if ( rb.rex_maturities.back().second == 0 ) {
282 rb.rex_maturities.pop_back();
283 }
284 }
285 if ( moved_rex < rex.amount ) {
286 const int64_t d_rex = rex.amount - moved_rex;
287 rb.matured_rex -= d_rex;
288 moved_rex += d_rex;
289 check( rex_in_sell_order.amount <= rb.matured_rex, "logic error in mvtosavings" );
290 }
291 check( moved_rex == rex.amount, "programmer error in mvtosavings" );
292 });
293 put_rex_savings( bitr, rex_in_savings + rex.amount );
294 }
295
296 void system_contract::mvfrsavings( const name& owner, const asset& rex )
297 {
298 require_auth( owner );
299
300 runrex(2);
301
302 auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" );
303 check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" );
304 const int64_t rex_in_savings = read_rex_savings( bitr );
305 check( rex.amount <= rex_in_savings, "insufficient REX in savings" );
306 process_rex_maturities( bitr );
307 _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) {
308 const time_point_sec maturity = get_rex_maturity();
309 if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) {
310 rb.rex_maturities.back().second += rex.amount;
311 } else {
312 rb.rex_maturities.emplace_back( pair_time_point_sec_int64 { maturity, rex.amount } );
313 }
314 });
315 put_rex_savings( bitr, rex_in_savings - rex.amount );
316 update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) );
317 }
318
319 void system_contract::closerex( const name& owner )
320 {
321 require_auth( owner );
322
323 if ( rex_system_initialized() )
324 runrex(2);
325
326 update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) );
327
329 {
330 rex_cpu_loan_table cpu_loans( get_self(), get_self().value );
331 auto cpu_idx = cpu_loans.get_index<"byowner"_n>();
332 bool no_outstanding_cpu_loans = ( cpu_idx.find( owner.value ) == cpu_idx.end() );
333
334 rex_net_loan_table net_loans( get_self(), get_self().value );
335 auto net_idx = net_loans.get_index<"byowner"_n>();
336 bool no_outstanding_net_loans = ( net_idx.find( owner.value ) == net_idx.end() );
337
338 auto fund_itr = _rexfunds.find( owner.value );
339 bool no_outstanding_rex_fund = ( fund_itr != _rexfunds.end() ) && ( fund_itr->balance.amount == 0 );
340
341 if ( no_outstanding_cpu_loans && no_outstanding_net_loans && no_outstanding_rex_fund ) {
342 _rexfunds.erase( fund_itr );
343 }
344 }
345
347 {
348 auto rex_itr = _rexbalance.find( owner.value );
349 if ( rex_itr != _rexbalance.end() ) {
350 check( rex_itr->rex_balance.amount == 0, "account has remaining REX balance, must sell first");
351 _rexbalance.erase( rex_itr );
352 }
353 }
354 }
355
364 void system_contract::update_resource_limits( const name& from, const name& receiver, int64_t delta_net, int64_t delta_cpu )
365 {
366 if ( delta_cpu == 0 && delta_net == 0 ) { // nothing to update
367 return;
368 }
369
370 user_resources_table totals_tbl( get_self(), receiver.value );
371 auto tot_itr = totals_tbl.find( receiver.value );
372 if ( tot_itr == totals_tbl.end() ) {
373 check( 0 <= delta_net && 0 <= delta_cpu, "logic error, should not occur");
374 tot_itr = totals_tbl.emplace( from, [&]( auto& tot ) {
375 tot.owner = receiver;
376 tot.net_weight = asset( delta_net, core_symbol() );
377 tot.cpu_weight = asset( delta_cpu, core_symbol() );
378 });
379 } else {
380 totals_tbl.modify( tot_itr, same_payer, [&]( auto& tot ) {
381 tot.net_weight.amount += delta_net;
382 tot.cpu_weight.amount += delta_cpu;
383 });
384 }
385 check( 0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth" );
386 check( 0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth" );
387
388 {
389 bool net_managed = false;
390 bool cpu_managed = false;
391
392 auto voter_itr = _voters.find( receiver.value );
393 if( voter_itr != _voters.end() ) {
394 net_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::net_managed );
395 cpu_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::cpu_managed );
396 }
397
398 if( !(net_managed && cpu_managed) ) {
399 int64_t ram_bytes = 0, net = 0, cpu = 0;
400 get_resource_limits( receiver, ram_bytes, net, cpu );
401
402 set_resource_limits( receiver,
403 ram_bytes,
404 net_managed ? net : tot_itr->net_weight.amount,
405 cpu_managed ? cpu : tot_itr->cpu_weight.amount );
406 }
407 }
408
409 if ( tot_itr->is_empty() ) {
410 totals_tbl.erase( tot_itr );
411 }
412 }
413
421 void system_contract::check_voting_requirement( const name& owner, const char* error_msg )const
422 {
423 auto vitr = _voters.find( owner.value );
424 check( vitr != _voters.end() && ( vitr->proxy || 21 <= vitr->producers.size() ), error_msg );
425 }
426
433 bool system_contract::rex_loans_available()const
434 {
435 if ( !rex_available() ) {
436 return false;
437 } else {
438 if ( _rexorders.begin() == _rexorders.end() ) {
439 return true; // no outstanding sellrex orders
440 } else {
441 auto idx = _rexorders.get_index<"bytime"_n>();
442 return !idx.begin()->is_open; // no outstanding unfilled sellrex orders
443 }
444 }
445 }
446
454 void system_contract::add_loan_to_rex_pool( const asset& payment, int64_t rented_tokens, bool new_loan )
455 {
456 add_to_rex_return_pool( payment );
457 _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& rt ) {
458 // add payment to total_rent
459 rt.total_rent.amount += payment.amount;
460 // move rented_tokens from total_unlent to total_lent
461 rt.total_unlent.amount -= rented_tokens;
462 rt.total_lent.amount += rented_tokens;
463 // increment loan_num if a new loan is being created
464 if ( new_loan ) {
465 rt.loan_num++;
466 }
467 });
468 }
469
475 void system_contract::remove_loan_from_rex_pool( const rex_loan& loan )
476 {
477 const auto& pool = _rexpool.begin();
478 const int64_t delta_total_rent = exchange_state::get_bancor_output( pool->total_unlent.amount,
479 pool->total_rent.amount,
480 loan.total_staked.amount );
481 _rexpool.modify( pool, same_payer, [&]( auto& rt ) {
482 // deduct calculated delta_total_rent from total_rent
483 rt.total_rent.amount -= delta_total_rent;
484 // move rented tokens from total_lent to total_unlent
485 rt.total_unlent.amount += loan.total_staked.amount;
486 rt.total_lent.amount -= loan.total_staked.amount;
487 rt.total_lendable.amount = rt.total_unlent.amount + rt.total_lent.amount;
488 });
489 }
490
494 template <typename Index, typename Iterator>
495 int64_t system_contract::update_renewed_loan( Index& idx, const Iterator& itr, int64_t rented_tokens )
496 {
497 int64_t delta_stake = rented_tokens - itr->total_staked.amount;
498 idx.modify ( itr, same_payer, [&]( auto& loan ) {
499 loan.total_staked.amount = rented_tokens;
500 loan.expiration += sysio::days(30);
501 loan.balance.amount -= loan.payment.amount;
502 });
503 return delta_stake;
504 }
505
511 void system_contract::runrex( uint16_t max )
512 {
513 check( rex_system_initialized(), "rex system not initialized yet" );
514
515 update_rex_pool();
516
517 const auto& pool = _rexpool.begin();
518
519 auto process_expired_loan = [&]( auto& idx, const auto& itr ) -> std::pair<bool, int64_t> {
521 remove_loan_from_rex_pool( *itr );
522 bool delete_loan = false;
523 int64_t delta_stake = 0;
525 int64_t rented_tokens = exchange_state::get_bancor_output( pool->total_rent.amount,
526 pool->total_unlent.amount,
527 itr->payment.amount );
529 bool renew_loan = itr->payment <= itr->balance
530 && itr->payment.amount < rented_tokens
531 && rex_loans_available();
532 if ( renew_loan ) {
534 add_loan_to_rex_pool( itr->payment, rented_tokens, false );
536 delta_stake = update_renewed_loan( idx, itr, rented_tokens );
537 } else {
538 delete_loan = true;
539 delta_stake = -( itr->total_staked.amount );
541 if ( itr->balance.amount > 0 ) {
542 transfer_to_fund( itr->from, itr->balance );
543 }
544 }
545
546 return { delete_loan, delta_stake };
547 };
548
550 if ( pool->namebid_proceeds.amount > 0 ) {
551 channel_to_rex( names_account, pool->namebid_proceeds );
552 _rexpool.modify( pool, same_payer, [&]( auto& rt ) {
553 rt.namebid_proceeds.amount = 0;
554 });
555 }
556
558 {
559 rex_cpu_loan_table cpu_loans( get_self(), get_self().value );
560 auto cpu_idx = cpu_loans.get_index<"byexpr"_n>();
561 for ( uint16_t i = 0; i < max; ++i ) {
562 auto itr = cpu_idx.begin();
563 if ( itr == cpu_idx.end() || itr->expiration > current_time_point() ) break;
564
565 auto result = process_expired_loan( cpu_idx, itr );
566 if ( result.second != 0 )
567 update_resource_limits( itr->from, itr->receiver, 0, result.second );
568
569 if ( result.first )
570 cpu_idx.erase( itr );
571 }
572 }
573
575 {
576 rex_net_loan_table net_loans( get_self(), get_self().value );
577 auto net_idx = net_loans.get_index<"byexpr"_n>();
578 for ( uint16_t i = 0; i < max; ++i ) {
579 auto itr = net_idx.begin();
580 if ( itr == net_idx.end() || itr->expiration > current_time_point() ) break;
581
582 auto result = process_expired_loan( net_idx, itr );
583 if ( result.second != 0 )
584 update_resource_limits( itr->from, itr->receiver, result.second, 0 );
585
586 if ( result.first )
587 net_idx.erase( itr );
588 }
589 }
590
592 if ( _rexorders.begin() != _rexorders.end() ) {
593 auto idx = _rexorders.get_index<"bytime"_n>();
594 auto oitr = idx.begin();
595 for ( uint16_t i = 0; i < max; ++i ) {
596 if ( oitr == idx.end() || !oitr->is_open ) break;
597 auto next = oitr;
598 ++next;
599 auto bitr = _rexbalance.find( oitr->owner.value );
600 if ( bitr != _rexbalance.end() ) { // should always be true
601 auto result = fill_rex_order( bitr, oitr->rex_requested );
602 if ( result.success ) {
603 const name order_owner = oitr->owner;
604 idx.modify( oitr, same_payer, [&]( auto& order ) {
605 order.proceeds.amount = result.proceeds.amount;
606 order.stake_change.amount = result.stake_change.amount;
607 order.close();
608 });
610 rex_results::orderresult_action order_act( rex_account, std::vector<sysio::permission_level>{ } );
611 order_act.send( order_owner, result.proceeds );
612 }
613 }
614 oitr = next;
615 }
616 }
617
618 }
619
623 void system_contract::update_rex_pool()
624 {
625 auto get_elapsed_intervals = [&]( const time_point_sec& t1, const time_point_sec& t0 ) -> uint32_t {
626 return ( t1.sec_since_epoch() - t0.sec_since_epoch() ) / rex_return_pool::dist_interval;
627 };
628
629 const time_point_sec ct = current_time_point();
630 const uint32_t cts = ct.sec_since_epoch();
631 const time_point_sec effective_time{cts - cts % rex_return_pool::dist_interval};
632
633 const auto ret_pool_elem = _rexretpool.begin();
634 const auto ret_buckets_elem = _rexretbuckets.begin();
635
636 if ( ret_pool_elem == _rexretpool.end() || effective_time <= ret_pool_elem->last_dist_time ) {
637 return;
638 }
639
640 const int64_t current_rate = ret_pool_elem->current_rate_of_increase;
641 const uint32_t elapsed_intervals = get_elapsed_intervals( effective_time, ret_pool_elem->last_dist_time );
642 int64_t change_estimate = current_rate * elapsed_intervals;
643
644 {
645 const bool new_return_bucket = ret_pool_elem->pending_bucket_time <= effective_time;
646 int64_t new_bucket_rate = 0;
647 time_point_sec new_bucket_time = time_point_sec::min();
648 _rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) {
649 if ( new_return_bucket ) {
650 int64_t remainder = rp.pending_bucket_proceeds % rex_return_pool::total_intervals;
651 new_bucket_rate = ( rp.pending_bucket_proceeds - remainder ) / rex_return_pool::total_intervals;
652 new_bucket_time = rp.pending_bucket_time;
653 rp.current_rate_of_increase += new_bucket_rate;
654 change_estimate += remainder + new_bucket_rate * get_elapsed_intervals( effective_time, rp.pending_bucket_time );
655 rp.pending_bucket_proceeds = 0;
656 rp.pending_bucket_time = time_point_sec::maximum();
657 if ( new_bucket_time < rp.oldest_bucket_time ) {
658 rp.oldest_bucket_time = new_bucket_time;
659 }
660 }
661 rp.proceeds -= change_estimate;
662 rp.last_dist_time = effective_time;
663 });
664
665 if ( new_return_bucket ) {
666 _rexretbuckets.modify( ret_buckets_elem, same_payer, [&]( auto& rb ) {
667 auto iter = std::lower_bound(rb.return_buckets.begin(), rb.return_buckets.end(), new_bucket_time, [](const pair_time_point_sec_int64& bucket, time_point_sec first) {
668 return bucket.first < first;
669 });
670 if ((iter != rb.return_buckets.end()) && (iter->first == new_bucket_time)) {
671 iter->second = new_bucket_rate;
672 } else {
673 rb.return_buckets.insert(iter, pair_time_point_sec_int64{new_bucket_time, new_bucket_rate});
674 }
675 });
676 }
677 }
678
679 const time_point_sec time_threshold = effective_time - seconds(rex_return_pool::total_intervals * rex_return_pool::dist_interval);
680 if ( ret_pool_elem->oldest_bucket_time <= time_threshold ) {
681 int64_t expired_rate = 0;
682 int64_t surplus = 0;
683 _rexretbuckets.modify( ret_buckets_elem, same_payer, [&]( auto& rb ) {
684 auto& return_buckets = rb.return_buckets;
685 auto iter = return_buckets.begin();
686 for (; iter != return_buckets.end() && iter->first <= time_threshold; ++iter) {
687 const uint32_t overtime = get_elapsed_intervals( effective_time,
688 iter->first + seconds(rex_return_pool::total_intervals * rex_return_pool::dist_interval) );
689 surplus += iter->second * overtime;
690 expired_rate += iter->second;
691 }
692 return_buckets.erase(return_buckets.begin(), iter);
693 });
694
695 _rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) {
696 if ( !ret_buckets_elem->return_buckets.empty() ) {
697 rp.oldest_bucket_time = ret_buckets_elem->return_buckets.begin()->first;
698 } else {
699 rp.oldest_bucket_time = time_point_sec::min();
700 }
701 if ( expired_rate > 0) {
702 rp.current_rate_of_increase -= expired_rate;
703 }
704 if ( surplus > 0 ) {
705 change_estimate -= surplus;
706 rp.proceeds += surplus;
707 }
708 });
709 }
710
711 if ( change_estimate > 0 && ret_pool_elem->proceeds < 0 ) {
712 _rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) {
713 change_estimate += rp.proceeds;
714 rp.proceeds = 0;
715 });
716 }
717
718 if ( change_estimate > 0 ) {
719 _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& pool ) {
720 pool.total_unlent.amount += change_estimate;
721 pool.total_lendable = pool.total_unlent + pool.total_lent;
722 });
723 }
724 }
725
726 template <typename T>
727 int64_t system_contract::rent_rex( T& table, const name& from, const name& receiver, const asset& payment, const asset& fund )
728 {
729 runrex(2);
730
731 check( rex_loans_available(), "rex loans are currently not available" );
732 check( payment.symbol == core_symbol() && fund.symbol == core_symbol(), "must use core token" );
733 check( 0 < payment.amount && 0 <= fund.amount, "must use positive asset amount" );
734
735 transfer_from_fund( from, payment + fund );
736
737 const auto& pool = _rexpool.begin();
738
739 int64_t rented_tokens = exchange_state::get_bancor_output( pool->total_rent.amount,
740 pool->total_unlent.amount,
741 payment.amount );
742 check( payment.amount < rented_tokens, "loan price does not favor renting" );
743 add_loan_to_rex_pool( payment, rented_tokens, true );
744
745 table.emplace( from, [&]( auto& c ) {
746 c.from = from;
747 c.receiver = receiver;
748 c.payment = payment;
749 c.balance = fund;
750 c.total_staked = asset( rented_tokens, core_symbol() );
751 c.expiration = current_time_point() + sysio::days(30);
752 c.loan_num = pool->loan_num;
753 });
754
755 rex_results::rentresult_action rentresult_act{ rex_account, std::vector<sysio::permission_level>{ } };
756 rentresult_act.send( asset{ rented_tokens, core_symbol() } );
757 return rented_tokens;
758 }
759
776 rex_order_outcome system_contract::fill_rex_order( const rex_balance_table::const_iterator& bitr, const asset& rex )
777 {
778 auto rexpool_itr = _rexpool.begin();
779 const int64_t S0 = rexpool_itr->total_lendable.amount;
780 const int64_t R0 = rexpool_itr->total_rex.amount;
781 const int64_t p = (uint128_t(rex.amount) * S0) / R0;
782 const int64_t R1 = R0 - rex.amount;
783 const int64_t S1 = S0 - p;
784 asset proceeds( p, core_symbol() );
785 asset stake_change( 0, core_symbol() );
786 bool success = false;
787
788 const int64_t unlent_lower_bound = rexpool_itr->total_lent.amount / 10;
789 const int64_t available_unlent = rexpool_itr->total_unlent.amount - unlent_lower_bound; // available_unlent <= 0 is possible
790 if ( proceeds.amount <= available_unlent ) {
791 const int64_t init_vote_stake_amount = bitr->vote_stake.amount;
792 const int64_t current_stake_value = ( uint128_t(bitr->rex_balance.amount) * S0 ) / R0;
793 _rexpool.modify( rexpool_itr, same_payer, [&]( auto& rt ) {
794 rt.total_rex.amount = R1;
795 rt.total_lendable.amount = S1;
796 rt.total_unlent.amount = rt.total_lendable.amount - rt.total_lent.amount;
797 });
798 _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) {
799 rb.vote_stake.amount = current_stake_value - proceeds.amount;
800 rb.rex_balance.amount -= rex.amount;
801 rb.matured_rex -= rex.amount;
802 });
803 stake_change.amount = bitr->vote_stake.amount - init_vote_stake_amount;
804 success = true;
805 } else {
806 proceeds.amount = 0;
807 }
808
809 return { success, proceeds, stake_change };
810 }
811
812 template <typename T>
813 void system_contract::fund_rex_loan( T& table, const name& from, uint64_t loan_num, const asset& payment )
814 {
815 check( payment.symbol == core_symbol(), "must use core token" );
816 transfer_from_fund( from, payment );
817 auto itr = table.require_find( loan_num, "loan not found" );
818 check( itr->from == from, "user must be loan creator" );
819 check( itr->expiration > current_time_point(), "loan has already expired" );
820 table.modify( itr, same_payer, [&]( auto& loan ) {
821 loan.balance.amount += payment.amount;
822 });
823 }
824
825 template <typename T>
826 void system_contract::defund_rex_loan( T& table, const name& from, uint64_t loan_num, const asset& amount )
827 {
828 check( amount.symbol == core_symbol(), "must use core token" );
829 auto itr = table.require_find( loan_num, "loan not found" );
830 check( itr->from == from, "user must be loan creator" );
831 check( itr->expiration > current_time_point(), "loan has already expired" );
832 check( itr->balance >= amount, "insufficent loan balance" );
833 table.modify( itr, same_payer, [&]( auto& loan ) {
834 loan.balance.amount -= amount.amount;
835 });
836 transfer_to_fund( from, amount );
837 }
838
847 void system_contract::transfer_from_fund( const name& owner, const asset& amount )
848 {
849 check( 0 < amount.amount && amount.symbol == core_symbol(), "must transfer positive amount from REX fund" );
850 auto itr = _rexfunds.require_find( owner.value, "must deposit to REX fund first" );
851 check( amount <= itr->balance, "insufficient funds" );
852 _rexfunds.modify( itr, same_payer, [&]( auto& fund ) {
853 fund.balance.amount -= amount.amount;
854 });
855 }
856
863 void system_contract::transfer_to_fund( const name& owner, const asset& amount )
864 {
865 check( 0 < amount.amount && amount.symbol == core_symbol(), "must transfer positive amount to REX fund" );
866 auto itr = _rexfunds.find( owner.value );
867 if ( itr == _rexfunds.end() ) {
868 _rexfunds.emplace( owner, [&]( auto& fund ) {
869 fund.owner = owner;
870 fund.balance = amount;
871 });
872 } else {
873 _rexfunds.modify( itr, same_payer, [&]( auto& fund ) {
874 fund.balance.amount += amount.amount;
875 });
876 }
877 }
878
894 asset system_contract::update_rex_account( const name& owner, const asset& proceeds, const asset& delta_stake, bool force_vote_update )
895 {
896 asset to_fund( proceeds );
897 asset to_stake( delta_stake );
898 asset rex_in_sell_order( 0, rex_symbol );
899 auto itr = _rexorders.find( owner.value );
900 if ( itr != _rexorders.end() ) {
901 if ( itr->is_open ) {
902 rex_in_sell_order.amount = itr->rex_requested.amount;
903 } else {
904 to_fund.amount += itr->proceeds.amount;
905 to_stake.amount += itr->stake_change.amount;
906 _rexorders.erase( itr );
907 }
908 }
909
910 if ( to_fund.amount > 0 )
911 transfer_to_fund( owner, to_fund );
912 if ( force_vote_update || to_stake.amount != 0 )
913 update_voting_power( owner, to_stake );
914
915 return rex_in_sell_order;
916 }
917
925 void system_contract::channel_to_rex( const name& from, const asset& amount, bool required )
926 {
927#if CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX
928 if ( rex_available() ) {
929 add_to_rex_return_pool( amount );
930 // inline transfer to rex_account
931 token::transfer_action transfer_act{ token_account, { from, active_permission } };
932 transfer_act.send( from, rex_account, amount,
933 std::string("transfer from ") + from.to_string() + " to sysio.rex" );
934 return;
935 }
936#endif
937 sysio::check( !required, "can't channel fees to rex" );
938 }
939
945 void system_contract::channel_namebid_to_rex( const int64_t highest_bid )
946 {
947#if CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX
948 if ( rex_available() ) {
949 _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& rp ) {
950 rp.namebid_proceeds.amount += highest_bid;
951 });
952 }
953#endif
954 }
955
962 time_point_sec system_contract::get_rex_maturity()
963 {
964 const uint32_t num_of_maturity_buckets = 5;
965 static const uint32_t now = current_time_point().sec_since_epoch();
966 static const uint32_t r = now % seconds_per_day;
967 static const time_point_sec rms{ now - r + num_of_maturity_buckets * seconds_per_day };
968 return rms;
969 }
970
976 void system_contract::process_rex_maturities( const rex_balance_table::const_iterator& bitr )
977 {
978 const time_point_sec now = current_time_point();
979 _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) {
980 while ( !rb.rex_maturities.empty() && rb.rex_maturities.front().first <= now ) {
981 rb.matured_rex += rb.rex_maturities.front().second;
982 rb.rex_maturities.erase(rb.rex_maturities.begin());
983 }
984 });
985 }
986
993 void system_contract::consolidate_rex_balance( const rex_balance_table::const_iterator& bitr,
994 const asset& rex_in_sell_order )
995 {
996 const int64_t rex_in_savings = read_rex_savings( bitr );
997 _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) {
998 int64_t total = rb.matured_rex - rex_in_sell_order.amount;
999 rb.matured_rex = rex_in_sell_order.amount;
1000 while ( !rb.rex_maturities.empty() ) {
1001 total += rb.rex_maturities.front().second;
1002 rb.rex_maturities.erase(rb.rex_maturities.begin());
1003 }
1004 if ( total > 0 ) {
1005 rb.rex_maturities.emplace_back( pair_time_point_sec_int64{ get_rex_maturity(), total } );
1006 }
1007 });
1008 put_rex_savings( bitr, rex_in_savings );
1009 }
1010
1018 asset system_contract::add_to_rex_pool( const asset& payment )
1019 {
1028 const int64_t rex_ratio = 10000;
1029 const asset init_total_rent( 20'000'0000, core_symbol() );
1030 asset rex_received( 0, rex_symbol );
1031 auto itr = _rexpool.begin();
1032 if ( !rex_system_initialized() ) {
1034 _rexpool.emplace( get_self(), [&]( auto& rp ) {
1035 rex_received.amount = payment.amount * rex_ratio;
1036 rp.total_lendable = payment;
1037 rp.total_lent = asset( 0, core_symbol() );
1038 rp.total_unlent = rp.total_lendable - rp.total_lent;
1039 rp.total_rent = init_total_rent;
1040 rp.total_rex = rex_received;
1041 rp.namebid_proceeds = asset( 0, core_symbol() );
1042 });
1043 } else if ( !rex_available() ) {
1044 _rexpool.modify( itr, same_payer, [&]( auto& rp ) {
1045 rex_received.amount = payment.amount * rex_ratio;
1046 rp.total_lendable.amount = payment.amount;
1047 rp.total_lent.amount = 0;
1048 rp.total_unlent.amount = rp.total_lendable.amount - rp.total_lent.amount;
1049 rp.total_rent.amount = init_total_rent.amount;
1050 rp.total_rex.amount = rex_received.amount;
1051 });
1052 } else {
1054 check( itr->total_lendable.amount > 0, "lendable REX pool is empty" );
1055 const int64_t S0 = itr->total_lendable.amount;
1056 const int64_t S1 = S0 + payment.amount;
1057 const int64_t R0 = itr->total_rex.amount;
1058 const int64_t R1 = (uint128_t(S1) * R0) / S0;
1059 rex_received.amount = R1 - R0;
1060 _rexpool.modify( itr, same_payer, [&]( auto& rp ) {
1061 rp.total_lendable.amount = S1;
1062 rp.total_rex.amount = R1;
1063 rp.total_unlent.amount = rp.total_lendable.amount - rp.total_lent.amount;
1064 check( rp.total_unlent.amount >= 0, "programmer error, this should never go negative" );
1065 });
1066 }
1067
1068 return rex_received;
1069 }
1070
1076 void system_contract::add_to_rex_return_pool( const asset& fee )
1077 {
1078 update_rex_pool();
1079 if ( fee.amount <= 0 ) {
1080 return;
1081 }
1082
1083 const time_point_sec ct = current_time_point();
1084 const uint32_t cts = ct.sec_since_epoch();
1085 const uint32_t bucket_interval = rex_return_pool::hours_per_bucket * seconds_per_hour;
1086 const time_point_sec effective_time{cts - cts % bucket_interval + bucket_interval};
1087 const auto return_pool_elem = _rexretpool.begin();
1088 if ( return_pool_elem == _rexretpool.end() ) {
1089 _rexretpool.emplace( get_self(), [&]( auto& rp ) {
1090 rp.last_dist_time = effective_time;
1091 rp.pending_bucket_proceeds = fee.amount;
1092 rp.pending_bucket_time = effective_time;
1093 rp.proceeds = fee.amount;
1094 });
1095 _rexretbuckets.emplace( get_self(), [&]( auto& rb ) { } );
1096 } else {
1097 _rexretpool.modify( return_pool_elem, same_payer, [&]( auto& rp ) {
1098 rp.pending_bucket_proceeds += fee.amount;
1099 rp.proceeds += fee.amount;
1100 if ( rp.pending_bucket_time == time_point_sec::maximum() ) {
1101 rp.pending_bucket_time = effective_time;
1102 }
1103 });
1104 }
1105 }
1106
1116 asset system_contract::add_to_rex_balance( const name& owner, const asset& payment, const asset& rex_received )
1117 {
1118 asset init_rex_stake( 0, core_symbol() );
1119 asset current_rex_stake( 0, core_symbol() );
1120 auto bitr = _rexbalance.find( owner.value );
1121 if ( bitr == _rexbalance.end() ) {
1122 bitr = _rexbalance.emplace( owner, [&]( auto& rb ) {
1123 rb.owner = owner;
1124 rb.vote_stake = payment;
1125 rb.rex_balance = rex_received;
1126 });
1127 current_rex_stake.amount = payment.amount;
1128 } else {
1129 init_rex_stake.amount = bitr->vote_stake.amount;
1130 _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) {
1131 rb.rex_balance.amount += rex_received.amount;
1132 rb.vote_stake.amount = ( uint128_t(rb.rex_balance.amount) * _rexpool.begin()->total_lendable.amount )
1133 / _rexpool.begin()->total_rex.amount;
1134 });
1135 current_rex_stake.amount = bitr->vote_stake.amount;
1136 }
1137
1138 const int64_t rex_in_savings = read_rex_savings( bitr );
1139 process_rex_maturities( bitr );
1140 _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) {
1141 const time_point_sec maturity = get_rex_maturity();
1142 if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) {
1143 rb.rex_maturities.back().second += rex_received.amount;
1144 } else {
1145 rb.rex_maturities.emplace_back( pair_time_point_sec_int64 { maturity, rex_received.amount } );
1146 }
1147 });
1148 put_rex_savings( bitr, rex_in_savings );
1149 return current_rex_stake - init_rex_stake;
1150 }
1151
1163 int64_t system_contract::read_rex_savings( const rex_balance_table::const_iterator& bitr )
1164 {
1165 int64_t rex_in_savings = 0;
1166 static const time_point_sec end_of_days = time_point_sec::maximum();
1167 if ( !bitr->rex_maturities.empty() && bitr->rex_maturities.back().first == end_of_days ) {
1168 _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) {
1169 rex_in_savings = rb.rex_maturities.back().second;
1170 rb.rex_maturities.pop_back();
1171 });
1172 }
1173 return rex_in_savings;
1174 }
1175
1182 void system_contract::put_rex_savings( const rex_balance_table::const_iterator& bitr, int64_t rex )
1183 {
1184 if ( rex == 0 ) return;
1185 static const time_point_sec end_of_days = time_point_sec::maximum();
1186 _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) {
1187 if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == end_of_days ) {
1188 rb.rex_maturities.back().second += rex;
1189 } else {
1190 rb.rex_maturities.emplace_back( pair_time_point_sec_int64{ end_of_days, rex } );
1191 }
1192 });
1193 }
1194
1200 void system_contract::update_rex_stake( const name& voter )
1201 {
1202 int64_t delta_stake = 0;
1203 auto bitr = _rexbalance.find( voter.value );
1204 if ( bitr != _rexbalance.end() && rex_available() ) {
1205 asset init_vote_stake = bitr->vote_stake;
1206 asset current_vote_stake( 0, core_symbol() );
1207 current_vote_stake.amount = ( uint128_t(bitr->rex_balance.amount) * _rexpool.begin()->total_lendable.amount )
1208 / _rexpool.begin()->total_rex.amount;
1209 _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) {
1210 rb.vote_stake.amount = current_vote_stake.amount;
1211 });
1212 delta_stake = current_vote_stake.amount - init_vote_stake.amount;
1213 }
1214
1215 if ( delta_stake != 0 ) {
1216 auto vitr = _voters.find( voter.value );
1217 if ( vitr != _voters.end() ) {
1218 _voters.modify( vitr, same_payer, [&]( auto& vinfo ) {
1219 vinfo.staked += delta_stake;
1220 });
1221 }
1222 }
1223 }
1224
1225};
const mie::Vuint & p
Definition bn.cpp:27
const mie::Vuint & r
Definition bn.cpp:28
std::string name
action_wrapper<"sellresult"_n, &rex_results::sellresult > sellresult_action
action_wrapper<"rentresult"_n, &rex_results::rentresult > rentresult_action
action_wrapper<"orderresult"_n, &rex_results::orderresult > orderresult_action
action_wrapper<"buyresult"_n, &rex_results::buyresult > buyresult_action
sysio::action_wrapper<"transfer"_n, &token::transfer > transfer_action
void mvfrsavings(const name &owner, const asset &rex)
Definition rex.cpp:296
void mvtosavings(const name &owner, const asset &rex)
Definition rex.cpp:262
void deposit(const name &owner, const asset &amount)
Definition rex.cpp:11
void setrex(const asset &balance)
Definition rex.cpp:232
void fundcpuloan(const name &from, uint64_t loan_num, const asset &payment)
Definition rex.cpp:174
void rentnet(const name &from, const name &receiver, const asset &loan_payment, const asset &loan_fund)
Definition rex.cpp:165
void unstaketorex(const name &owner, const name &receiver, const asset &from_net, const asset &from_cpu)
Definition rex.cpp:57
void rentcpu(const name &from, const name &receiver, const asset &loan_payment, const asset &loan_fund)
Definition rex.cpp:156
static constexpr sysio::name stake_account
static constexpr sysio::name token_account
void cnclrexorder(const name &owner)
Definition rex.cpp:147
static constexpr sysio::name active_permission
void consolidate(const name &owner)
Definition rex.cpp:251
void updaterex(const name &owner)
Definition rex.cpp:206
void sellrex(const name &from, const asset &rex)
Definition rex.cpp:97
void rexexec(const name &user, uint16_t max)
Definition rex.cpp:244
void buyrex(const name &from, const asset &amount)
Definition rex.cpp:40
void closerex(const name &owner)
Definition rex.cpp:319
static constexpr sysio::name rex_account
void fundnetloan(const name &from, uint64_t loan_num, const asset &payment)
Definition rex.cpp:182
void withdraw(const name &owner, const asset &amount)
Definition rex.cpp:25
void defcpuloan(const name &from, uint64_t loan_num, const asset &amount)
Definition rex.cpp:190
void defnetloan(const name &from, uint64_t loan_num, const asset &amount)
Definition rex.cpp:198
constexpr microseconds seconds(int64_t s)
Definition time.hpp:32
constexpr microseconds days(int64_t d)
Definition time.hpp:36
__uint128_t uint128_t
Definition config.hpp:8
sysio::multi_index< "delband"_n, delegated_bandwidth > del_bandwidth_table
sysio::multi_index< "cpuloan"_n, rex_loan, indexed_by<"byexpr"_n, const_mem_fun< rex_loan, uint64_t, &rex_loan::by_expr > >, indexed_by<"byowner"_n, const_mem_fun< rex_loan, uint64_t, &rex_loan::by_owner > > > rex_cpu_loan_table
sysio::multi_index< "netloan"_n, rex_loan, indexed_by<"byexpr"_n, const_mem_fun< rex_loan, uint64_t, &rex_loan::by_expr > >, indexed_by<"byowner"_n, const_mem_fun< rex_loan, uint64_t, &rex_loan::by_owner > > > rex_net_loan_table
sysio::multi_index< "userres"_n, user_resources > user_resources_table
uint32_t next(octet_iterator &it, octet_iterator end)
Definition checked.h:137
#define value
Definition pkcs11.h:157
#define T(meth, val, expected)
unsigned short uint16_t
Definition stdint.h:125
signed __int64 int64_t
Definition stdint.h:135
unsigned int uint32_t
Definition stdint.h:126
unsigned __int64 uint64_t
Definition stdint.h:136
Immutable except for fc::from_variant.
Definition name.hpp:43