|
Tact 中的合约类似于流行的面向对象语言中的类,只是它们的实例部署在区块链上,不能像 结构和信息 那样被传递。
, J7 y' q! V, |
6 G! G/ X L5 Y2 \+ v自引用
& ]$ {* N2 ]- z4 L& B0 O# s9 b& B8 q合约和trait有一个内置的标识符 self,用于引用它们的字段(持久状态变量和常量)和方法(内部函数):
' f4 N* h& P* J' [7 Q- contract Example {
# I8 |/ ^. E; l! F. F0 h" q - // persistent state variables" i7 A% W- ]" T: |$ A; w
- foo: Int;5 R: U+ n, P. I! y4 p X
- 3 H7 v s: X9 O" Y+ y# N
- init() {1 v6 d n' Y x4 \$ G- E
- self.foo = 42; // <- referencing variable foo through self.
* h! |+ V! `/ R3 x$ S# A9 B3 l" y - }
5 e7 x' Z1 V& W - }
复制代码 结构
2 Q2 N& ^, [/ B3 @每份合约可包括:; y( V J% k% y5 O, T5 P4 X. E3 z
: n4 ^4 T" w) G2 T
- 可继承trait
- 支持的接口
- 持久状态变量
- 构造函数 init()
- 合约常量
- Getter 函数
- Receiver 功能
- 内部功能
, n) s; H3 t! B/ z) A$ _5 [6 P 1 l( W; |- |& K$ S; _- {3 L
9 G; M* |* l( {8 X! }( _
0 e/ L! O9 X. i# l1 x" }+ p: _
继承trait, with: S6 _; b+ J$ r. L
, E# O# u h Q2 R8 i& Y合约可以继承traits的所有声明和定义,并覆盖它们的某些默认行为。 除此之外,每个合约和trait都隐式继承了特殊的BaseTrait trait。
' u5 j* L+ q) \( C" K4 v- f
- p6 y' X6 m& ^: n. _要继承trait,请在合约声明中的关键字with后指定其名称。 要同时继承多个性状,请在逗号分隔的列表中指定它们的名称,并在后面加上逗号。
) E3 @' [0 @/ \* Y% n: y/ B- i! d
7 Z& i1 g1 h( I( u8 g0 U1 U4 Q- r# a- trait InheritMe {}
" c, d3 }9 T# h/ e - trait InheritMeToo {}/ g8 V+ G7 M" T0 P+ `. \
( N; i7 Z$ K" N" L1 T/ e* r- S- // A contract inheriting a single trait
0 W2 n$ L2 z4 }' m' c5 U9 g7 U - contract Single with InheritMe {}. v( K+ Z$ r5 X- ?
- - x4 l3 T+ h' h8 K H" |3 p
- // A contract inheriting multiple traits) @+ j6 f% t) A. `
- contract Plural with
+ S, F, v4 P" _1 q$ O - InheritMe,
1 ?' [' J1 u) K3 T/ t8 |" P - InheritMeToo, // trailing comma is allowed
# T1 l7 O/ N9 P) t" S! ]+ d1 \" V - {}
复制代码 {' V4 m* v, _
# e. A! d. i: }2 m& W& E; l
由于traits不允许有init()函数,因此继承了声明了任何持久状态变量的trait的合约必须通过提供自己的init()函数来初始化这些变量。 R( ~ z1 ?! g
9 V; R1 }, ?# O e5 `- l- trait Supe { omelander: Bool }# S/ e& d/ t( h
- # E! Z, G t& x+ R
- contract Vot with Supe {; q: Z) U$ C) `& s3 W* Q6 v
- init() {
1 j9 H4 N. d* l/ K5 r6 u - self.omelander = true;
( f+ Y2 Z2 e" r% N* @! F' Q: z - }( V0 D- s; R( h* {7 F
- }
复制代码 如果在trait中声明或定义了内部函数和常量,则可将其标记为虚拟或抽象,并在从trait继承的合约中重写。
8 \. L7 d9 F! o- d
1 t* R* k9 R, ]' k8 B
& J: d6 h4 Q1 E7 X- Y4 ?支持的接口,@interface(…)
, I8 v* w+ C( K) p* G如果不查看源代码,就很难弄清一个合约是做什么的,有哪些receiver和getter。 有时,无法获得或无法访问源代码,剩下的办法就是尝试拆解合约,并以这种方式对其进行回推,这是一种非常混乱且容易出错的方法,其收益会递减,而且没有真正的可重复性。0 M5 O2 s$ m9 J6 }/ G: V
, `4 b6 e2 g* ]# }9 c
为了解决这个问题,创建了 OTP-001:支持的接口。 据此,Tact 合约可以报告 支持的接口列表,作为特殊的 supported_interfaces getter 的返回值。 该getter可以通过任何TON区块链浏览器在链下访问——只需指定supported_interfaces作为执行的方法,返回一个十六进制值的列表。2 r4 K# e8 u/ e5 ]- O
+ }/ }7 R, S' S6 H
4 e! A' {8 _8 ]2 z5 Q要声明支持某个接口,可在 contract 和trait声明之前添加一个或多个@interface("…")属性:5 a4 Q1 t; J0 W" D
- @interface("His name is")/ U6 T( X# M2 S h, b: Z ], n4 N
- @interface("John")1 d4 b: j+ p' f5 T8 y% k
- contract SeeNah with Misc {8 f- [ ? Y% W& Q* m
- // ..." q' r. {% [6 ]" b, P2 l
- }$ {5 V1 N I0 U# ~2 ]
- 0 a% d' j0 z6 J" k& H
- @interface("name_of_your_org - miscellaneous")
' v2 F% E5 D1 s - trait Misc {
) \" y; o2 T* J: s e% n - // ...3 z9 s* R9 V' [5 k! D9 q q/ N4 r
- }
复制代码
# V+ J% W* c( S6 R! a2 D
6 X A: z- m8 V$ W9 d; `0 [4 G y2 P: i1 a% b' p4 d/ y
0 h6 `! K' Y& V5 n. Q标准库中的一些traits也定义了它们的接口:
2 ~6 H0 ^. `1 ?2 Y* L8 B
4 ~/ L q0 H* e6 Z; e- Ownable trait 指定"org.ton.ownable"
- OwnableTransferable trait 指定"org.ton.ownable.transferable.v2"
- Stoppable trait 指定"org.ton.stoppable"
- Resumable trait 指定"org.ton.resumable"2 E# o, \0 L, i. c. |
3 G& ^$ C" D4 x8 t1 W
要启用 supported_interfaces getter 生成并在 Tact 合约中使用 @interface() 属性,请修改项目根目录下的 tact.config.json 文件(如果该文件不存在,则创建该文件),并 将 interfacesGetter 属性设置为 true。 }' u7 j) z# N7 z5 W( Y! x
/ r& F/ F; [5 k% J2 H
如果您的项目基于 Blueprint,您可以在合约的编译配置中启用supported_interfaces,这些配置位于名为wrappers/的目录中:6 P. {$ e3 X+ ?/ h5 D
5 R( c! z' E# I- u' L5 m& r) x/ V" ^$ {" v- m
- import { CompilerConfig } from '@ton/blueprint';8 }. i) A: p. A6 V$ A o
- 2 o, k& N$ { D s; v
- export const compile: CompilerConfig = {7 a6 M, }+ o* l4 O5 m
- lang: 'tact',; r: ~: n; X6 k7 ]% ^) Y2 H
- target: 'contracts/your_contract_name.tact'," K1 [ r$ Z1 j4 p+ i# ?9 \3 E
- options: {
3 E. s7 T: P3 ^; R- m) H - interfacesGetter: true, // ← that's the stuff!1 p& O, m) N) A/ L; ~# I# ]
- }
$ A- Q3 N- K9 [ e6 T# r - };
复制代码 除此之外,tact.config.json 仍然可以用于 Blueprint 项目。 除此之外,Blueprint 项目中仍可使用 tact.config.json。 在这种情况下,除非在 wrappers/ 中进行修改,否则 tact.config.json中指定的值将作为默认值。8 P* n; N/ I6 }3 F
: @3 ~" C. f) x
$ L }3 i6 J4 Q注意5 \( d& \0 E# h8 @( ]% B- t
5 j" V! ?- c. l% }' H j1 V' J4 |
如果你有 separateCompilables 选项设置为 true 在 blueprint.config.ts,然后.compile.ts文件将位于compilables/目录中,不位于wrapper/。$ _2 j0 V! W* Z1 M# V3 u
5 L. Q7 w2 y+ _; l! S: \. R; C. T
警告8 _- H: v* A* B6 ~+ K
8 c' Z$ n3 |* j5 S6 c& u请注意,添加接口并不能保证合约实际实现任何特定功能,也不能保证以任何特定方式实现这些功能。 这只是一种可验证的链外承诺,即合约中可能包含某些特定代码。 您应该相信但要核实这些说法。! H" A a+ \, i% }6 ?" r
! `5 A8 X. E1 X% Q* u1 x0 c8 ~此外,不同接口之间也不能保证不会发生名称冲突,尽管这种情况不太可能发生,因为即使是 SHA-256 的前 128 位也能提供足够的抗碰撞性。, i: X* W5 V8 j4 k
; b3 t( c- t) Q1 V+ ^) I
; |- m5 N+ I" q; h2 ]* ^" Z8 c, U. ~9 ~5 I
持久状态变量
8 N: J( j. r! b% w5 }* ]合约可以定义在合约调用之间持续存在的状态变量。 合约可以定义在合约调用之间持续存在的状态变量。 TON 中的合约支付租金 与它们消耗的持久空间成正比,因此鼓励通过序列化进行紧凑表示。. z2 F7 l) K" e0 p* ?
8 H! W6 i$ Z d) b; R0 T- c. d5 i i
- contract Example {$ j7 |) @* \$ l/ G# _3 c0 s
- // persistent state variables5 F6 E% K& q6 G
- val: Int; // Int: X5 _; ^' v/ a$ H
- val32: Int as uint32; // Int serialized to an 32-bit unsigned
9 b% g6 `% z! B6 Y - mapVal: map<Int, Int>; // Int keys to Int values2 |2 z7 ?! \2 P$ U7 b
- optVal: Int?; // Int or null
6 F# T2 ~, h6 F1 X$ t& h - }
复制代码 状态变量必须有默认值或在 init() 函数中初始化,该函数在部署合约时运行。 唯一的例外是 map<K, V> 类型的持久状态变量,因为它们默认初始化为空。
2 q9 Y: W9 T: A d9 b# X( O# g- S0 i1 {6 I
7 P# y3 G# Q" y: D* t
注意1 v. W; C) M$ \$ k+ L6 K- K
* c* e) S& V1 I) w
请注意,Tact 也支持非持续状态的局部变量,请参阅:变量声明。
+ h" S1 j+ Y9 u5 v3 n" ^, K& }/ R' D! E0 {2 k
' _( Y# J: }( A
|
|