现在需要为smartcontract 创建更复杂的逻辑,并且用typescript编写本地测试。
#include "imports/stdlib.fc"; () recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure { slice cs = in_msg.begin_parse(); int flags = cs~load_uint(4); slice sender_address = cs~load_msg_addr(); set_data(begin_cell().store_slice(sender_address).end_cell()); } slice get_the_latest_sender() method_id { slice ds = get_data().begin_parse(); return ds~load_msg_addr(); }
这里我们将重写合约,使其增加一些新的逻辑:
- 它将接收一条带有特定命令的消息(命令用于确定应根据消息中接收到的数据执行合约的哪部分功能
- 根据这条特定命令,它将增加存储在 c4 存储器中的数字。
- 它提供了一个 getter 方法,用于返回当前计数器的值和发送增量操作代码消息的地址
写入数据存储和 getter 方法的概念之前讲过,命令的概念却是新的。命令也被称为操作码,或简称为操作码。操作码通常存储在传入 recv_internal 函数的 in_msg_body 片段的开头。
它本身只是一个整数,用它来表示某个逻辑块。这是一个标准,但操作码本身并不会自动插入在in_msg_body片段的开头--在发送信息之前,必须在编写信息时传递操作码。
需要注意,读取数据时,必须按照写入数据的顺序进行。为了确保 get_data()在第一次运行时正常工作,我们将从初始数据开始,因此我们从部署合同的那一刻起就在 c4 存储器中存储某个值。
OP code
将OPcode操作码插入in_msg_body前的代码如下:
#include "imports/stdlib.fc"; () recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure { slice cs = in_msg.begin_parse(); int flags = cs~load_uint(4); slice sender_address = cs~load_msg_addr(); int op = in_msg_body~load_uint(32); set_data(begin_cell().store_slice(sender_address).end_cell()); } slice get_the_latest_sender() method_id { slice ds = get_data().begin_parse(); return ds~load_msg_addr(); }
在 FunC 中,需要为变量指定内存中分配用于存储变量的位数。在 ~load_uint(32) 方法中设置 32,意味着这个整数的最大位数将是 32 位。
我们要用来递增计数器值的操作码将是 1。将逻辑封装到 if 语句中,检查操作码是否等于 1:
() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure { slice cs = in_msg.begin_parse(); int flags = cs~load_uint(4); slice sender_address = cs~load_msg_addr(); int op = in_msg_body~load_uint(32); if (op == 1) { ;; counter logic is coming here set_data(begin_cell().store_slice(sender_address).end_cell()); } } slice get_the_latest_sener() method_id { slice ds = get_data().begin_parse(); return ds~load_msg_addr(); }
In FunC用双分号 ;;开启注释
计数器逻辑实现
本地存储结构如下:
- 计数器 — 整数
- sender_address - slice属性,最近发送增量命令的钱包地址
在 recv_internal 函数中是假设本地存储空间已经存储了这些数据的,因此我们只需准备好读取这些数据的逻辑就行。
其中不需要读取前一个最新的发件人,因为将值保存回storage时,已经使用了新值--当前消息的发件人地址。
当得到计数器值后要将其递增。最后一步就是按照读取数据的顺序保存数据:
slice ds = get_data().begin_parse(); int counter_value = ds~load_uint(32); set_data( begin_cell().store_uint(counter_value + 1, 32).store_slice(sender_address).end_cell() );
继续更新 getter 方法,使其也能以正确的顺序读取存储数据。将其重命名为 get_contract_storage_data,因为现在它还提供计数器值和最近的发送者地址的功能:
(int, slice) get_contract_storage_data() method_id { slice ds = get_data().begin_parse(); return ( ds~load_uint(32), ;; counter_value ds~load_msg_addr() ;; the most recent sender ); }
由于现在返回的值不止一个,因此还需要将函数定义中的预期结果和返回结果都用圆括号封装起来。
最终代码是怎样的:
#include "imports/stdlib.fc"; () recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure { slice cs = in_msg.begin_parse(); int flags = cs~load_uint(4); slice sender_address = cs~load_msg_addr(); int op = in_msg_body~load_uint(32); if (op == 1) { slice ds = get_data().begin_parse(); int counter_value = ds~load_uint(32); set_data( begin_cell().store_uint(counter_value + 1, 32).store_slice(sender_address).end_cell() ); return (); } return (); } (int, slice) get_contract_storage_data() method_id { slice ds = get_data().begin_parse(); return ( ds~load_uint(32), ;; counter_value ds~load_msg_addr() ;; the most recent sender ); }
通过运行 yarn 编译就可以来检查我们的合约是否编译正确。