在审查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(value:0)""_n 或者name(0)的常量表达式,一些开发人员仍然使用它来表示同一个付款人。
2. get_first_receiver,get_self()
在contracts.hpp中定义的两个getters get_self
和get_first_receiver
返回正在运行的操作的执行上下文的一部分。(在EOSIO.CDT1.6中实现了get_first_receiver
以支持旧的get_code
,现在不推荐使用。)get_self
方法返回当前运行代码的合约,而get_first_receiver
返回动作源自的帐户。除非涉及通过require_recipient
的通知,否则这两个帐户是相同的。
例如,通过侦听eosio.token
的传输操作的通知,get_self()
返回你的合约部署到的帐户,而get_first_receiver()
返回eosio.token
帐户。这是因为该行为来自向你的合约帐户发送转移操作到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 header file中的所有操作定义了动作包装器:
[[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获得。
使用eosio-cpp的-I标志包含头文件的附加包含目录。
包含头文件后,将发送内联传输操作,如下所示:
#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 header中定义了两个日期类,它们的精度不同。time_point_sec
类是一个标准的UNIX时间戳,用于存储自1970年1月1日以来在uint32_t
中的秒数,time_point
具有更精确的精度,用于存储uint64_t
中经过的microseconds
(而不是毫秒数)。从两个类转换都很容易。
要使用时间算术,可以使用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
,使代码更容易推理。
转载自:http://blog.hubwiz.com/2019/06/28/eos-programming-concepts/