#include <eosio.system/eosio.system.hpp>
#include <eosio.token/eosio.token.hpp>

#include <eosio/crypto.hpp>
#include <eosio/dispatcher.hpp>
#include "../../../../lib/consts.hpp"
#include <cmath>

namespace eosiosystem {

   using eosio::current_time_point;
   using eosio::token;

   system_contract::system_contract( name s, name code, datastream<const char*> ds )
   :native(s,code,ds),
    _voters(get_self(), get_self().value),
    _producers(get_self(), get_self().value),
    _global(get_self(), get_self().value),
    _rammarket(get_self(), get_self().value)
    
   {
      _gstate  = _global.exists() ? _global.get() : get_default_parameters();
   }

   eosio_global_state system_contract::get_default_parameters() {
      eosio_global_state dp;
      get_blockchain_parameters(dp);
      return dp;
   }

   symbol system_contract::core_symbol()const {
      const static auto sym = get_core_symbol( _rammarket );
      return sym;
   }

   system_contract::~system_contract() {
      _global.set( _gstate, get_self() );
   }
     
   /**
    * @brief Устанавливает максимальный размер RAM в системе.
    * Увеличивает количество RAM доступного для продажи на основе изменения максимального размера RAM.
    * @param max_ram_size Новый максимальный размер RAM в байтах
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::setram( uint64_t max_ram_size ) {
      require_auth( get_self() );

      check( _gstate.max_ram_size < max_ram_size, "ram may only be increased" ); /// decreasing ram might result market maker issues
      check( max_ram_size < 1024ll*1024*1024*1024*1024, "ram size is unrealistic" );
      check( max_ram_size > _gstate.total_ram_bytes_reserved, "attempt to set max below reserved" );

      auto delta = int64_t(max_ram_size) - int64_t(_gstate.max_ram_size);
      auto itr = _rammarket.find(ramcore_symbol.raw());

      /**
       *  Increase the amount of ram for sale based upon the change in max ram size.
       */
      _rammarket.modify( itr, same_payer, [&]( auto& m ) {
         m.base.balance.amount += delta;
      });

