English 简体中文 繁體中文 한국 사람 日本語 Deutsch русский بالعربية TÜRKÇE português คนไทย french

简体中文 繁體中文 English 日本語 Deutsch 한국 사람 بالعربية TÜRKÇE คนไทย Français русский

回答

收藏

Tact 语言基础 | 合约

开源社区 开源社区 9422 人阅读 | 0 人回复 | 2025-04-01

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
  1. contract Example {
    # I8 |/ ^. E; l! F. F0 h" q
  2.     // persistent state variables" i7 A% W- ]" T: |$ A; w
  3.     foo: Int;5 R: U+ n, P. I! y4 p  X
  4. 3 H7 v  s: X9 O" Y+ y# N
  5.     init() {1 v6 d  n' Y  x4 \$ G- E
  6.         self.foo = 42; // <- referencing variable foo through self.
    * h! |+ V! `/ R3 x$ S# A9 B3 l" y
  7.     }
    5 e7 x' Z1 V& W
  8. }
复制代码
结构
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
  1. trait InheritMe {}
    " c, d3 }9 T# h/ e
  2. trait InheritMeToo {}/ g8 V+ G7 M" T0 P+ `. \

  3. ( N; i7 Z$ K" N" L1 T/ e* r- S
  4. // A contract inheriting a single trait
    0 W2 n$ L2 z4 }' m' c5 U9 g7 U
  5. contract Single with InheritMe {}. v( K+ Z$ r5 X- ?
  6. - x4 l3 T+ h' h8 K  H" |3 p
  7. // A contract inheriting multiple traits) @+ j6 f% t) A. `
  8. contract Plural with
    + S, F, v4 P" _1 q$ O
  9.     InheritMe,
    1 ?' [' J1 u) K3 T/ t8 |" P
  10.     InheritMeToo, // trailing comma is allowed
    # T1 l7 O/ N9 P) t" S! ]+ d1 \" V
  11. {}
复制代码
  {' 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
  1. trait Supe { omelander: Bool }# S/ e& d/ t( h
  2. # E! Z, G  t& x+ R
  3. contract Vot with Supe {; q: Z) U$ C) `& s3 W* Q6 v
  4.     init() {
    1 j9 H4 N. d* l/ K5 r6 u
  5.         self.omelander = true;
    ( f+ Y2 Z2 e" r% N* @! F' Q: z
  6.     }( V0 D- s; R( h* {7 F
  7. }
复制代码
如果在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
  1. @interface("His name is")/ U6 T( X# M2 S  h, b: Z  ], n4 N
  2. @interface("John")1 d4 b: j+ p' f5 T8 y% k
  3. contract SeeNah with Misc {8 f- [  ?  Y% W& Q* m
  4.     // ..." q' r. {% [6 ]" b, P2 l
  5. }$ {5 V1 N  I0 U# ~2 ]
  6. 0 a% d' j0 z6 J" k& H
  7. @interface("name_of_your_org - miscellaneous")
    ' v2 F% E5 D1 s
  8. trait Misc {
    ) \" y; o2 T* J: s  e% n
  9.     // ...3 z9 s* R9 V' [5 k! D9 q  q/ N4 r
  10. }
复制代码

# 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
  1. import { CompilerConfig } from '@ton/blueprint';8 }. i) A: p. A6 V$ A  o
  2. 2 o, k& N$ {  D  s; v
  3. export const compile: CompilerConfig = {7 a6 M, }+ o* l4 O5 m
  4.   lang: 'tact',; r: ~: n; X6 k7 ]% ^) Y2 H
  5.   target: 'contracts/your_contract_name.tact'," K1 [  r$ Z1 j4 p+ i# ?9 \3 E
  6.   options: {
    3 E. s7 T: P3 ^; R- m) H
  7.     interfacesGetter: true, // ← that's the stuff!1 p& O, m) N) A/ L; ~# I# ]
  8.   }
    $ A- Q3 N- K9 [  e6 T# r
  9. };
复制代码
除此之外,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
  1. contract Example {$ j7 |) @* \$ l/ G# _3 c0 s
  2.     // persistent state variables5 F6 E% K& q6 G
  3.     val: Int;              // Int: X5 _; ^' v/ a$ H
  4.     val32: Int as uint32;  // Int serialized to an 32-bit unsigned
    9 b% g6 `% z! B6 Y
  5.     mapVal: map<Int, Int>; // Int keys to Int values2 |2 z7 ?! \2 P$ U7 b
  6.     optVal: Int?;          // Int or null
    6 F# T2 ~, h6 F1 X$ t& h
  7. }
复制代码
状态变量必须有默认值或在 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
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则