Appearance
与 FunC 的兼容性
Tact 本身编译为 FunC,并将其所有实体直接映射到各种 FunC 和 TL-B 类型。
类型转换
Tact 中的原始类型直接映射到 FunC 的对应类型。
关于变量复制的所有规则都是相同的。最大的区别之一是 Tact 中没有可见的变异操作符,而大多数 Slice 操作会就地修改变量。
序列化转换
与 FunC 需要手动定义序列化逻辑不同,Tact 中的结构体和消息的序列化是自动的。
Tact 的自动布局算法是贪婪的。这意味着它会取下一个变量,计算其大小,并尝试将其放入当前单元格。如果放不下,它会创建一个新单元格并继续。所有用于自动布局的内部结构体在分配前都会被扁平化。
所有可选类型都序列化为 TL-B 中的 Maybe,地址类型除外。
由于在某些情况下不定义序列化时选择什么,因此不支持 Either。
示例
solidity
// _ value1:int257 = SomeValue;
struct SomeValue {
value1: Int; // 默认为 257 位
}
// _ 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; // 总是作为引用存储
}
// _ cell:^slice = SomeValue;
struct SomeValue {
cell: Slice; // 总是作为引用存储
}
// _ 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 分配到下一个单元格,flag 在 value4 之前写入
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;
}
## 将接收的消息转换为操作
Tact 为每个接收到的类型化消息生成一个唯一的操作码,但可以被覆盖。
FunC 中的代码:
```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 中变为:
solidity
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 方法
您可以在 Tact 中表达除列表风格列表之外的所有内容,这些内容与 FunC 的 get 方法兼容。
原始返回类型
如果 FunC 中的 get 方法返回一个原始类型,您可以以相同的方式在 Tact 中实现它。
FunC 中的代码:
func
int seqno() method_id {
return 0;
}
在 Tact 中变为:
solidity
get fun seqno(): Int {
return 0;
}
张量返回类型
在 FunC 中,张量类型 (int, int) 和 (int, (int)) 之间存在差异,但对于 TVM 来说,它们都表示两个整数的堆栈。
要转换 FunC get 方法返回的张量,您需要定义一个结构体,其字段类型与张量相同且顺序相同。
FunC 中的代码:
func
(int, slice, slice, cell) get_wallet_data() method_id {
return ...;
}
在 Tact 中变为:
solidity
struct JettonWalletData {
balance: Int;
owner: Address;
master: Address;
walletCode: Cell;
}
contract JettonWallet {
get fun get_wallet_data(): JettonWalletData {
return ...;
}
}
元组返回类型
在 FunC 中,如果您返回一个元组而不是张量,您需要按照张量类型的过程操作,但需要将 get 方法的返回类型定义为可选的。
FunC 中的代码:
func
[int, int] get_contract_state() method_id {
return ...;
}
在 Tact 中变为:
solidity
struct ContractState {
valueA: Int;
valueB: Int;
}
contract StatefulContract {
get fun get_contract_state(): ContractState? {
return ...;
}
}
混合元组和张量返回类型
当一些张量是元组时,您需要按照前面的步骤定义一个结构体,元组部分必须定义为单独的结构体。
FunC 中的代码:
func
(int, [int, int]) get_contract_state() method_id {
return ...;
}
在 Tact 中变为:
solidity
struct ContractStateInner {
valueA: Int;
valueB: Int;
}
struct ContractState {
valueA: Int;
valueB: ContractStateInner;
}
contract StatefulContract {
get fun get_contract_state(): ContractState {
return ...;
}
}
参数映射
转换 get 方法的参数很直接。每个参数都按原样映射到 FunC 参数,每个元组映射到一个结构体。
FunC 中的代码:
func
(int, [int, int]) get_contract_state(int arg1, [int,int] arg2) method_id {
return ...;
}
在 Tact 中变为:
solidity
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
}
};
}
}