      _gstate.max_ram_size = max_ram_size;
   }

   /**
    * @brief Обновляет предложение RAM на основе времени с последнего увеличения.
    * Увеличивает количество RAM доступного для продажи на основе времени блоков.
    * @ingroup public_actions
    * @ingroup public_system_actions

    */
   void system_contract::update_ram_supply() {
      auto cbt = eosio::current_block_time();

      if( cbt <= _gstate.last_ram_increase ) return;

      auto itr = _rammarket.find(ramcore_symbol.raw());
      auto new_ram = (cbt.slot - _gstate.last_ram_increase.slot)*_gstate.new_ram_per_block;
      _gstate.max_ram_size += new_ram;

      /**
       *  Increase the amount of ram for sale based upon the change in max ram size.
       */
      _rammarket.modify( itr, same_payer, [&]( auto& m ) {
         m.base.balance.amount += new_ram;
      });
      _gstate.last_ram_increase = cbt;
   }

   /**
    * @brief Устанавливает скорость увеличения RAM в байтах за блок.
    * @param bytes_per_block Количество байт RAM, добавляемых за блок
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::setramrate( uint16_t bytes_per_block ) {
      require_auth( get_self() );

      update_ram_supply();
      _gstate.new_ram_per_block = bytes_per_block;
   }

#ifdef SYSTEM_BLOCKCHAIN_PARAMETERS
   extern "C" [[eosio::wasm_import]] void set_parameters_packed(const void*, size_t);
#endif

   /**
    * @brief Устанавливает параметры блокчейна.
    * Обновляет глобальные параметры блокчейна, включая лимиты блоков, транзакций и авторизаций.
    * @param params Новые параметры блокчейна
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::setparams( const blockchain_parameters_t& params ) {
      require_auth( get_self() );
      (eosio::blockchain_parameters&)(_gstate) = params;
      check( 3 <= _gstate.max_authority_depth, "max_authority_depth should be at least 3" );
#ifndef SYSTEM_BLOCKCHAIN_PARAMETERS
      set_blockchain_parameters( params );
#else
      constexpr size_t param_count = 18;
      // an upper bound on the serialized size
      char buf[1 + sizeof(params) + param_count];
      datastream<char*> stream(buf, sizeof(buf));

      stream << uint8_t(17);
      stream << uint8_t(0) << params.max_block_net_usage
             << uint8_t(1) << params.target_block_net_usage_pct
             << uint8_t(2) << params.max_transaction_net_usage
             << uint8_t(3) << params.base_per_transaction_net_usage
             << uint8_t(4) << params.net_usage_leeway
             << uint8_t(5) << params.context_free_discount_net_usage_num
             << uint8_t(6) << params.context_free_discount_net_usage_den

             << uint8_t(7) << params.max_block_cpu_usage
             << uint8_t(8) << params.target_block_cpu_usage_pct
             << uint8_t(9) << params.max_transaction_cpu_usage
             << uint8_t(10) << params.min_transaction_cpu_usage

             << uint8_t(11) << params.max_transaction_lifetime
             << uint8_t(12) << params.deferred_trx_expiration_window
             << uint8_t(13) << params.max_transaction_delay
             << uint8_t(14) << params.max_inline_action_size
             << uint8_t(15) << params.max_inline_action_depth
             << uint8_t(16) << params.max_authority_depth;
      if(params.max_action_return_value_size)
      {
         stream << uint8_t(17) << params.max_action_return_value_size.value();
         ++buf[0];
      }

      set_parameters_packed(buf, stream.tellp());
#endif
   }

#ifdef SYSTEM_CONFIGURABLE_WASM_LIMITS

   // The limits on contract WebAssembly modules
   struct wasm_parameters
   {
      uint32_t max_mutable_global_bytes;
      uint32_t max_table_elements;
      uint32_t max_section_elements;
      uint32_t max_linear_memory_init;
      uint32_t max_func_local_bytes;
      uint32_t max_nested_structures;
      uint32_t max_symbol_bytes;
      uint32_t max_module_bytes;
      uint32_t max_code_bytes;
      uint32_t max_pages;
      uint32_t max_call_depth;
   };

   static constexpr wasm_parameters default_limits = {
       .max_mutable_global_bytes = 1024,
       .max_table_elements = 1024,
       .max_section_elements = 8192,
       .max_linear_memory_init = 64*1024,
       .max_func_local_bytes = 8192,
       .max_nested_structures = 1024,
       .max_symbol_bytes = 8192,
       .max_module_bytes = 20*1024*1024,
       .max_code_bytes = 20*1024*1024,
       .max_pages = 528,
       .max_call_depth = 251
   };

   static constexpr wasm_parameters high_limits = {
       .max_mutable_global_bytes = 8192,
       .max_table_elements = 8192,
       .max_section_elements = 8192,
       .max_linear_memory_init = 16*64*1024,
       .max_func_local_bytes = 8192,
       .max_nested_structures = 1024,
       .max_symbol_bytes = 8192,
       .max_module_bytes = 20*1024*1024,
       .max_code_bytes = 20*1024*1024,
       .max_pages = 528,
       .max_call_depth = 1024
   };

   extern "C" [[eosio::wasm_import]] void set_wasm_parameters_packed( const void*, size_t );

   void set_wasm_parameters( const wasm_parameters& params )
   {
      char buf[sizeof(uint32_t) + sizeof(params)] = {};
      memcpy(buf + sizeof(uint32_t), &params, sizeof(params));
      set_wasm_parameters_packed( buf, sizeof(buf) );
   }

   /**
    * @brief Устанавливает лимиты WebAssembly.
    * Допустимые параметры: "low", "default" (эквивалентно low) и "high".
    * Значение "high" позволяет развертывать более крупные контракты.
    * @param settings Настройки лимитов WebAssembly
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::wasmcfg( const name& settings )
   {
      require_auth( get_self() );
      if( settings == "default"_n || settings == "low"_n )
      {
         set_wasm_parameters( default_limits );
      }
      else if( settings == "high"_n )
      {
         set_wasm_parameters( high_limits );
      }
      else
      {
         check(false, "Unknown configuration");
      }
   }

#endif

   /**
    * @brief Устанавливает привилегированный статус для аккаунта.
    * Позволяет включить или выключить привилегированный статус для аккаунта.
    * @param account Аккаунт для установки привилегированного статуса
    * @param ispriv 0 для false, > 0 для true
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::setpriv( const name& account, uint8_t ispriv ) {
      require_auth( get_self() );
      set_privileged( account, ispriv );
   }

   /**
    * @brief Устанавливает лимиты ресурсов для аккаунта.
    * Устанавливает абсолютные лимиты RAM, NET и CPU для указанного аккаунта.
    * Поддерживает только аккаунты с неограниченными ресурсами.
    * @param account Имя аккаунта, для которого устанавливаются лимиты ресурсов
    * @param ram Лимит RAM в абсолютных байтах
    * @param net Пропорциональный лимит NET на основе (вес / общий_вес_всех_аккаунтов)
    * @param cpu Пропорциональный лимит CPU на основе (вес / общий_вес_всех_аккаунтов)
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::setalimits( const name& account, int64_t ram, int64_t net, int64_t cpu ) {
      require_auth( get_self() );

      user_resources_table userres( get_self(), account.value );
      auto ritr = userres.find( account.value );
      check( ritr == userres.end(), "only supports unlimited accounts" );

      auto vitr = _voters.find( account.value );
      if( vitr != _voters.end() ) {
         bool ram_managed = has_field( vitr->flags1, voter_info::flags1_fields::ram_managed );
         bool net_managed = has_field( vitr->flags1, voter_info::flags1_fields::net_managed );
         bool cpu_managed = has_field( vitr->flags1, voter_info::flags1_fields::cpu_managed );
         check( !(ram_managed || net_managed || cpu_managed), "cannot use setalimits on an account with managed resources" );
      }

      set_resource_limits( account, ram, net, cpu );
   }

   /**
    * @brief Устанавливает лимиты RAM для аккаунта.
    * Устанавливает лимит RAM в абсолютных байтах для указанного аккаунта.
    * @param account Имя аккаунта, для которого устанавливается лимит ресурсов
    * @param ram_bytes Лимит RAM в абсолютных байтах
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::setacctram( const name& account, const std::optional<int64_t>& ram_bytes ) {
      require_auth( get_self() );

      int64_t current_ram, current_net, current_cpu;
      get_resource_limits( account, current_ram, current_net, current_cpu );

      int64_t ram = 0;

      if( !ram_bytes ) {
         auto vitr = _voters.find( account.value );
         check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::ram_managed ),
                "RAM of account is already unmanaged" );

         user_resources_table userres( get_self(), account.value );
         auto ritr = userres.find( account.value );

         ram = ram_gift_bytes;
         if( ritr != userres.end() ) {
            ram += ritr->ram_bytes;
         }

         _voters.modify( vitr, same_payer, [&]( auto& v ) {
            v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, false );
         });
      } else {
         check( *ram_bytes >= 0, "not allowed to set RAM limit to unlimited" );

         auto vitr = _voters.find( account.value );
         if ( vitr != _voters.end() ) {
            _voters.modify( vitr, same_payer, [&]( auto& v ) {
               v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, true );
            });
         } else {
            _voters.emplace( account, [&]( auto& v ) {
               v.owner  = account;
               v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, true );
            });
         }

         ram = *ram_bytes;
      }

      set_resource_limits( account, ram, current_net, current_cpu );
   }

   /**
    * @brief Устанавливает лимиты NET для аккаунта.
    * Устанавливает пропорциональный лимит NET для указанного аккаунта.
    * @param account Имя аккаунта, для которого устанавливается лимит ресурсов
    * @param net_weight Пропорциональный лимит NET на основе (вес / общий_вес_всех_аккаунтов)
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::setacctnet( const name& account, const std::optional<int64_t>& net_weight ) {
      require_auth( get_self() );

      int64_t current_ram, current_net, current_cpu;
      get_resource_limits( account, current_ram, current_net, current_cpu );

      int64_t net = 0;

      if( !net_weight ) {
         auto vitr = _voters.find( account.value );
         check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::net_managed ),
                "Network bandwidth of account is already unmanaged" );

         user_resources_table userres( get_self(), account.value );
         auto ritr = userres.find( account.value );

         if( ritr != userres.end() ) {
            net = ritr->net_weight.amount;
         }

         _voters.modify( vitr, same_payer, [&]( auto& v ) {
            v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, false );
         });
      } else {
         check( *net_weight >= -1, "invalid value for net_weight" );

         auto vitr = _voters.find( account.value );
         if ( vitr != _voters.end() ) {
            _voters.modify( vitr, same_payer, [&]( auto& v ) {
               v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, true );
            });
         } else {
            _voters.emplace( account, [&]( auto& v ) {
               v.owner  = account;
               v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, true );
            });
         }

         net = *net_weight;
      }

      set_resource_limits( account, current_ram, net, current_cpu );
   }

   /**
    * @brief Устанавливает лимиты CPU для аккаунта.
    * Устанавливает пропорциональный лимит CPU для указанного аккаунта.
    * @param account Имя аккаунта, для которого устанавливается лимит ресурсов
    * @param cpu_weight Пропорциональный лимит CPU на основе (вес / общий_вес_всех_аккаунтов)
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::setacctcpu( const name& account, const std::optional<int64_t>& cpu_weight ) {
      require_auth( get_self() );

      int64_t current_ram, current_net, current_cpu;
      get_resource_limits( account, current_ram, current_net, current_cpu );

      int64_t cpu = 0;

      if( !cpu_weight ) {
         auto vitr = _voters.find( account.value );
         check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::cpu_managed ),
                "CPU bandwidth of account is already unmanaged" );

         user_resources_table userres( get_self(), account.value );
         auto ritr = userres.find( account.value );

         if( ritr != userres.end() ) {
            cpu = ritr->cpu_weight.amount;
         }

         _voters.modify( vitr, same_payer, [&]( auto& v ) {
            v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, false );
         });
      } else {
         check( *cpu_weight >= -1, "invalid value for cpu_weight" );

         auto vitr = _voters.find( account.value );
         if ( vitr != _voters.end() ) {
            _voters.modify( vitr, same_payer, [&]( auto& v ) {
               v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, true );
            });
         } else {
            _voters.emplace( account, [&]( auto& v ) {
               v.owner  = account;
               v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, true );
            });
         }

         cpu = *cpu_weight;
      }

      set_resource_limits( account, current_ram, current_net, cpu );
   }

   /**
    * @brief Активирует протокольную функцию.
    * Активирует протокольную функцию по хешу перед деплоем системного контракта с большим количеством функций.
    * @param feature_digest Хеш протокольной функции для активации
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::activate( const eosio::checksum256& feature_digest ) {
      require_auth( get_self() );
      preactivate_feature( feature_digest );
   }

   /**
    * @brief Удаляет продюсера по имени.
    * Деактивирует продюсера по имени, если не найден - вызывает ошибку.
    * @param producer Аккаунт продюсера для деактивации
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::rmvproducer( const name& producer ) {
      require_auth( get_self() );
      auto prod = _producers.find( producer.value );
      check( prod != _producers.end(), "producer not found" );
      _producers.modify( prod, same_payer, [&](auto& p) {
            p.deactivate();
         });
   }

   /**
    * @brief Обновляет текущую ревизию.
    * Обновляет текущую ревизию. Ревизия должна быть увеличена на 1 по сравнению с текущей.
    * Текущая ревизия не может быть выше 254 и должна быть меньше или равна 1.
    * @param revision Ревизия, которая должна быть увеличена на 1 по сравнению с текущей ревизией
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::updtrevision( uint8_t revision ) {
      require_auth( get_self() );
      check( _gstate.revision < 255, "can not increment revision" ); // prevent wrap around
      check( revision == _gstate.revision + 1, "can only increment revision by one" );
      check( revision <= 1, // set upper bound to greatest revision supported in the code
             "specified revision is not yet supported by the code" );
      _gstate.revision = revision;
   }




   /**
    * @brief Создает новый аккаунт через кооператив.
    * Позволяет кооперативу создать новый аккаунт с указанными владельцем и активными разрешениями.
    * @param coopname Имя кооператива
    * @param new_account_name Имя нового аккаунта
    * @param owner Авторизация владельца
    * @param active Активная авторизация
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p registrator
    */
   void system_contract::createaccnt(const name coopname, const name new_account_name, authority owner, authority active) {
  require_auth(_registrator);
  
  auto core_symbol = system_contract::get_core_symbol();
  eosio::asset register_amount = asset(_register_amount, core_symbol);

  /* компенсируем затраты на регистрацию системным аккаунтом переводом токенов на системный аккаунт
   * Далее в методе newaccount происходит powerup за счет средств системного аккаунта, поэтому, 
   * мы компенсируем его расходы. Это сделано для того, чтобы для пользователя списание за регистрацию
   * пайщика было в AXON, а не в вычислительных ресурсах его POWERuppенного аккаунта кооператива.
   */

  token::transfer_action transfer_act{ token_account, { {coopname, active_permission} } };
  transfer_act.send( coopname, get_self(), register_amount, "Оплата регистрации аккаунта" );


  /* эти движения с внутренним вызовом newaccount необходимы потому что в протоколе EOSIO
   * захардкодено требование наличия авторизации в первом параметре (creator) метода newaccount, а сам список параметров не изменяемый.
   * именно поэтому мы не можем сразу вызвать этот метод из контракта registrator, т.к.
   * нам необходимо разделить регистратора и сам кооператив для списания оплаты, 
   * а передать информацию о кооперативе с его подписью в newaccount мы не можем.
  */

  action(
    permission_level{ get_self(), "active"_n},
    get_self(),
    "newaccount"_n,
    std::make_tuple(get_self(), new_account_name, owner, active)
  ).send();
  
}


   /**
    *  Called after a new account is created. This code enforces resource-limits rules
    *  for new accounts as well as new account naming conventions.
    *
    *  Account names containing '.' symbols must have a suffix equal to the name of the creator.
    *  This allows users who buy a premium name (shorter than 12 characters with no dots) to be the only ones
    *  who can create accounts with the creator's name as a suffix.
    *
    */
