|
Tact 中的合约类似于流行的面向对象语言中的类,只是它们的实例部署在区块链上,不能像 结构和信息 那样被传递。
) ~& y( ?8 u4 R. a0 a8 }$ V) | A3 {2 \- [( o
自引用
1 _& k2 M- l' m) Q# [合约和trait有一个内置的标识符 self,用于引用它们的字段(持久状态变量和常量)和方法(内部函数):
+ ~0 S" C+ y8 o: ]) v) H9 w# i- contract Example {4 Q O5 b a: n+ T
- // persistent state variables
1 M2 ^ f( W8 M! u/ N6 w - foo: Int;
2 |( X7 G! u" c* F9 P+ D) s - . L- M+ n a6 g6 k1 @
- init() {
2 c0 e' H" Q1 W( h! ? - self.foo = 42; // <- referencing variable foo through self.
6 F) f$ U' N5 f6 P* z2 e* l - }( q( `2 P" w4 w5 H2 k y6 D7 \
- }
复制代码 结构
4 x% _/ e5 \7 K( n1 r( b, E每份合约可包括:1 l: W1 l. l+ W) p7 ~" ~3 j, Q
) m. P7 J$ i: x6 E! V3 q8 p& l5 d/ S
- 可继承trait
- 支持的接口
- 持久状态变量
- 构造函数 init()
- 合约常量
- Getter 函数
- Receiver 功能
- 内部功能4 U" r- C: A2 E& M1 i5 m0 o* }
) X1 | {, |4 u2 U$ H5 l/ p7 x
% u) n2 Z# h! X: T" T: A( k S# x8 R7 A# |
继承trait, with
* P! D" Y' t5 V" d
& b2 |/ V; V X$ |/ `合约可以继承traits的所有声明和定义,并覆盖它们的某些默认行为。 除此之外,每个合约和trait都隐式继承了特殊的BaseTrait trait。
/ B! f7 a; Q) m) {- C) \% E
& e: N0 D8 V& o要继承trait,请在合约声明中的关键字with后指定其名称。 要同时继承多个性状,请在逗号分隔的列表中指定它们的名称,并在后面加上逗号。4 b/ z! y2 R% d" r! H7 q4 q2 p
6 N" I6 ?; _, [3 m
/ c0 j+ o3 L3 _' i- trait InheritMe {}
, r+ C8 b$ |" j1 f, ]* n - trait InheritMeToo {}
/ H7 j' t# u g$ n) l `3 S
. h3 L: ], S, x3 u4 f+ e) a- // A contract inheriting a single trait9 _0 l9 |7 ?; N# J9 U
- contract Single with InheritMe {}
6 `1 T$ t6 A6 _ - 6 i5 A! D& `) i7 S& {# {4 _: P
- // A contract inheriting multiple traits
, f; P* {( l% a3 i- a7 \$ t - contract Plural with
0 w p! t, h2 ?( f. Z4 P - InheritMe,
1 ~( V! J7 H8 x5 P& H2 J/ F - InheritMeToo, // trailing comma is allowed
" z) K% \; V- a5 M' } - {}
复制代码
3 O# H6 e) q- X. \, Q% ?, \6 a4 z& w& Z0 G( o& P" o( ?; [
由于traits不允许有init()函数,因此继承了声明了任何持久状态变量的trait的合约必须通过提供自己的init()函数来初始化这些变量。7 T% J2 i) [# E) p3 {! ^
) l# Z: z+ k$ K/ w: J
- trait Supe { omelander: Bool }
2 Y3 x/ M* f+ J2 d+ L; u
W+ V1 M% [) u% [" V' H- contract Vot with Supe {: P9 ]2 U3 p' n, c' b& p
- init() {
6 W4 h) K& g7 ]. P9 h - self.omelander = true;
- k! u+ ~# g5 {( f# M" i - }
1 D B/ M) ?; C; |& v5 b7 ~ - }
复制代码 如果在trait中声明或定义了内部函数和常量,则可将其标记为虚拟或抽象,并在从trait继承的合约中重写。
) e4 W: o/ J' p7 h% x U, E! n& g J7 U0 x. K/ Y
& `1 m+ \3 W5 H9 ~- d7 g支持的接口,@interface(…)
' N/ k7 {! k9 `6 w: g如果不查看源代码,就很难弄清一个合约是做什么的,有哪些receiver和getter。 有时,无法获得或无法访问源代码,剩下的办法就是尝试拆解合约,并以这种方式对其进行回推,这是一种非常混乱且容易出错的方法,其收益会递减,而且没有真正的可重复性。/ A8 Y% x" D( j3 ^2 U
9 |! w4 V |9 n! |; |& c为了解决这个问题,创建了 OTP-001:支持的接口。 据此,Tact 合约可以报告 支持的接口列表,作为特殊的 supported_interfaces getter 的返回值。 该getter可以通过任何TON区块链浏览器在链下访问——只需指定supported_interfaces作为执行的方法,返回一个十六进制值的列表。& j, N7 t$ }8 e( `: i4 Z ?
% X5 U# v1 i% r ?1 `6 t6 K
+ h$ e7 A& j0 X要声明支持某个接口,可在 contract 和trait声明之前添加一个或多个@interface("…")属性:
( |- Y. I3 P& P9 R0 i- @interface("His name is")
9 Y. d" t" N2 a( }* ^% R - @interface("John"): d) m" f$ r- u6 V2 Q- M2 n' |
- contract SeeNah with Misc {! o9 ?4 [5 [: W' ]
- // ...
# M: |( ^4 `$ F6 g$ z - }
/ r4 i3 }8 F, [% j& l, L' ] - 3 M0 g i8 [! D. h. W+ W+ A3 Z
- @interface("name_of_your_org - miscellaneous")1 N) k. ?& V. w( X8 e
- trait Misc {6 o, X! q4 W1 O
- // ...7 B& O, f; V: u, q' i. j& F+ L; P2 R
- }
复制代码
9 c* ?' u, b% C; V2 w0 w2 i l
; J$ z$ I& H w: d, X) ~5 s* k% z* Q0 c, W% X: M0 {, a
; }! J a+ |' K3 k8 W( j0 j X2 e
标准库中的一些traits也定义了它们的接口:: i$ V# C h/ E& B; C: w( v) ~
* }% i) Q, a) x& G# V3 `0 {7 [- Ownable trait 指定"org.ton.ownable"
- OwnableTransferable trait 指定"org.ton.ownable.transferable.v2"
- Stoppable trait 指定"org.ton.stoppable"
- Resumable trait 指定"org.ton.resumable"
2 o4 X( V- e' y1 e
4 P6 D1 @" o5 f! ^' q N9 z8 ~5 D要启用 supported_interfaces getter 生成并在 Tact 合约中使用 @interface() 属性,请修改项目根目录下的 tact.config.json 文件(如果该文件不存在,则创建该文件),并 将 interfacesGetter 属性设置为 true。' W% d/ z+ T; ~) n, d
6 U e/ P2 n" T) a6 m( r. R1 j如果您的项目基于 Blueprint,您可以在合约的编译配置中启用supported_interfaces,这些配置位于名为wrappers/的目录中:
Y6 P, i2 c9 [: Z
- _( O; U+ M9 W& y( m n. g- ~) k! }" ?# g& x9 y' S0 e
- import { CompilerConfig } from '@ton/blueprint';
. p6 m$ |# e2 z: l# x. M - . H h- R5 T) t6 v8 T# v
- export const compile: CompilerConfig = {
8 a6 q4 n2 ~1 i - lang: 'tact',
, w5 g# J# D7 q2 @8 i* C2 {# g }) i - target: 'contracts/your_contract_name.tact',7 w1 Z6 ], \. J
- options: {9 E( S4 ~) v. D. K% r$ v" {+ J0 Z
- interfacesGetter: true, // ← that's the stuff!
$ s; k& i l7 [2 W/ z- c, p6 q, ?( \ - }
' S" l, |0 ]7 _$ Q6 o5 R$ ^) c - };
复制代码 除此之外,tact.config.json 仍然可以用于 Blueprint 项目。 除此之外,Blueprint 项目中仍可使用 tact.config.json。 在这种情况下,除非在 wrappers/ 中进行修改,否则 tact.config.json中指定的值将作为默认值。
# [/ Z- E, I/ p9 b9 Z1 p2 B8 F% M1 m: M
, w6 {2 u6 ]% M4 I
注意% F+ T5 B0 C. l
% I2 S! P: K A2 U7 |' P
如果你有 separateCompilables 选项设置为 true 在 blueprint.config.ts,然后.compile.ts文件将位于compilables/目录中,不位于wrapper/。6 a+ I4 g; y8 A& p# t& r
" n9 g. }. N% [. i
警告
2 j0 f3 Z! o( F0 V: A6 ~
0 e' |/ y/ E" [1 m: t请注意,添加接口并不能保证合约实际实现任何特定功能,也不能保证以任何特定方式实现这些功能。 这只是一种可验证的链外承诺,即合约中可能包含某些特定代码。 您应该相信但要核实这些说法。
. t8 v, Q; |! T Q, I7 c" ]6 h! g
此外,不同接口之间也不能保证不会发生名称冲突,尽管这种情况不太可能发生,因为即使是 SHA-256 的前 128 位也能提供足够的抗碰撞性。8 L& W4 I: }7 X! ?6 g% w
4 ]( n# Q( h, U2 }
2 y+ {2 X5 k* b4 _/ {: k7 a$ h
持久状态变量
" z1 u( O6 t: v5 G @! N& K+ x- W合约可以定义在合约调用之间持续存在的状态变量。 合约可以定义在合约调用之间持续存在的状态变量。 TON 中的合约支付租金 与它们消耗的持久空间成正比,因此鼓励通过序列化进行紧凑表示。
& Z' V% J3 v3 F0 }( K2 @8 l9 H7 z# y
+ k0 \9 I1 Q% V- ~- contract Example {- t7 Y, L( `2 a6 M" M2 C
- // persistent state variables. x1 z. a" c0 p4 w9 ^
- val: Int; // Int7 D. r2 ^! }5 T1 }* B1 d6 o
- val32: Int as uint32; // Int serialized to an 32-bit unsigned# S- |6 B8 F' o
- mapVal: map<Int, Int>; // Int keys to Int values; {( P( }( @3 V5 i
- optVal: Int?; // Int or null( M! b1 Q) B( j/ ?
- }
复制代码 状态变量必须有默认值或在 init() 函数中初始化,该函数在部署合约时运行。 唯一的例外是 map<K, V> 类型的持久状态变量,因为它们默认初始化为空。+ u- r. U( o' J- j2 Y) G
! J) g' ?- x4 x) O
. F( A* `. p/ N注意
2 n1 n- M. a( x) Y! K4 {# x/ h7 m
请注意,Tact 也支持非持续状态的局部变量,请参阅:变量声明。
( O2 f3 g& p8 D3 Q% v: n, v# I2 Q6 T% a- _% e$ R! R" F4 B
4 t O; `: J3 o8 ^* J( ?. v' P% M
|
|