本帖最后由 riyad 于 2025-3-15 04:26 编辑 % ]: V* N1 K. K9 t! {
* S" c& _, m7 U2 I2 o我们将从 Jetton 标准。Jetton 也被称为 "同质化代表标准",编号74。 在 this repository 的所有标准中,有关于 Jetton 合约必须如何运作的全部描述。您可以在这里看到两个合约,一个主合约(或矿工合约)和一个钱包合约。
1 P3 J! ~$ ]% x9 m
我们来看看钱包智能合约的规范. 钱包智能合约必须处理某些信息。例如,传输和解释所发生的一切以及传输的原因。它必须处理烧毁信息。然后,它还必须使用 get_wallet_data 方法来返回有关自身的数据。这就是实现钱包合约的主要方法。 # t) q' D* d: u
这里是主合约. 在这里,我们不放代码,我们放的是应该以某种方式实现的功能,因此其他合约可以与您的 Jetton 合约进行交互。它们会清楚地知道如何与你的合约互动,因为你有主合约。矿工实际上是在铸造所有这些jettons。您还会为每个Jetton用户和外部服务制定钱包合约,这些合约有时会与用户的钱包合约互动。有时,它们也会与你的主合约、矿工合约发生交互。为了让你不需要每次都搞清楚它是如何运作的,它们只是同意了它们必须如何运作的标准。例如,钱包希望显示余额,而不希望处理每种 Jetton 类型。
9 x( K! x( x7 v2 ~$ ]: \' S$ W
我们假定,如果某些功能不符合此处解释的 Jetton 标准,则不能被视为 Jetton - 无论是钱包、浏览器还是其他服务。您必须遵守这些规则,才能被视为 Jetton。正如我之前提到的,这只是一套合约。 / x- ~9 ?" W. {3 }2 {, s
到目前为止,我们已经学到了合约编程的基础知识,但这里还有更复杂的逻辑。阅读 Jetton、Master、Wallet 合约对你理解和实际了解其工作原理非常有帮助。通过这种方式,你可以了解Cell是如何组成的,你可以在自定义合约中实现哪些复杂的逻辑,即使这些合约与 Jetton 无关。Jetton 和 NFT 合约是让你了解可以做什么、如何使用语法以及如何实现一些复杂功能的绝佳途径。 让我们深入研究一下 Jetton 合约的代码。正如我所说,有两种合约。我们已经有了实现它们的最佳实践。你不必使用这些代码,可以自己实现,但必须符合标准。
' M& Q5 j( o' k2 L6 c
$ \# t9 \1 J* W* I) T
我们继续。我将克隆代币通证合约标准实现的存储库: - git clone https://github.com/ton-blockchain/token-contract.git5 y+ o$ g5 {+ t) }8 S# m
复制代码我们从 Jetton 矿工开始,因为它是初始化其他一切的核心合约。一开始我们看到的是存储方案。 - ;; storage scheme& J' X5 E3 V5 F
- ;; storage#_ total_supply:Coins admin_address:MsgAddress content:^Cell jetton_wallet_code:^Cell = Storage;
7 j8 B/ \2 F* j$ P3 f/ p( J - * Q( X7 u5 C) T+ @( @
- (int, slice, cell, cell) load_data() inline {$ b/ }: E% x2 V' _6 K8 B
- slice ds = get_data().begin_parse();4 ^$ e& O4 s7 x, p4 ]2 h6 l* c
- return (
" T5 o# Y' O5 U4 P. |9 r - ds~load_coins(), ;; total_supply
8 {& v W: C/ z7 L3 a - ds~load_msg_addr(), ;; admin_address
+ c: \! D c; F! A - ds~load_ref(), ;; content
1 ^, t+ i5 l4 c9 J/ b5 K - ds~load_ref() ;; jetton_wallet_code3 o3 }5 F7 v# C$ j& v% u+ q
- );* M( N3 Y) M7 [" \6 d4 t
- }. A- b( T( z$ h1 K
- 2 H- L4 ^1 L6 O# Q! e6 Z
- () save_data(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) impure inline {
0 _$ q3 K% m8 W4 X - set_data(begin_cell()
( W' @$ H! u( q; r. K$ m/ |8 M1 v - .store_coins(total_supply)
' X- `5 H$ y# o# G6 j) E - .store_slice(admin_address)5 L% M- I% Y( Z2 g9 Z8 i
- .store_ref(content)
( H3 f: V- l' C; U0 Z - .store_ref(jetton_wallet_code)
+ o2 E: s9 v$ c: T3 L4 M' P8 W" z1 x. @ - .end_cell()
$ o" d5 A) b" K( d - );) {* |$ T" V" e- s1 H; d
- }
复制代码我们已经知道这个逻辑封装了读取和保存存储中的数据。我们可以在代码的任何地方使用这个函数,以简化读取数据的过程。首先,我们有 total_supply. 我们保持 admin_address — 它是整个合约的管理者,有些信息只能从管理者那里接受. 我们看到 content — 基本上就是描述通证中独有内容的数据。在这里,我们还可以了解一下数据标准--这些数据应该有一个标准,包括标题、描述和其他一些东西,这样浏览器和其他服务就可以使用你的合约元数据向用户展示他们正在使用的 Jetton。
! j+ a) o9 {+ m3 b: B
最后是 jetton_wallet_code. 从第一课中,你可能已经知道合约是有比例的。我们有一个主合约--这就是矿工。它在挖矿时会部署钱包合约。然后,钱包合约也可以部署其他钱包。因此,如果我要给一个还没有钱包的人汇款,我就会用代码发送。因此,我们的钱包合约也会保留钱包代码,我们马上就会看到。
, n6 ~8 [4 _, T' n
需要注意的是,对于链外内容布局,应将第一个字节存储为 0x01,这将向任何人显示此 URI 指向 JSON。因此,你基本上可以对读取这些数据的任何人说 "嘿,我有链外数据。看看这个链接,下载 JSON,你会在那里找到一切"。 但如果是在链上存储,就必须把 0x00. 这样你就可以看到键值字典存储在内存的其他部分。这基本上就是一个如何优化的技巧。你可以自行决定,但重要的是,你要知道我们所说的内容Cell内存储的是什么。 ) D: C$ i- A2 ^& L/ r5 _
从存储器读取数据并将数据保存到存储器后,我们将 mint_tokens. 这是一个实现铸造功能的函数,我们只需在这里定义它。 - () mint_tokens(slice to_address, cell jetton_wallet_code, int amount, cell master_msg) impure {9 b5 R' @. ]: u" l7 Y
- cell state_init = calculate_jetton_wallet_state_init(to_address, my_address(), jetton_wallet_code);
3 F! M* p2 w L6 C3 k) g+ f. z4 t - slice to_wallet_address = calculate_jetton_wallet_address(state_init);
5 H; W1 K7 Y& Y6 @; Q F9 x* R - var msg = begin_cell()
0 l/ D- v, w* X5 ]" @ - .store_uint(0x18, 6)
, j5 W9 w) k8 K5 j9 @4 U - .store_slice(to_wallet_address)
. u* Y8 c6 j/ W+ M - .store_coins(amount)3 u9 `6 E8 b4 k7 \1 U
- .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
( q u# k k* ^, Z4 z2 y1 Z - .store_ref(state_init)
" E/ J0 P( v' i - .store_ref(master_msg);) G+ r2 [* y& j
- send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
% i* m- E3 I6 o1 u - }
复制代码我们的下一个东西则是 recv_internal, 这是任何智能合约的核心功能。在这里,你实际上读到了信息的第一个参数。我们很快就会解释。 2 C/ ?, l: n6 t% W. }3 r7 n5 U: Y
- () recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {+ s% i& t7 ?: A
- if (in_msg_body.slice_empty?()) { ;; ignore empty messages3 H) _6 g( `' x, Z( ]
- return ();# ]! i& g' e) f+ B/ {$ s
- }1 M- ~9 F5 l1 t. b1 g* F6 V
- slice cs = in_msg_full.begin_parse();, n. i$ Y& U6 j* i: U* W1 M
- int flags = cs~load_uint(4);2 `: a: e. a" G# [4 ?
- % v% W" l% i2 ?2 v
- if (flags & 1) { ;; ignore all bounced messages% v# j0 Y/ M: T% P! n! |
- return ();
9 K7 G+ b) p: z - }
; i; v9 `! g8 H - slice sender_address = cs~load_msg_addr();
7 M7 W) o" ]# L7 O8 c- @ - O/ H d0 a# A5 U2 T" d" u
- int op = in_msg_body~load_uint(32);; G0 p! }! f/ M
- int query_id = in_msg_body~load_uint(64);
1 v3 X& |4 O( }. B a
1 }* ~: j s [& N0 D- (int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data();
复制代码 然后,根据操作代码并接收更多扩展功能:8 t2 L* g$ M5 r
/ [. J+ e: U/ ]7 `/ @, p
- if (op == op::mint()) {6 R4 O0 L+ ], H5 G' l7 k9 k
- throw_unless(73, equal_slices(sender_address, admin_address));; j4 t8 s5 @6 H e7 h4 g' J1 t
- slice to_address = in_msg_body~load_msg_addr();
* K2 ^# L- U; ? - int amount = in_msg_body~load_coins();- Q- }1 [. j; J3 [ ^- t" G; s
- cell master_msg = in_msg_body~load_ref();
4 R( ]4 b2 _6 N8 | - slice master_msg_cs = master_msg.begin_parse();7 F1 T9 m+ ^- r' u7 \2 K
- master_msg_cs~skip_bits(32 + 64); ;; op + query_id
. @# {, f, x0 M, o4 m% F) _- I - int jetton_amount = master_msg_cs~load_coins();. I- {! l* C- x
- mint_tokens(to_address, jetton_wallet_code, amount, master_msg);
& m* |2 U4 s* o% g - save_data(total_supply + jetton_amount, admin_address, content, jetton_wallet_code);
% x+ L8 H- l6 @: | - return ();% ^0 S5 \: F! f
- }
2 \1 ?7 C. N+ t& r C1 s+ \* @ - # r) [& G1 w* V+ ]6 I& y5 l/ J& m' _
- if (op == op::burn_notification()) {- s+ i) Y9 [, E% `! d
- int jetton_amount = in_msg_body~load_coins();( |2 y, E$ @% W7 y! \3 x
- slice from_address = in_msg_body~load_msg_addr();
\6 r" n- m0 \9 T' A |" n - throw_unless(74,: Y4 i6 A" o) _3 n& K
- equal_slices(calculate_user_jetton_wallet_address(from_address, my_address(), jetton_wallet_code), sender_address)
2 F" v5 P! L' [, [( y6 _ - );
' f# X7 M# t v S& R) h - save_data(total_supply - jetton_amount, admin_address, content, jetton_wallet_code);
# H: r: ?" X J2 b# Y# r - slice response_address = in_msg_body~load_msg_addr();
5 R1 }5 V! c! Q4 a* G - if (response_address.preload_uint(2) != 0) {
4 y- b8 q& m" \! V% c; {0 W U - var msg = begin_cell()
, X3 B& M3 ~ }# D5 g4 Z* P - .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 0110003 |8 b+ e0 N, K; ~ M7 B' b T0 G0 g
- .store_slice(response_address)# q8 U( M$ n# o% \5 k
- .store_coins(0)- L& C8 m0 }9 q/ T6 y) z, V
- .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)/ u; K+ ?! q0 e
- .store_uint(op::excesses(), 32)
5 N- W# m. b! v4 ~' w& D - .store_uint(query_id, 64);& T! L. l/ N/ g* \4 U( }
- send_raw_message(msg.end_cell(), 2 + 64);6 q; ^$ b h% w1 B
- }
4 W% O& v: w4 x; d: z+ Z4 S - return ();4 i# X5 i+ J6 k/ [
- }: ?) a8 i( U# b! j+ Z. l
- * b* J# i: k5 K( p: c6 \
- if (op == 3) { ;; change admin8 [+ @6 g( } N, h4 ^2 l
- throw_unless(73, equal_slices(sender_address, admin_address));
- U( ]& k' l, U/ X3 R9 c - slice new_admin_address = in_msg_body~load_msg_addr();
8 L1 z9 E& ] f# }2 ] L - save_data(total_supply, new_admin_address, content, jetton_wallet_code);* C0 J, L8 g! H! r, J' P* r
- return ();
. M5 B7 s! A9 L# ^$ s - }6 }$ k; t8 W6 U0 N( T. C; w2 Y
- ' B# h( W5 D" y0 q5 H) i
- if (op == 4) { ;; change content, delete this for immutable tokens
( O8 z- t. u( q# E - throw_unless(73, equal_slices(sender_address, admin_address));. \/ z/ g( S x
- save_data(total_supply, admin_address, in_msg_body~load_ref(), jetton_wallet_code);
: f' P2 ^2 ?7 P5 X/ I - return ();
1 k1 Z" f3 t3 Y - }9 J R5 x) s' H, V
- 6 t' z+ B9 [' d/ O, ~
- throw(0xffff);
) ?* X) R% P0 L* q# h( W6 e3 [* R - }
复制代码例如,我们在这个函数中保留了铸造的功能。我们准备了一些数据,读出了一些重要的信息--我们也将讨论这个问题。然后我们运行这个 mint_tokens 功能。我们不会把所有代码都放在这里。例如 burn_notification, 逻辑非常简单,因此我们没有将其封装到另一个函数中。下面是核心结构。 让我们来概述一下一切。我们有 load_data, save_data — 这很简单,我们已经知道了。我们有一些处理任何信息的基本逻辑。然后,我们还有准备运行 mint_tokens 功能。我们还有 burn_notification 逻辑--这是当一些代币本应被烧毁时发出的信息。我们还有一些管理逻辑。例如,上面我们有 (op == 3), 表示更换管理员。在这里,您只需读取当前的管理员,进行比较,然后保存新的管理员。 这很简单,我们已经可以很容易地理解。你可以更改内容,也可以关闭删除这个逻辑的选项。如果你不使用可编辑的内容,或者你想让它成为可编辑的内容,其实你可以把它保留在这里。在这里,管理员可以保存带有不同元数据的数据,如你所见。这是一些基本的东西,只需要你稍加练习,就能理解它有时会有多复杂,以及你可以在这里使用从我们编写合约时学到的东西。 当然,我们还有 getter 方法,这里很简单。你只需返回从这里读取的内容,并将其提供给外部。所以你可以获得 total_supply, admin_address, content, jetton_wallet_code, 和获取钱包地址可以帮助找出某个地址的钱包地址。例如,我有一个钱包,我想获得一些jettons。但我想知道一旦我得到了 Jettons,我的钱包地址会是什么,因为 Jettons 不会进入我的钱包合约,而是进入一个已部署的钱包,即 Jetton 钱包合约。到那时,我需要找出我的地址。这个功能可以帮我做到这一点。 好了,我们再花点时间来了解一下里面的内容。这个逻辑非常简单。我们不想处理空的信息正文,所以我们返回并执行: - () recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
6 y# S$ u& e7 }5 w; i1 {, `. P - if (in_msg_body.slice_empty?()) { ;; ignore empty messages) n" p* `2 @/ i& }
- return ();
复制代码然后,我们要找出标记,这样就可以忽略所有退回的信息。我们还将在此处停止执行: - if (flags & 1) { ;; ignore all bounced messages
2 j$ J8 [7 f0 p: o. N% P+ Z - return ();" e7 d& S Q8 K) J- T
- }
复制代码然后,我们找出谁是发件人。此时,我们从 in_msg_full — 这对我们如何进一步发展非常重要。 - slice sender_address = cs~load_msg_addr();
, A1 P2 R, y* ~
复制代码你还记得我们是如何部署合约的吧?我们实际上是在计算 state_init,这正是这里要做的,因为这个函数要部署一个新钱包、新合约。因此,我们使用的这个函数在 Jetton utils. 是的,我们在这里定义了这个函数,并将一个Cell与 state_init. 大家还记得,我们在 TypeScript 中做了类似的工作。现在你可以在 FunC 中看到类似的内容。下面是 jetton_wallet_code, 接下来你必须打包数据。一旦有了合约,数据就是初始数据。我们将在下一课概述钱包代码时了解更多。在这里,你可以使用这个函数来打包 Jetton 钱包的数据。在存储方案中,我们将看到 balance, owner_address, jetton_master_address, jetton_wallet_code. 此时,我们将在Cell格中输入代码,形成 state_init, 初始状态数据。这是我们将要使用的函数之一。 让我们回到矿工身上。我们正在计算 state_init 因为我们需要将其附加到信息中。然后我们有 to_address, 因此,我们需要知道该把信息发送到哪里。那么合约的地址是什么呢?就像我们在 TypeScript 中做的那样,但在 FunC 中它也会做同样的事情。因此,这对你来说是一个很好的练习。这个 to_wallet 地址将是我们实际发送信息的目的地 state_init, master_msg, 和 amount. - slice calculate_jetton_wallet_address(cell state_init) inline {
! }# f; `7 T; Z - return begin_cell().store_uint(4, 3)
: W) z$ N. s- ^. ] - .store_int(workchain(), 8)
3 |$ z: [3 T7 O% w - .store_uint(cell_hash(state_init), 256)# k8 y$ z3 o" m- ?
- .end_cell()7 P" ~/ Q( s4 Q% |
- .begin_parse();
复制代码现在我们将使用 calculate_jetton_wallet_address 函数来计算钱包地址。在这里,我们正在做类似的事情。因此,正如你所理解的,如果 state_init 变化。但我们可以看到 state_init Cell格哈希值并不是全部,也不是整个地址。有一些标志,有一个 workchain 在这里我们也使用参数中的数字。默认情况下,我们使用零工作链。 在这里,您还可以了解地址是如何形成的。TypeScript 将我们从这种复杂性中解救出来,但你需要了解这一点,因为有时你需要编译相同数量的Cell,在部署合约时将Cell与实际发送的地址编译在一起。因此,查看地址是一个非常好的做法。我们在 TypeScript 中做了同样的事情,但这里是在 FunC 中进行的,所以你在这里可以学到更多。 因此,我们正在撰写地址。您可以在这里阅读有关每个位(bit)及其含义的文档. 但这是 workchain, 这是Cell哈希值,表示如果 state_init 变化。而 state_init 如果代码或数据发生变化,它们也会发生变化。这就像多米诺骨牌。这是一个Cell,如果你想读出它,就可以开始解析。这就是返回片段的方法。记住,在 FunC 中,地址总是一个片段。 - () mint_tokens(slice to_address, cell jetton_wallet_code, int amount, cell master_msg) impure {
& C* V/ {7 \: ~( t1 \0 n - cell state_init = calculate_jetton_wallet_state_init(to_address, my_address(), jetton_wallet_code); o$ c" p. a1 a7 d7 F/ M
- slice to_wallet_address = calculate_jetton_wallet_address(state_init);
% D% f L+ h- u8 P( i3 H - var msg = begin_cell()
6 x6 i. f+ F8 \ - .store_uint(0x18, 6). v1 K2 t" ?+ S' w5 D
- .store_slice(to_wallet_address)
! O+ i) v; o0 T% F0 `; @' i - .store_coins(amount)" a* F! I" h: ^* U0 F3 A; [/ Q
- .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
# ~3 K% G l9 k1 r# j2 y - .store_ref(state_init)
* C6 y4 m" J# h- x* @ - .store_ref(master_msg);
4 V- {9 H0 {6 H2 |8 f3 ~$ e$ o9 J: t - send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
! H. K# [6 g( a: P, @- n - }
复制代码 好了,我们回到矿工的话题。基本上,我们计算了 state_init, 并计算出钱包地址。 我们把要转发和发送的Ton数放在这里。我们还把 master_msg, 就是我们的内部转账信息。信息到达后,将由钱包合约处理。我们稍后再讨论这个问题。/ u: ]# Q/ a% |3 l
- if (op == op::burn_notification()) {
8 T; k" g( u2 ]# J$ k: X - int jetton_amount = in_msg_body~load_coins();
' c/ F+ E: N; d5 {1 H - slice from_address = in_msg_body~load_msg_addr();
- J5 S, @: p4 g- h) e# ^; |* t - throw_unless(74,$ J8 t- U d- @2 {' X
- equal_slices(calculate_user_jetton_wallet_address(from_address, my_address(), jetton_wallet_code), sender_address)/ s( U9 z2 `. `2 D' M% r Y3 Z$ K" p
- );# k) f' c& t+ |0 \
- save_data(total_supply - jetton_amount, admin_address, content, jetton_wallet_code);/ h5 f9 d0 v; j ?* F
- slice response_address = in_msg_body~load_msg_addr();7 p3 ~- d6 ]. |. E
- if (response_address.preload_uint(2) != 0) {
7 C! q9 N0 \) I1 C - var msg = begin_cell()
* ^5 n# V# @; r, P6 J+ N - .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
" W! d" \6 Q) K% A" p; ? - .store_slice(response_address)
8 U# K J& d+ f% }, _ - .store_coins(0)7 |8 E4 a- m2 A/ d
- .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)2 v a7 O. }( T) T+ v [: T
- .store_uint(op::excesses(), 32)& Z+ C D- [& V
- .store_uint(query_id, 64);" O" X( ]/ R& A- ]/ o9 }! ^# S
- send_raw_message(msg.end_cell(), 2 + 64);! O5 g) W! V1 G9 I2 d% j
- }) E4 k. X, n$ Y! k
- return ();
" t4 r, h, F$ X! q - }
复制代码 我们要快速浏览的最后一部分是 burn_notification。基本上,这是当我们收到要刻录的 Jetton 数量时。我们要找出发送地址--为此我们使用 calculate_user_jetton_wallet_address 功能。
' Y& B+ @4 O6 l% y |2 R; S3 y3 H8 P# |. `
4 c2 O" P" @ Z/ X9 M; u我们看看它能做什么。因此,有人在发送信息时使用了 burn_notification. 我们计算出应该烧掉多少Jettons。然后,我们要检查烧毁通知来自谁。让我们查看 utils:- slice calculate_jetton_wallet_address(cell state_init) inline {0 O& t9 D0 R; p+ a
- return begin_cell().store_uint(4, 3)1 P7 Y$ T9 {, Q4 f" K# k& i
- .store_int(workchain(), 8)9 t, ~( t9 I3 K+ g% Q9 ~
- .store_uint(cell_hash(state_init), 256)
- G& Z5 J% |( i, Q - .end_cell()
! |3 Q5 d4 U; p1 C: C3 }% e - .begin_parse();( P- _, [7 Z) |$ R$ e9 `( f
- }
; \& `9 o. u1 T9 l; i; H/ R3 B - 4 c" ]4 H. G! ~4 N; M# c) u: x
- slice calculate_user_jetton_wallet_address(slice owner_address, slice jetton_master_address, cell jetton_wallet_code) inline {
1 D) n# m/ o i; P- P. T. ` - return calculate_jetton_wallet_address(calculate_jetton_wallet_state_init(owner_address, jetton_master_address, jetton_wallet_code));0 C! W# I5 ?0 |+ S; e
- }
复制代码我们在这里计算所有者地址的 state_init 。让我们回到矿工的话题: from_address 将是所有者地址, my_address 是这份合约的地址,暨我们的矿工。然后我们有 jetton_wallet_code. 因此,我们要检查 calculate_user_jetton_wallet_address 函数的结果与 sender_address 相同。 无论是谁发送了这条信息, from_address, 我们都应该烧掉这些Jettons。因此,我们将计算该用户的钱包地址。我们要确保它与 sender_address相同. 然后我们打开这个函数,看看它能做什么。 它基本上使用了我们已经知道的另外两个函数。它正在计算地址并从我们在这里传递的owner_address、jetton_master_address (本合约地址)和 jetton_wallet_code获取 state_init. 从 state_init,中,我们计算 jetton_master_address, 这样就能确保发送这条信息的人,就是我们要烧掉Jettons的合约的所有者。 这样,我们就能从总供应量中扣除Jettons的数量,并保存这些数据。现在,我们可以在存储中看到供应的Jettons数量减少了。然后我们读取响应地址。根据计划,正文中必须包含响应地址--我们应该将剩余的Jetton交付给谁,并附上 op::excesses. 我们从这里开始组成正文。我们正在撰写无法被退回的信息,因此快捷方式改为 0x10. 基本上,我们使用 2 + 64 原始代码。因此,我们会向响应地址发送该信息附带的所有资金,以及 op::excesses. 基本上就是这样。我们已经像处理程序一样发送了两条合约信息,我们发现这些只是合约而已。Jettons钱包里没有魔法,它只是一个具有特定逻辑的合约,只要读懂合约的工作原理,就能给我们带来很多好处。下一课,我们将学习Jetton钱包,你将从另一个角度了解它,看看钱包是如何与它互动的。此外,我们还将回到 minter 有时要看一些逻辑,尤其是在部署时。我祝愿你在学习软件仓库的这一逻辑时好运。下一课再见,Jetton 钱包合约代码回顾!
8 B: _4 q% I1 A |