void native::newaccount(const name& creator, 
                        const name& new_account_name, 
                        ignore<authority> owner, 
                        ignore<authority> active) {
    
    check(has_auth(_registrator) || has_auth(get_self()), "Недостаточно прав доступа");
    
    name payer = has_auth(_registrator) ? _registrator : get_self();

    if( creator != get_self() ) {
         uint64_t tmp = new_account_name.value >> 4;
         bool has_dot = false;

         for( uint32_t i = 0; i < 12; ++i ) {
           has_dot |= !(tmp & 0x1f);
           tmp >>= 5;
         }

         if( has_dot ) { // or is less than 12 characters
            auto suffix = new_account_name.suffix();
            if( suffix == new_account_name ) {
               name_bid_table bids(get_self(), get_self().value);
               auto current = bids.find( new_account_name.value );
               check( current != bids.end(), "no active bid for name" );
               check( current->high_bidder == creator, "only highest bidder can result" );
               check( current->high_bid < 0, "auction for name is not closed yet" );
               bids.erase( current );
            } else {
               check( creator == suffix, "only suffix may create this account" );
            }
         }
      }

      powerup_state_singleton state_sing{ get_self(), 0 };
      /* если powerup активна, то все регистрации должны автоматически выдавать RAM из числа средств системного аккаунта бессрочно */
      
      if (state_sing.exists()) {
        // вызываем мтеод
        auto core_symbol = system_contract::get_core_symbol();
        eosio::asset register_amount = asset(_register_amount, core_symbol);
        auto state = state_sing.get();

        // здесь мы увеличиваем POWER у аккаунта за счет системы
        // оплату за это увеличения мы списываем в промежуточном методе createaccnt
        system_contract::powerup_action action{ get_self(), { {creator, system_contract::active_permission} } };
        action.send( creator, new_account_name, state.powerup_days, register_amount, true );

      } else { 
        user_resources_table  userres( get_self(), new_account_name.value );

        userres.emplace( new_account_name, [&]( auto& res ) {
          res.owner = new_account_name;
          res.net_weight = asset( 0, system_contract::get_core_symbol() );
          res.cpu_weight = asset( 0, system_contract::get_core_symbol() );
        });

        set_resource_limits( new_account_name, 0, 0, 0 );
      }
}


