|
Tact 中的合约类似于流行的面向对象语言中的类,只是它们的实例部署在区块链上,不能像 结构和信息 那样被传递。
7 S. J I# O3 B1 {
, H5 g: Y$ O( b2 y% D2 D自引用1 t2 G: g+ ^0 Q4 V- s. v" J
合约和trait有一个内置的标识符 self,用于引用它们的字段(持久状态变量和常量)和方法(内部函数):
5 d/ g8 [, N0 a% y- N; a) }1 U- contract Example {$ P1 |: B3 e3 ~1 |/ F
- // persistent state variables2 \' p* [9 {. M* _" O7 m
- foo: Int;/ C" b: M4 I- Z R6 \. q
& l: f7 B/ f# F7 w2 A( C' \& f" d- init() {% C/ M/ g- x) i6 ]* f
- self.foo = 42; // <- referencing variable foo through self.8 |6 w, N/ X# H D4 ]
- }
8 Z9 y/ D- Y3 } j$ |# T3 v - }
复制代码 结构) Y: M4 ]5 Q K6 T" J
每份合约可包括:2 M. z$ H' P6 n" ?( u- `8 M" B
! O# A' w& ? e- 可继承trait
- 支持的接口
- 持久状态变量
- 构造函数 init()
- 合约常量
- Getter 函数
- Receiver 功能
- 内部功能7 Q. N P& { h; k: u& N" Y# `( O
% h$ U9 |1 X$ H8 B
3 O0 S# A$ Y: i% {* k: i7 ]/ J \9 `+ B# V+ p& r+ N) J t
继承trait, with. ~$ L; v2 } s& v( \) x
+ @* }( j# n3 [6 N3 |: c) E% q
合约可以继承traits的所有声明和定义,并覆盖它们的某些默认行为。 除此之外,每个合约和trait都隐式继承了特殊的BaseTrait trait。
2 B( ]9 l+ | x, f' F# i) {, o n+ w3 p+ V3 ^# J( B+ _
要继承trait,请在合约声明中的关键字with后指定其名称。 要同时继承多个性状,请在逗号分隔的列表中指定它们的名称,并在后面加上逗号。
' ^0 M5 O5 M5 g( X; z) O0 j* f1 F( G2 e% z8 R: p- F# i* o, a3 O
) K) n: a. W. Z: k5 H
- trait InheritMe {}2 y8 Z& K- Z, T! L0 p* v
- trait InheritMeToo {}
; D5 u& D3 \" p) Y$ K0 p8 N - G6 ~1 U+ v1 d6 W1 J H) d
- // A contract inheriting a single trait
$ \" p) h2 R3 G$ @( \ - contract Single with InheritMe {}
5 R1 l, `8 M9 B) q% ^4 A4 P - , I. g% T3 }3 z
- // A contract inheriting multiple traits) m" P; G7 M( C V8 b
- contract Plural with! S& [6 E% o. l9 e" |; a5 _9 Z. B i
- InheritMe,
0 w/ ^ S# [, e, E- m1 [ - InheritMeToo, // trailing comma is allowed9 O4 w: ^5 y8 t" Y& ?9 O) ~
- {}
复制代码
- u( C. ^5 ` L$ o5 ?" B, s, G0 ~- s! K% W/ g
由于traits不允许有init()函数,因此继承了声明了任何持久状态变量的trait的合约必须通过提供自己的init()函数来初始化这些变量。
1 y- M% u" Y2 D( ]2 s- [4 b( p; e6 `
- trait Supe { omelander: Bool }
6 W1 c% W: O; Z4 `# A - ! [6 }' H( c. R+ g- D
- contract Vot with Supe {
' e$ f$ a3 o1 Y# J# e' B6 q - init() {
( G! K+ C$ p6 z - self.omelander = true;
% }* S3 h/ S7 @8 C& E - }5 }0 G; ~6 Y) E8 g, \% C
- }
复制代码 如果在trait中声明或定义了内部函数和常量,则可将其标记为虚拟或抽象,并在从trait继承的合约中重写。: A0 e8 z9 m9 w/ p, `
Z6 S: L0 f; F& i5 B W4 a5 y$ Z; E
支持的接口,@interface(…)
0 ^1 Q/ K- r+ C7 E) {! [如果不查看源代码,就很难弄清一个合约是做什么的,有哪些receiver和getter。 有时,无法获得或无法访问源代码,剩下的办法就是尝试拆解合约,并以这种方式对其进行回推,这是一种非常混乱且容易出错的方法,其收益会递减,而且没有真正的可重复性。
1 P8 G/ x, A0 E- P+ @6 W$ T
, s* e `0 A& v- X为了解决这个问题,创建了 OTP-001:支持的接口。 据此,Tact 合约可以报告 支持的接口列表,作为特殊的 supported_interfaces getter 的返回值。 该getter可以通过任何TON区块链浏览器在链下访问——只需指定supported_interfaces作为执行的方法,返回一个十六进制值的列表。
) h" I5 t$ c( \4 w& C. p& ?6 u7 [. c( m5 }1 N0 b0 o0 y% q. u
o/ A: L9 Q/ q4 G要声明支持某个接口,可在 contract 和trait声明之前添加一个或多个@interface("…")属性:
* v; b- t: I2 i5 b+ @" Z- @interface("His name is"); u T$ @$ E- @- B2 d! Y
- @interface("John")& I" S% J0 n, O* N4 K2 V$ u9 U
- contract SeeNah with Misc {% N* E+ S* ]/ S: S
- // ...
1 [! v ^+ E" \! i2 w - }& Q& y5 M, _8 F( t* S
4 `( v- b% B" X" B! f- ]- @interface("name_of_your_org - miscellaneous")+ [# K: h2 v- ]) `! C* O7 t
- trait Misc {; l, _6 ]' d" ?1 n
- // ...' h6 u3 V, I+ R3 @5 W9 }/ k
- }
复制代码
* M7 U- v# T' d5 e1 i' w
/ x( P9 p. [8 _$ R1 s8 j
0 r( o5 a; P$ o! _7 T* P. N- Q" t+ q: V/ m( ?; s S
标准库中的一些traits也定义了它们的接口:2 P, x0 ?2 Q) J& X) l+ J
# M! g$ T+ F6 ]- Ownable trait 指定"org.ton.ownable"
- OwnableTransferable trait 指定"org.ton.ownable.transferable.v2"
- Stoppable trait 指定"org.ton.stoppable"
- Resumable trait 指定"org.ton.resumable"
' i. S' Y( Z4 j7 u" v! `! \
/ x: E7 v0 P7 E8 l# g3 `4 n要启用 supported_interfaces getter 生成并在 Tact 合约中使用 @interface() 属性,请修改项目根目录下的 tact.config.json 文件(如果该文件不存在,则创建该文件),并 将 interfacesGetter 属性设置为 true。/ Y* n, \( O6 W0 j
' U, K; i. q6 v7 d& {% \0 C* w如果您的项目基于 Blueprint,您可以在合约的编译配置中启用supported_interfaces,这些配置位于名为wrappers/的目录中:
" k8 |9 R8 K: ?+ x8 \# k9 a5 h5 v) y& | T! b
/ u0 e) D4 \$ A9 G: D+ S
- import { CompilerConfig } from '@ton/blueprint';
/ |: O5 U/ B( l. ~" _5 N
$ r3 K7 m6 M6 |7 {' j- export const compile: CompilerConfig = {
% B- W7 M7 q5 y3 G' e2 ^- e7 d: Z - lang: 'tact',0 J; q( K; m/ {; U4 v
- target: 'contracts/your_contract_name.tact',2 b4 F' T6 y7 ?7 g# N/ p+ \
- options: {
$ w7 ]( i& J8 n. [ - interfacesGetter: true, // ← that's the stuff!. s6 c L! J% @
- }+ L% v3 a( b g5 S& C
- };
复制代码 除此之外,tact.config.json 仍然可以用于 Blueprint 项目。 除此之外,Blueprint 项目中仍可使用 tact.config.json。 在这种情况下,除非在 wrappers/ 中进行修改,否则 tact.config.json中指定的值将作为默认值。
4 v1 v* g, }- v, _- _
v, I( Z' i [6 Q& Y6 r v' I& x0 y3 e/ ?, e3 \: `
注意
/ Y" }/ R/ J3 {0 q, @8 `" s' q% e N' V8 T& t0 n; N3 z
如果你有 separateCompilables 选项设置为 true 在 blueprint.config.ts,然后.compile.ts文件将位于compilables/目录中,不位于wrapper/。
# o; K+ c6 k/ l7 C+ v% `; Q
8 |' v, P' t9 G3 z5 R警告
: B7 `! [! b4 F1 w4 j0 y ]! Q4 x3 v
请注意,添加接口并不能保证合约实际实现任何特定功能,也不能保证以任何特定方式实现这些功能。 这只是一种可验证的链外承诺,即合约中可能包含某些特定代码。 您应该相信但要核实这些说法。
! z6 Y+ `+ s4 \
) K" [' v8 f+ R) y此外,不同接口之间也不能保证不会发生名称冲突,尽管这种情况不太可能发生,因为即使是 SHA-256 的前 128 位也能提供足够的抗碰撞性。
, f- Y/ z$ K. T" M: e D5 @% Y: c7 _7 _- q2 ]- h. V
' S- E9 _3 T" ]
8 X/ A3 _# j# k. T4 L& B/ R
持久状态变量
! C7 w- ]7 _0 {) I P( X合约可以定义在合约调用之间持续存在的状态变量。 合约可以定义在合约调用之间持续存在的状态变量。 TON 中的合约支付租金 与它们消耗的持久空间成正比,因此鼓励通过序列化进行紧凑表示。. V* d) G' e" s
; ~! { d% T+ Z7 V0 ^% p* h
5 A, S, a. j, ^2 e
- contract Example {1 ?5 X, L X+ \( h
- // persistent state variables; A2 ? L* ~, p. {8 E [
- val: Int; // Int+ `% J, t. {4 \% g% G, s. |% p5 ]7 X
- val32: Int as uint32; // Int serialized to an 32-bit unsigned; ?" [- F* \! J& y# X
- mapVal: map<Int, Int>; // Int keys to Int values
$ [' H G; f6 G6 Q6 S# k - optVal: Int?; // Int or null
- d% N% p4 x% j j4 V# F - }
复制代码 状态变量必须有默认值或在 init() 函数中初始化,该函数在部署合约时运行。 唯一的例外是 map<K, V> 类型的持久状态变量,因为它们默认初始化为空。% z; i1 {# Y& o5 V9 j5 n- {. \
5 m8 J. E% p2 w' I5 `/ q7 x$ L# L. \
% q6 h- x+ o' A) J3 \; W+ R! P: Z8 S注意
4 o. U K0 a8 j) c1 q; z6 k( v1 s: r2 z+ y- e6 n
请注意,Tact 也支持非持续状态的局部变量,请参阅:变量声明。
) G% H. z5 b# i5 x+ J# f) u) U& L3 G! R7 a( }0 e
+ q; Q( ^2 G% v0 L8 W& s) D |
|