与 func 的兼容性
Tact 本身编译为 FunC,并将其所有实体直接映射到各种 FunC 和 TL-B 类型。
转换类型
Tact 中的 原始类型可直接映射到 FunC 中的类型。
复制变量的所有规则都是一样的。 所有关于复制变量的规则都是一样的。其中一个最大的不同是,在 Tact 中没有可见的突变(mutation)操作符,大多数 slice
操作都是就地突变变量。
转换序列化
在 Tact 中,Structs和Messages的序列化是自动进行的,不像 FunC 需要手动定义序列化逻辑。
Tact 的自动布局算法是贪婪的。 这意味着它会获取下一个变量,计算其大小,并尝试将其放入当前cell中。 如果不合适,它会创建一个新cell并继续。 自动布局的所有内部结构在分配前都会被扁平化。
除了 Address
以外,所有可选类型在 TL-B 中都序列化为 Maybe
。
没有对 Either
的支持,因为它没有定义在某些情况下序列化时应选择什么。
实例
// _ value1:int257 = SomeValue;struct SomeValue { value1: Int; // Default is 257 bits}
// _ value1:int256 value2:uint32 = SomeValue;struct SomeValue { value1: Int as int256; value2: Int as uint32;}
// _ value1:bool value2:Maybe bool = SomeValue;struct SomeValue { value1: Bool; value2: Bool?;}
// _ cell:^cell = SomeValue;struct SomeValue { cell: Cell; // Always stored as a reference}
// _ cell:^slice = SomeValue;struct SomeValue { cell: Slice; // Always stored as a reference}
// _ value1:int256 value2:int256 value3:int256 ^[value4:int256] = SomeValue;struct SomeValue { value1: Int as int256; value2: Int as int256; value3: Int as int256; value4: Int as int256;}
// _ value1:int256 value2:int256 value3:int256 ^[value4:int256] flag:bool = SomeValue;struct SomeValue { value1: Int as int256; value2: Int as int256; value3: Int as int256; flag: Bool; // Flag is written before value4 to avoid auto-layout to allocate it to the next cell value4: Int as int256;}
// _ value1:int256 value2:int256 value3:int256 ^[value4:int256 flag:bool] = SomeValue;struct SomeValue { value1: Int as int256; value2: Int as int256; value3: Int as int256; value4: Int as int256; flag: Bool;}
// _ value1:int256 value2:^TailString value3:int256 = SomeValue;struct SomeValue { value1: Int as int256; value2: String; value3: Int as int256;}
将收到的信息转换为 op
操作
Tact 会为每条接收到的键入信息生成一个唯一的 op
,但它可以被覆盖。
下面是 FunC 中的代码
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure { ;; incoming message code...
;; Receive MessageWithGeneratedOp message if (op == 1180414602) { ;; code... }
;; Receive MessageWithOverwrittenOP message if (op == 291) { ;; code... }
}
在 Tact 中变成了这样:
message MessageWithGeneratedOp { amount: Int as uint32;}
message(0x123) MessageWithOverwrittenOP { amount: Int as uint32;}
contract Contract { // Contract Body...
receive(msg: MessageWithGeneratedOp) { // code... }
receive(msg: MessageWithOverwrittenOP) { // code... }
}
转换 get
-methods
你可以用与 FunC 的 get
方法兼容的 Tact 来表达除 list-style-lists
以外的所有内容。
基本返回类型
如果一个 get
方法在 FunC 中返回一个基元,那么在 Tact 中也可以用同样的方法实现它。
下面是 FunC 中的代码
int seqno() method_id { return 0;}
在 Tact 中变成了这样:
get fun seqno(): Int { return 0;}
张量(Tensor)返回类型
在 FunC 中,张量类型 (int, int)
和 (int, (int))
是有区别的,但对于 TVM 来说没有区别,它们都表示两个整数的堆栈。
要转换从 FunC get
方法返回的张量,需要定义一个 Struct,它与张量的字段类型相同,顺序也相同。
下面是 FunC 中的代码
(int, slice, slice, cell) get_wallet_data() method_id { return ...;}
在 Tact 中变成了这样:
struct JettonWalletData { balance: Int; owner: Address; master: Address; walletCode: Cell;}
contract JettonWallet { get fun get_wallet_data(): JettonWalletData { return ...; }}
元组(Tuple)返回类型
在 FunC 中,如果返回的是元组而不是张量,则需要遵循张量类型的流程,但要将 “get ”方法的返回类型定义为可选类型。
下面是 FunC 中的代码
[int, int] get_contract_state() method_id { return ...;}
在 Tact 中变成了这样:
struct ContractState { valueA: Int; valueB: Int;}
contract StatefulContract { get fun get_contract_state(): ContractState? { return ...; }}
混合元组(tuple)和张量(tensor)返回类型
如果某些张量是元组,则需要像前面的步骤一样定义结构体,而元组必须定义为单独的 Struct。
下面是 FunC 中的代码
(int, [int, int]) get_contract_state() method_id { return ...;}
在 Tact 中变成了这样:
struct ContractStateInner { valueA: Int; valueB: Int;}
struct ContractState { valueA: Int; valueB: ContractStateInner;}
contract StatefulContract { get fun get_contract_state(): ContractState { return ...; }}
参数映射
get方法参数的转换过程非常直接。 每个参数按原样映射为 FunC 的参数,每个元组映射为一个 Struct。
下面是 FunC 中的代码
(int, [int, int]) get_contract_state(int arg1, [int,int] arg2) method_id { return ...;}
在 Tact 中变成了这样:
struct ContractStateArg2 { valueA: Int; valueB: Int;}
struct ContractStateInner { valueA: Int; valueB: Int;}
struct ContractState { valueA: Int; valueB: ContractStateInner;}
contract StatefulContract { get fun get_contract_state(arg1: Int, arg2: ContractStateArg2): ContractState { return ContractState{ valueA: arg1, valueB: ContractStateInner{ valueA: arg2.valueA, valueB: arg2.valueB, // trailing comma is allowed }, // trailing comma is allowed }; }}