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

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

回答

收藏

Tact 语言基础 | 合约

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

Tact 中的合约类似于流行的面向对象语言中的类,只是它们的实例部署在区块链上,不能像 结构和信息 那样被传递。' c" U6 N5 e, h9 |9 ^

7 e1 Y* V' }$ \, K, r# i自引用! M1 }3 |2 |4 b: n: t8 g
合约和trait有一个内置的标识符 self,用于引用它们的字段(持久状态变量和常量)和方法(内部函数):0 p: D& D# }) @" n
  1. contract Example {% {' w3 b8 S4 m" Y4 p  q" s, E
  2.     // persistent state variables: W, B+ A( s9 k" ]: V
  3.     foo: Int;% i0 \* z  g- A1 c/ B
  4. # B* D% V+ f" Y5 |4 P6 ?$ `
  5.     init() {
    * K/ O2 b& b. ^4 k) r0 C
  6.         self.foo = 42; // <- referencing variable foo through self.
    , Z5 t: b( K4 j5 A( y' \8 {
  7.     }3 f7 X& l0 u% N3 Q) y7 _7 M
  8. }
复制代码
结构3 g4 ~& T! h) M1 m
每份合约可包括:
& ~7 {% A6 B' ^! R1 v" v6 m) W$ @0 V: v5 a
  • 可继承trait
  • 支持的接口
  • 持久状态变量
  • 构造函数 init()
  • 合约常量
  • Getter 函数
  • Receiver 功能
  • 内部功能
    . H6 Y: `' h9 V9 N; x3 i
. g8 Y, W6 J' |0 E) s

) ~% g9 Q5 V- P/ O% l
3 i! }1 F! L3 T/ G# y* {9 B# o继承trait, with. f( \% N4 ^% p9 o& a& }
9 J) z8 D, `+ H$ B; F4 @4 y" E* B2 k6 g
合约可以继承traits的所有声明和定义,并覆盖它们的某些默认行为。 除此之外,每个合约和trait都隐式继承了特殊的BaseTrait trait。) `: S+ e# D4 ]4 w' s9 W
& j: x( K9 t  F/ G4 H
要继承trait,请在合约声明中的关键字with后指定其名称。 要同时继承多个性状,请在逗号分隔的列表中指定它们的名称,并在后面加上逗号。: z. ^" `3 U, q) @6 E# S# y& ?

) E( e, ]) B$ B3 k' j6 ~* }7 u6 z/ k- V% \
  1. trait InheritMe {}0 A0 l: d: ]8 K, \1 F% ?
  2. trait InheritMeToo {}
    % c8 F9 F8 r5 v0 u, A8 U" {. `
  3. 3 o: d" n! E1 N: y6 q
  4. // A contract inheriting a single trait$ S) f' ]2 p9 z2 ^# d4 M5 @! t
  5. contract Single with InheritMe {}
    9 A. l" Q9 j' _4 r$ W" s

  6. / ^. d  S- A0 ^8 Q; y3 K" g6 \
  7. // A contract inheriting multiple traits; ]- V. t2 m8 ^& N
  8. contract Plural with
    # a/ [) V. H  w' s+ B8 Q
  9.     InheritMe,
    ; I& X$ W' j0 m% V
  10.     InheritMeToo, // trailing comma is allowed
    : O4 V! L9 ^) |7 ^6 H3 ~6 {4 z
  11. {}
复制代码
* u+ F. D# @; {9 t  Z, \# ^

1 l$ Q( ~7 T1 B0 W由于traits不允许有init()函数,因此继承了声明了任何持久状态变量的trait的合约必须通过提供自己的init()函数来初始化这些变量。
5 z- r9 u- _* o2 {& H5 v/ i, G. h/ X
  1. trait Supe { omelander: Bool }4 a, T. Z! s6 P. a
  2. ! N( k0 P; x# e! ~
  3. contract Vot with Supe {
    . u( d5 z5 e0 o( N; U2 V4 ?
  4.     init() {7 k" S, f3 B9 _$ F. O* y
  5.         self.omelander = true;
    5 e; X/ O: B6 {; `/ ^  V
  6.     }
    5 i5 P& p" V. ^% L: Y* E1 L+ |$ O
  7. }
复制代码
如果在trait中声明或定义了内部函数和常量,则可将其标记为虚拟或抽象,并在从trait继承的合约中重写。
9 ^# Q( |. H$ P- T% I% H! h6 C: K/ r( G  d) R( [
  N# o# M6 \4 ~' M2 U$ Y6 J
支持的接口,@interface(…)" S: g6 L5 z. Y( m
如果不查看源代码,就很难弄清一个合约是做什么的,有哪些receiver和getter。 有时,无法获得或无法访问源代码,剩下的办法就是尝试拆解合约,并以这种方式对其进行回推,这是一种非常混乱且容易出错的方法,其收益会递减,而且没有真正的可重复性。# Y( K9 H- @5 t- M2 i1 d
/ e& `. P. j: v3 x. Y
为了解决这个问题,创建了 OTP-001:支持的接口。 据此,Tact 合约可以报告 支持的接口列表,作为特殊的 supported_interfaces getter 的返回值。 该getter可以通过任何TON区块链浏览器在链下访问——只需指定supported_interfaces作为执行的方法,返回一个十六进制值的列表。) x% v5 M9 x( f9 y

& t  o0 Q1 R  u" q% u
8 x) B3 v& R: z* m0 M. }; D要声明支持某个接口,可在 contract 和trait声明之前添加一个或多个@interface("…")属性:
  \$ z$ U  \: `; T' Z
  1. @interface("His name is")# H$ ~6 n9 \. o+ R4 w
  2. @interface("John")! {" D- K0 f7 u6 h6 \4 j5 P
  3. contract SeeNah with Misc {. ]2 I% k, A6 {  x5 }
  4.     // ...
    3 G9 {+ I, I4 {6 t% J9 I2 h/ u: `
  5. }1 H2 L% |' r* Z
  6. " y* L1 J0 k# {$ ^9 R: l' s) \7 o2 f( z
  7. @interface("name_of_your_org - miscellaneous")! R. N  Y; W9 t! `, T5 r6 }
  8. trait Misc {4 w8 [9 h9 {: j% `1 B0 c: h( ]# M2 Z
  9.     // .... }* g2 d1 Q+ }& v; v  t* q; L0 A
  10. }
复制代码

% B* ~6 W0 B/ ?/ v% w, Z, q& Q2 B( R! e9 s; d) c* y% J5 c

7 d9 f2 Y/ o2 S6 K% S
7 m7 l& e. |( q. m) r( e2 ]* i标准库中的一些traits也定义了它们的接口:9 P$ G7 F; G8 G9 |; x

2 ^5 K9 ?9 s, Y1 k
  • Ownable trait 指定"org.ton.ownable"
  • OwnableTransferable trait 指定"org.ton.ownable.transferable.v2"
  • Stoppable trait 指定"org.ton.stoppable"
  • Resumable trait 指定"org.ton.resumable"* `" p% K" f7 a8 H

5 k) w; D  A' h* I要启用 supported_interfaces getter 生成并在 Tact 合约中使用 @interface() 属性,请修改项目根目录下的 tact.config.json 文件(如果该文件不存在,则创建该文件),并 将 interfacesGetter 属性设置为 true。
& q3 \. I% K  V7 G) J8 b2 c/ B* x* z- s7 \2 `5 e  i
如果您的项目基于 Blueprint,您可以在合约的编译配置中启用supported_interfaces,这些配置位于名为wrappers/的目录中:7 r" F. [0 ^3 V9 u, {1 q; I, Q

$ B. H  x) p# s
8 q$ z" h- a& Y2 R' N
  1. import { CompilerConfig } from '@ton/blueprint';5 _7 W( I0 n2 Y

  2. . A$ D( K* i: j+ m) A" J- g1 [
  3. export const compile: CompilerConfig = {/ Y8 h8 x7 {  b9 W3 s: {/ ]* H$ f
  4.   lang: 'tact',9 i9 ~$ V# e  n  ~+ X
  5.   target: 'contracts/your_contract_name.tact',7 E' L2 N# E, ^: K' H8 u
  6.   options: {( _* _: Q0 D2 b# O0 \4 v
  7.     interfacesGetter: true, // ← that's the stuff!
    9 F2 L8 h1 ^" t0 D, s' {
  8.   }
    & x! h: A6 r) I5 R
  9. };
复制代码
除此之外,tact.config.json 仍然可以用于 Blueprint 项目。 除此之外,Blueprint 项目中仍可使用 tact.config.json。 在这种情况下,除非在 wrappers/ 中进行修改,否则 tact.config.json中指定的值将作为默认值。
! h2 Y# v" k4 g% [
6 u! s0 E' F) F
& C4 r9 m0 q/ x1 `6 r注意
- d' w8 z$ X8 y6 o% C# v3 w
2 i/ _, Z6 u+ E; l  e8 R如果你有 separateCompilables 选项设置为 true 在 blueprint.config.ts,然后.compile.ts文件将位于compilables/目录中,不位于wrapper/。. @  a6 a& X$ ^0 B$ O

$ d7 ~6 [, J. F3 s- X& E# _4 B警告
1 B! C7 h5 `: _
2 _! D! o" Y( z9 m. n请注意,添加接口并不能保证合约实际实现任何特定功能,也不能保证以任何特定方式实现这些功能。 这只是一种可验证的链外承诺,即合约中可能包含某些特定代码。 您应该相信但要核实这些说法。
7 b) }% U# b, g2 J" U& ?! c+ z2 E" G) S0 f
此外,不同接口之间也不能保证不会发生名称冲突,尽管这种情况不太可能发生,因为即使是 SHA-256 的前 128 位也能提供足够的抗碰撞性。6 S+ m7 V8 [4 e7 N' H

6 b" w! i9 K* e7 Y2 V' x1 f& N0 ^7 z/ g, l
! o8 L5 S/ I! |/ T6 e
持久状态变量) A8 n8 O, v% G# R: S) q/ [
合约可以定义在合约调用之间持续存在的状态变量。 合约可以定义在合约调用之间持续存在的状态变量。 TON 中的合约支付租金 与它们消耗的持久空间成正比,因此鼓励通过序列化进行紧凑表示。
- s; e( D6 R7 v# a$ o! j' P. h6 ?3 L) ~

, e" b2 B; |/ c2 I7 f, `
  1. contract Example {
    & W6 l& K5 D4 T, Z
  2.     // persistent state variables
    % C" z. e6 V" v' Y+ ?
  3.     val: Int;              // Int
    / ]" v5 u1 f2 e- q& S
  4.     val32: Int as uint32;  // Int serialized to an 32-bit unsigned/ [; V' X2 W/ J% _( a, Z
  5.     mapVal: map<Int, Int>; // Int keys to Int values" m# S8 D, }: x2 c
  6.     optVal: Int?;          // Int or null/ Q0 X  s; x+ p* s
  7. }
复制代码
状态变量必须有默认值或在 init() 函数中初始化,该函数在部署合约时运行。 唯一的例外是 map<K, V> 类型的持久状态变量,因为它们默认初始化为空。
5 ~) I1 M7 x& A6 ]. C8 ?3 G) F
( F9 y* L/ x1 ~* X
' o. |. l, a7 d注意
$ S  B3 C$ S% [
& _; T, T2 R- L' C+ O- H/ `# X6 V请注意,Tact 也支持非持续状态的局部变量,请参阅:变量声明。& H; X! [8 Q- v7 h) P
" a' J2 @6 L& m) V2 t

  h4 z4 H- S! s8 {) Q2 O7 {
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则