FunC 开发手册
创建 FunC 开发手册的核心原因是将所有 FunC 开发者的经验汇集在一个地方,以便未来的开发者们使用!
与 FunC 文档相比,本文更侧重于 FunC 开发者在智能合约开发过程中每天都要解决的任务。
基础知识
如何编写 if 语句
假设我们想检查某个事件是否相关。为此,我们使用标志变量。记住在 FunC 中 true
是 -1
而 false
是 0
。
int flag = 0; ;; false
if (flag) {
;; 做一些事情
}
else {
;; 拒绝交易
}
💡 注意
我们不需要使用
==
操作符,因为0
的值是false
,所以任何其他值都将是true
。
💡 有用的链接
如何编写 repeat 循环
以指数运算为例
int number = 2;
int multiplier = number;
int degree = 5;
repeat(degree - 1) {
number *= multiplier;
}
💡 有用的链接
如何编写 while 循环
当我们不知道要执行特定操作多少次时,while 循环很有用。例如,取一个 cell
,我们知 道它可以存储最多四个对其他 cell 的引用。
cell inner_cell = begin_cell() ;; 创建一个新的空构建器
.store_uint(123, 16) ;; 存储值为 123 且长度为 16 位的 uint
.end_cell(); ;; 将构建器转换为 cell
cell message = begin_cell()
.store_ref(inner_cell) ;; 将 cell 作为引用存储
.store_ref(inner_cell)
.end_cell();
slice msg = message.begin_parse(); ;; 将 cell 转换为 slice
while (msg.slice_refs_empty?() != -1) { ;; 我们应该记住 -1 是 true
cell inner_cell = msg~load_ref(); ;; 从 slice msg 中加载 cell
;; 做一些事情
}
💡 有用的链接
如何编写 do until 循环
当我们需要循环至少运行一次时,我们使用 do until
。
int flag = 0;
do {
;; 即使 flag 是 false (0) 也做一些事情
} until (flag == -1); ;; -1 是 true
💡 有用的链接
如何确定 slice 是否为空
在处理 slice
之前,需要检查它是否有数据以便正确处理。我们可以使用 slice_empty?()
来做到这一点,但我们必须考虑到,如果有至少一个 bit
的数据或一个 ref
,它将返回 -1
(true
)。
;; 创建空 slice
slice empty_slice = "";
;; `slice_empty?()` 返回 `true`,因为 slice 没有任何 `bits` 和 `refs`
empty_slice.slice_empty?();
;; 创建仅包含 bits 的 slice
slice slice_with_bits_only = "Hello, world!";
;; `slice_empty?()` 返回 `false`,因为 slice 有 `bits`
slice_with_bits_only.slice_empty?();
;; 创建仅包含 refs 的 slice
slice slice_with_refs_only = begin_cell()
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_empty?()` 返回 `false`,因为 slice 有 `refs`
slice_with_refs_only.slice_empty?();
;; 创建包含 bits 和 refs 的 slice
slice slice_with_bits_and_refs = begin_cell()
.store_slice("Hello, world!")
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_empty?()` 返回 `false`,因为 slice 有 `bits` 和 `refs`
slice_with_bits_and_refs.slice_empty?();
💡 有用的链接
如何确定 slice 是否为空(不含任何 bits,但可能包含 refs)
如果我们只需要检查 bits
,不关心 slice
中是否有任何 refs
,那么我们应该使用 slice_data_empty?()
。
;; 创建空 slice
slice empty_slice = "";
;; `slice_data_empty?()` 返回 `true`,因为 slice 没有任何 `bits`
empty_slice.slice_data_empty?();
;; 创建仅包含 bits 的 slice
slice slice_with_bits_only = "Hello, world!";
;; `slice_data_empty?()` 返回 `false`,因为 slice 有 `bits`
slice_with_bits_only.slice_data_empty?();
;; 创建仅包含 refs 的 slice
slice slice_with_refs_only = begin_cell()
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_data_empty?()` 返回 `true`,因为 slice 没有 `bits`
slice_with_refs_only.slice_data_empty?();
;; 创建包含 bits 和 refs 的 slice
slice slice_with_bits_and_refs = begin_cell()
.store_slice("Hello, world!")
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_data_empty?()` 返回 `false`,因为 slice 有 `bits`
slice_with_bits_and_refs.slice_data_empty?();
💡 有用的链接
如何确定 slice 是否为空(没有任何 refs,但可能有 bits)
如果我们只对 refs
感兴趣,我们应该使用 slice_refs_empty?()
来检查它们的存在。
;; 创建空 slice
slice empty_slice = "";
;; `slice_refs_empty?()` 返回 `true`,因为 slice 没有任何 `refs`
empty_slice.slice_refs_empty?();
;; 创建只包含 bits 的 slice
slice slice_with_bits_only = "Hello, world!";
;; `slice_refs_empty?()` 返回 `true`,因为 slice 没有任何 `refs`
slice_with_bits_only.slice_refs_empty?();
;; 创建只包含 refs 的 slice
slice slice_with_refs_only = begin_cell()
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_refs_empty?()` 返回 `false`,因为 slice 有 `refs`
slice_with_refs_only.slice_refs_empty?();
;; 创建包含 bits 和 refs 的 slice
slice slice_with_bits_and_refs = begin_cell()
.store_slice("Hello, world!")
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_refs_empty?()` 返回 `false`,因为 slice 有 `refs`
slice_with_bits_and_refs.slice_refs_empty?();
💡 有用的链接
如何确定 cell 是否为空
要检查 cell
中是否有任何数据,我们应首先将其转换为 slice
。如果我们只对 bits
感兴趣,应使用 slice_data_empty?()
;如果只对 refs
感兴趣,则使用 slice_refs_empty?()
。如果我们想检查是否有任何数据,无论是 bit
还是 ref
,我们需要使用 slice_empty?()
。
cell cell_with_bits_and_refs = begin_cell()
.store_uint(1337, 16)
.store_ref(null())
.end_cell();
;; 将 `cell` 类型更改为 slice,使用 `begin_parse()`
slice cs = cell_with_bits_and_refs.begin_parse();
;; 确定 slice 是否为空
if (cs.slice_empty?()) {
;; cell 为空
}
else {
;; cell 不为空
}
💡 有用的链接
如何确定 dict 是否为空
有一个 dict_empty?()
方法可以检查 dict 中是否有数据。这个方法相当于 cell_null?()
,因为通常一个空的 cell 就是一个空字典。
cell d = new_dict();
d~udict_set(256, 0, "hello");
d~udict_set(256, 1, "world");
if (d.dict_empty?()) { ;; 确定 dict 是否为空
;; dict 为空
}
else {
;; dict 不为空
}
💡 有用的链接
文档中的“new_dict()” 创建空字典
[文档中的“dict_set()”](/develop/
func/stdlib/#dict_set) 为 dict d 添加一些元素,所以它不为空
如何确定 tuple 是否为空
在处理 tuples
时,始终知道内部是否有值以供提取是很重要的。如果我们尝试从空的 tuple
中提取值,将会得到一个错误:“not a tuple of valid size”,exit code 7。
;; 声明 tlen 函数,因为它在 stdlib 中没有提供
(int) tlen (tuple t) asm "TLEN";
() main () {
tuple t = empty_tuple();
t~tpush(13);
t~tpush(37);
if (t.tlen() == 0) {
;; tuple 为空
}
else {
;; tuple 不为空
}
}
💡 注意
💡 有用的链接
如何确定 Lisp 类型的列表是否为空
tuple numbers = null();
numbers = cons(100, numbers);
if (numbers.null?()) {
;; Lisp 类型的列表为空
} else {
;; Lisp 类型的列表不为空
}
我们使用 cons 函数将数字 100 添加到我们的 Lisp 类型列表中,所以它不为空。
如何确定合约的状态是否为空
假设我们有一个 counter
,用于存储交易次数。在智能合约状态的第一次交易中,这个变量不可用,因为状态为空,因此需要处理这种情况。如果状态为空,我们创建一个变量 counter
并保存它。
;; `get_data()` 将从合约状态返回数据 cell
cell contract_data = get_data();
slice cs = contract_data.begin_parse();
if (cs.slice_empty?()) {
;; 合约数据为空,所以我们创建 counter 并保存
int counter = 1;
;; 创建 cell,添加 counter 并保存在合约状态中
set_data(begin_cell().store_uint(counter, 32).end_cell());
}
else {
;; 合约数据不为空,所以我们获取我们的 counter,增加它并保存
;; 我们应该指定 counter 的正确的位长度
int counter = cs~load_uint(32) + 1;
set_data(begin_cell().store_uint(counter, 32).end_cell());
}
💡 注意
我们可以通过确定 cell 是否为空 来确定合约的状态是否为空。
💡 有用的链接
如何构建内部消息 cell
如果我们希望合约发送一个内部消息,我们应该首先正确地创建它为一个 cell,指定技术标志位、接收地址和其余数据。
;; 我们使用字面量 `a` 从包含地址的字符串中获取有效地址的 slice
slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a;
int amount = 1000000000;
;; 我们使用 `op` 来识别操作
int op = 0;
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(addr)
.store_coins(amount)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认消息 header 部(参见发送消息页面)
.store_uint(op, 32)
.end_cell();
send_raw_message(msg, 3); ;; 模式 3 - 分别支付费用并忽略错误
💡 注意
在这个例子中,我们使用字面量
a
获取地址。你可以在文档中找到更多关于字符串字面量的信息。
💡 注意