Изучая смарт-контракты EOS, я заметил, что у каждого разработчика свой стиль программирования и что есть много разных способов делать одни и те же вещи, например, работать со временем или отправлять действия в другие контракты. Эта статья представляет собой дамп некоторых функций библиотеки EOSIO, которые я считаю изящными и которые полезно знать.

1. eosio::same_payer

Первый - это просто постоянное выражение, которое можно использовать при изменении записей многоиндексных таблиц. При использовании eosio::same_payer новая RAM, которая будет использоваться (если есть), выделяется той же учетной записи, которая уже заплатила за запись в таблице.

Использование:

statstable.modify( st, eosio::same_payer, [&]( auto& s ) {
    s.supply += quantity;
});

Он определен в [multi_index.hpp] и представляет собой просто постоянное выражение для пустого name (значение: 0) ""_n или name(0), которое некоторые разработчики до сих пор используют для обозначения того же плательщика.

2. get_first_receiver, get_self()

Два геттера get_self и get_first_receiver, определенные в контрактах.hpp, возвращают часть контекста выполнения запущенного действия. (В EOSIO.CDT 1.6 get_first_receiver был реализован в пользу старого get_code, который теперь устарел.) Метод get_self возвращает контракт, в котором в настоящее время выполняется код, тогда как get_first_receiver возвращает учетную запись, из которой произошло действие. Эти две учетные записи одинаковы, если только не используются уведомления через require_recipient.

Например, при прослушивании уведомлений о действии eosio.token transfer get_self() возвращает учетную запись, в которой развернут ваш контракт, тогда как get_first_receiver() возвращает учетную запись eosio.token. Это связано с тем, что действие инициировано учетной записью, отправившей transfer действие учетной записи eosio.token, которая связана с вашей контрактной учетной записью.

Использование:

[[eosio::on_notify("eosio.token::transfer")]] void cryptoship::transfer(name from, name to, const asset &quantity,
                          string memo) {
  print(get_self()); // cryptoship
  print(get_first_receiver()); // eosio.token
}

3. action_wrapper

Отправка новых действий из кода контракта в другой контракт необходима во многих случаях использования. Это единственный способ, которым контракты могут активно общаться друг с другом. Опять же, есть много способов сделать это, но один из самых элегантных - использовать eosio::action_wrappers. Он создает «шаблон действия» для определенного действия определенного кода смарт-контракта, который затем можно использовать для вызова этого действия.

Первый аргумент - это имя действия, а второй - метод объявления действия.

использование

Заголовок eosio.token определяет оболочки действий для всех своих действий в файле заголовка eosio.token.hpp:

[[eosio::action]]
  void create( name   issuer,
              asset  maximum_supply);

  [[eosio::action]]
  void issue( name to, asset quantity, string memo );

  [[eosio::action]]
  void retire( asset quantity, string memo );

  [[eosio::action]]
  void transfer( name    from,
                name    to,
                asset   quantity,
                string  memo );

  // ...

  using create_action = eosio::action_wrapper<"create"_n, &token::create>;
  using issue_action = eosio::action_wrapper<"issue"_n, &token::issue>;
  using retire_action = eosio::action_wrapper<"retire"_n, &token::retire>;
  using transfer_action = eosio::action_wrapper<"transfer"_n, &token::transfer>;
  // ...

Теперь мы можем отправлять встроенные действия в любой eosio.token контракт, включив этот файл заголовка.

Важно отметить, что необходимо включить только этот файл заголовка с объявлениями. Это означает, что можно легко написать оболочки действий даже для контрактов с закрытым исходным кодом с неизвестными деталями реализации. Нужно написать только декларацию, подпись действия, которую можно получить из ABI.

Дополнительные подключаемые каталоги для файлов заголовков включаются с помощью флага -I eosio-cpp.

После включения файла заголовка действие встроенной передачи отправляется следующим образом:

#include <eosio_token/include/eosio_token.hpp>

// can specify the contract to send the action to as first argument
token::transfer_action payout("eosio.token"_n, {get_self(), "active"_n});
// transfer arguments are now passed as postional arguments
payout.send(get_self(), to, quantity, memo);

То же самое работает для отложенных транзакций с использованием метода to_action:

token::transfer_action payout("eosio.token"_n, {get_self(), "active"_n});

transaction t{};
t.actions.emplace_back(payout.to_action(get_self(), to, quantity, memo));
t.delay_sec = 10;
t.send(0 /* sender id */, get_self(), false);

4. Временные классы EOSIO time_point, time_point_sec, microseconds

Библиотека EOSIO определяет два класса дат в заголовке time.hpp, различающиеся своей точностью. Класс time_point_sec - это стандартная временная метка UNIX, хранящая секунды с 1 января 1970 года в uint32_t, time_point, имеет более точную точность, сохраняя прошедшее количество микросекунд (не миллисекунд) в uint64_t. Легко преобразовать из обоих классов и в оба.

Для арифметических вычислений со временем используется класс microseconds, который поставляется с полезными помощниками, такими как seconds, minutes или hours.

использование

eosio::time_point tp = eosio::current_time_point();
eosio::time_point_sec tps = eosio::current_time_point();
eosio::microseconds micros = tp.time_since_epoch();
uint64_t count_micros = micros.count();
uint32_t count_seconds = tps.sec_since_epoch();

// no more 60*60*24*1e6
const auto MICROSECONDS_IN_DAY = hours(24);
count_micros += MICROSECONDS_IN_DAY;
// no more 60*60*24
count_seconds += hours(24).to_seconds();

eosio::time_point_sec lastGame = /* ... */;
check((eosio::time_point_sec)(current_time_point() + minutes(1)) >= lastGame,
      "last game not finished");

Использование класса microseconds и его помощников позволяет избежать использования любых констант, таких как const auto SECONDS_PER_DAY = 60*60*24, что упрощает анализ кода.

Если вы хотите узнать больше о методах программирования смарт-контрактов EOS, ознакомьтесь с книгой Learn EOS Development book.

Первоначально опубликовано на cmichel.io