If delegate some cpu form accountnum11 to accountnum12, then I want to undelegate with the below command:

cleos system undelegatebw accountnum11 accountnum12  '0.0000 EOS' '10.0000 EOS'. 

In normal condions, 10 EOS will be given back into accountnum1's balance after 3 days.

But if I continue to execute the following command:

cleos system undelegatebw account1 account2  '0.0000 EOS' '1.0000 EOS'.

Then I get refund_request with cleos get account accountnum11:

"refund_request": {
    "owner": "account1",
    "request_time": "2018-12-11T08:09:44",
    "net_amount": "0.0000 EOS",
    "cpu_amount": "11.0000 EOS"

the request_time and the cpu_amount were updated.

I don't understand, how refund the two undelegatebw transactions?

  • Does it combine two transactions into one to refund?
  • Or just refund one by one, the time is the last refund_request's time?

And I cannot find the description in the document or in code.

I just saw the refund action in elegate_bandwidth.cpp, but I don't know where call the delaying refund action automatically.


When undelegatebw is called, it is updated with the latest information. Here is the undelegatebw method:

void system_contract::undelegatebw( name from, name receiver,
                                       asset unstake_net_quantity, asset unstake_cpu_quantity )
      asset zero_asset( 0, core_symbol() );
      eosio_assert( unstake_cpu_quantity >= zero_asset, "must unstake a positive amount" );
      eosio_assert( unstake_net_quantity >= zero_asset, "must unstake a positive amount" );
      eosio_assert( unstake_cpu_quantity.amount + unstake_net_quantity.amount > 0, "must unstake a positive amount" );
      eosio_assert( _gstate.total_activated_stake >= min_activated_stake,
                    "cannot undelegate bandwidth until the chain is activated (at least 15% of all tokens participate in voting)" );

      changebw( from, receiver, -unstake_net_quantity, -unstake_cpu_quantity, false);
   } // undelegatebw

It calls changebw().

If you resend the undelegatebw, the method will update (modify the table) the request with the current data:

  if( is_delegating_to_self || is_undelegating ) {
            if ( req != refunds_tbl.end() ) { //need to update refund
               refunds_tbl.modify( req, same_payer, [&]( refund_request& r ) {
                  if ( net_balance.amount < 0 || cpu_balance.amount < 0 ) {
                     r.request_time = current_time_point();
                     // here current time snapshot taken
                  r.net_amount -= net_balance;
                  if ( r.net_amount.amount < 0 ) {
                     net_balance = -r.net_amount;
                     r.net_amount.amount = 0;
                  } else {
                     net_balance.amount = 0;
                  r.cpu_amount -= cpu_balance;
                  if ( r.cpu_amount.amount < 0 ){
                     cpu_balance = -r.cpu_amount;
                     r.cpu_amount.amount = 0;
                  } else {
                     cpu_balance.amount = 0;

               eosio_assert( 0 <= req->net_amount.amount, "negative net refund amount" ); //should never happen
               eosio_assert( 0 <= req->cpu_amount.amount, "negative cpu refund amount" ); //should never happen

               if ( req->net_amount.amount == 0 && req->cpu_amount.amount == 0 ) {
                  refunds_tbl.erase( req );
                  need_deferred_trx = false;
               } else {
                  need_deferred_trx = true;
                  // here deferred transactions set to true for logic below

Then it will send the inline action with a hard coded deferred time set on line 31 which is where the delay comes from:

static constexpr uint32_t refund_delay_sec = 3*24*3600;

Inline action:

if ( need_deferred_trx ) { // was set to true above
            eosio::transaction out;
            out.actions.emplace_back( permission_level{from, active_permission},
                                      _self, "refund"_n,
            out.delay_sec = refund_delay_sec;  // Static time delay set
            cancel_deferred( from.value ); // TODO: Remove this line when replacing deferred trxs is fixed
            out.send( from.value, from, true );
         } else {
            cancel_deferred( from.value );

Does it combine two transactions into one to refund?

If you make an undelegate action again in three days since you last undelegate action WITH THE SAME EOS account. EOS system will combine mutiple undelegate action into one and the refund will be get backed into your account after next three days. Because in refund_request table, it only saved one refund request record for only one eos account!!! In refund_request table, the account is primary_key.

Yes, I really saved the RAM resources for eosio. But it is bad for develper!!!

Or just refund one by one, the time is the last refund_request's time?

Yes, it will refund once with a combination of refund requests in threes days. The refund time is the last refund request time plus three days.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.