void native::setabi( const name& acnt, const std::vector<char>& abi,
                        const binary_extension<std::string>& memo ) {
      eosio::multi_index< "abihash"_n, abi_hash >  table(get_self(), get_self().value);
      auto itr = table.find( acnt.value );
      if( itr == table.end() ) {
         table.emplace( acnt, [&]( auto& row ) {
            row.owner = acnt;
            row.hash = eosio::sha256(const_cast<char*>(abi.data()), abi.size());
         });
      } else {
         table.modify( itr, same_payer, [&]( auto& row ) {
            row.hash = eosio::sha256(const_cast<char*>(abi.data()), abi.size());
         });
      }
   }


   void system_contract::changekey(name  account,
                           name  permission,
                           name  parent,
                           authority auth){
      
      require_auth(_registrator);

      eosio::action(eosio::permission_level(account, eosio::name("owner")),
          eosio::name(_system), eosio::name("updateauth"),
          std::tuple(account, permission, parent, auth))
      .send();

   }

  void system_contract::migrate() {
    require_auth(get_self());

    // // Изменяем продолжительность такта эмиссии на 365 дней, если она не равна 365 дням
    // emission_state_singleton emission_state_sing{ get_self(), get_self().value };
    // if (emission_state_sing.exists()) {
    //   auto state = emission_state_sing.get();
    //   const uint64_t year_duration = 365 * 24 * 60 * 60; // 365 дней в секундах

    //   if (state.tact_duration != year_duration) {
    //     state.tact_duration = year_duration;
    //     emission_state_sing.set(state, get_self());
    //   }
    // }

  };

  /**
   * @brief Инжектирует токены из фонда eosio.saving на кооператив.
   * Требует подписи _soviet для выполнения операции.
   * @param coopname Имя кооператива-получателя
   * @param quantity Сумма для перевода
   */
  void system_contract::injection(const name& coopname, const asset& quantity) {
    require_auth(_soviet);

    eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
    eosio::check(quantity.symbol == core_symbol(), "Символ должен соответствовать основному символу");

    // Переводим токены с eosio.saving на кооператив
    eosio::token::transfer_action transfer_act{ token_account, { {_saving_account, active_permission} } };
    transfer_act.send( _saving_account, coopname, quantity, "Конвертация токенов из фонда" );
  }

   /**
    * @brief Инициализирует системный контракт для версии и символа.
    * Действие выполняется успешно только когда:
    * - версия равна 0
    * - символ найден
    * - предложение системных токенов больше 0
    * - системный контракт еще не был инициализирован
    * @param version Версия, должна быть равна 0
    * @param core Системный символ токена
    * @ingroup public_actions
    * @ingroup public_system_actions

    * @note Авторизация требуется от аккаунта: @p eosio.system
    */
   void system_contract::init( uint64_t version, const symbol& core ) {
      require_auth( get_self() );
      check( version == 0, "unsupported version for init action" );

      auto itr = _rammarket.find(ramcore_symbol.raw());
      check( itr == _rammarket.end(), "system contract has already been initialized" );

      auto system_token_supply   = eosio::token::get_supply(token_account, core.code() );
      check( system_token_supply.symbol == core, "specified core symbol does not exist (precision mismatch)" );

      check( system_token_supply.amount > 0, "system token supply must be greater than 0" );
      _rammarket.emplace( get_self(), [&]( auto& m ) {
         m.supply.amount = 100000000000000ll;
         m.supply.symbol = ramcore_symbol;
         m.base.balance.amount = int64_t(_gstate.free_ram());
         m.base.balance.symbol = ram_symbol;
         m.quote.balance.amount = system_token_supply.amount * 10;
         m.quote.balance.symbol = core;
      });


      action(
        permission_level{ get_self(), "active"_n},
        _soviet,
        "init"_n,
        std::make_tuple()
      ).send();


      action(
        permission_level{ get_self(), "active"_n},
        _registrator,
        "init"_n,
        std::make_tuple()
      ).send();
      
   }

    void system_contract::setcode( const name& account, uint8_t vmtype, uint8_t vmversion, const std::vector<char>& code, const binary_extension<std::string>& memo ) {
        // Проверка на наличие имени аккаунта в векторе
        auto it = std::find(contracts_whitelist.begin(), contracts_whitelist.end(), account);
        eosio::check(it != contracts_whitelist.end(), "Только белый список аккаунтов может обновлять свой программный код.");  
    };

} /// eosio.system
