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

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

回答

收藏

7.3 Jetton 钱包合同

开源社区 开源社区 8451 人阅读 | 0 人回复 | 2025-03-15

在本课中,我们将回顾钱包智能合约. 正如你所记得的,一旦我们铸造了Jettons,钱包智能合约就会被矿工智能合约部署。或者,当我们进行转账时,钱包智能合约会被另一个钱包智能合约部署。所以,我们还是要做一定的准备:

! q5 \' `) z; k/ ^: a
  1. int min_tons_for_storage() asm "10000000 PUSHINT"; ;; 0.01 TON
    6 s+ p6 m5 x0 U6 a* K
  2. ;; Note that 2 * gas_consumptions is expected to be able to cover fees on both wallets (sender and receiver)
    ( e( {) ]1 L7 f+ O' K. C( N
  3. ;; and also constant fees on inter-wallet interaction, in particular fwd fee on state_init transfer
    7 @5 j3 }, m( f/ I/ L: n* l
  4. ;; that means that you need to reconsider this fee when:+ k2 @# v5 @( [6 W3 f8 M
  5. ;; a) jetton logic become more gas-heavy
    ; K6 i" F/ o/ ~- \
  6. ;; b) jetton-wallet code (sent with inter-wallet message) become larger or smaller
    7 I+ O% g/ }" T, S& l7 I9 F4 B5 Q8 Y
  7. ;; c) global fee changes / different workchain+ U# D5 J6 |2 [2 v: x
  8. int gas_consumption() asm "15000000 PUSHINT"; ;; 0.015 TON
复制代码
您可以在代码中看到,我们当然需要一定数量的Ton。我们需要从每笔交易中扣除TON,以确保存储支付有最低可用量。我们还对 gas_consumption 做了一些常数— 这只是一个近似值。
3 Q: T( x: ]- A8 O: K
  1. {-
    5 B% I+ ], P% r# s, q; S" w5 O
  2.   Storage
      n8 x9 r/ M7 b4 l# N! B
  3.   storage#_ balance:Coins owner_address:MsgAddressInt jetton_master_address:MsgAddressInt jetton_wallet_code:^Cell = Storage;) t( l- @/ A8 O) v  ]& z% \. b' \' C% s
  4. -}0 ?0 T3 @6 ^; y# M; z# x
  5. / g% Z5 v6 T- h, y. P
  6. (int, slice, slice, cell) load_data() inline {
      y4 S/ K* N& X) A" }3 ^
  7.   slice ds = get_data().begin_parse();
    9 B: Z0 B7 d' b
  8.   return (ds~load_coins(), ds~load_msg_addr(), ds~load_msg_addr(), ds~load_ref());) _& B( _  b$ ], m# Y8 I5 H
  9. }
    9 C2 N4 ]6 Y+ f

  10. * M  i% ^$ O" [# K' g
  11. () save_data (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) impure inline {
    * i& k# T( u3 {9 u
  12.   set_data(pack_jetton_wallet_data(balance, owner_address, jetton_master_address, jetton_wallet_code));+ R0 S0 \+ t8 w8 r. H( J% S9 G
  13. }
复制代码
当然,我们还可以这样做 load_data, save_data 就像我们一开始封装读取存储的逻辑一样。让我们来看看我们实际部署合约的矿工。在这里,我们将 Jetton 钱包数据从 utils: 输入余额、所有者地址、主地址和 jetton_wallet_code。我们可以在这里看到代币、地址、地址和Cell:
  1. return (ds~load_coins(), ds~load_msg_addr(), ds~load_msg_addr(), ds~load_ref());
    8 z9 q2 W' M- j
复制代码
因此,代码被存储为一个Cell,我们把它放在这里。基本上,这就是这个钱包合约首次执行后的初始数据示例。这里有哪些功能?我们有 send_tokens 功能, receive_tokens 功能, burn_tokens功能, on_bounce, 而这一切都是由我们来定义的。这不符合标准。recv_internal 是核心部分,在这里我们可以看到一个标准的东西。
因此,一旦信息中的操作代码与传输操作代码、内部传输或刻录相匹配,我们就会运行刚才讨论过的函数:
  1. int op = in_msg_body~load_uint(32);
    # h2 x. h7 q  s  G% [6 }, s5 n
  2. ' j, b) f6 G+ z( n
  3. if (op == op::transfer()) { ;; outgoing transfer! k( L2 `, l! J' \
  4.   send_tokens(in_msg_body, sender_address, msg_value, fwd_fee);# Y$ d5 p( S! y  D8 c$ e, i
  5.   return ();: ?% T" [/ l6 u2 E0 I5 {- m; L% D
  6. }
    . h5 c% a3 F" W, C$ t+ h
  7. ) P8 c* ]& s1 s" X) ?3 U1 @1 q
  8. if (op == op::internal_transfer()) { ;; incoming transfer( v- T* m, c2 n$ P& ^
  9.   receive_tokens(in_msg_body, sender_address, my_balance, fwd_fee, msg_value);5 U" I0 I. B- `! l
  10.   return ();- `" ]8 I* F; c
  11. }- A2 r( \1 Q  n6 |) l
  12. . ]5 _( p( V, B* n' V; f
  13. if (op == op::burn()) { ;; burn
    & ]4 U4 N" u9 @) B5 X  v$ q
  14.   burn_tokens(in_msg_body, sender_address, msg_value, fwd_fee);6 Q) ?9 T/ q$ T5 S; A) p
  15.   return ();/ c4 `! q0 P1 N
  16. }
复制代码
在这里,我们必须回顾五件核心事情。一是这里的准备工作:当我们第一次刚刚看到 in_msg_full 并开始读取它时会发生什么?因此,我们要弄清楚如何处理它。然后,根据操作代码,我们对转账进行检查,即 send_tokens 函数。然后,我们将回顾 receive_tokens 功能,也可在此处获取,以及 burn_tokens. 我们需要做的最后一件事是 getter 方法,但这是最简单的方法。因此,我们从逻辑开始 here:
3 K- t& {& b, Y2 ^
  1. () recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
    ) G) c4 w1 |) r- L$ X' n
  2.   if (in_msg_body.slice_empty?()) { ;; ignore empty messages3 v0 v8 m$ r1 i* ]
  3.     return ();2 [8 l6 u$ N* U- S4 T  ^1 q% V
  4.   }
    . T  U7 w* X! ]8 J. m" W2 Q

  5. & t% Z% U. G7 ?/ k7 Z- @0 |9 u
  6.   slice cs = in_msg_full.begin_parse();
    9 t" a* N# a7 t
  7.   int flags = cs~load_uint(4);8 h% \  ^4 n4 w
  8.   if (flags & 1) {
    8 A" X) s1 Z/ t, `
  9.     on_bounce(in_msg_body);
    ( A& n- z% Z2 O
  10.     return ();% g- ^% k9 E' ~2 v/ W
  11.   }  m' x& C4 t# w
  12.   slice sender_address = cs~load_msg_addr();: r" \  ^0 g$ j0 Z, w! a6 J! j) {
  13.   cs~load_msg_addr(); ;; skip dst) R# P  s! t1 B( t
  14.   cs~load_coins(); ;; skip value
    / ^+ i& |. n$ O! B, X
  15.   cs~skip_bits(1); ;; skip extracurrency collection
    9 F' t, @3 u. V' T. K: }1 e
  16.   cs~load_coins(); ;; skip ihr_fee$ c  B( d$ ?6 [* a* a
  17.   int fwd_fee = muldiv(cs~load_coins(), 3, 2); ;; we use message fwd_fee for estimation of forward_payload costs
复制代码
同样,我们不想处理空正文的信息。因此,我们开始读取 in_msg_full 来查看标记。我们将再次检测标记是否显示这是一条被退回的信息。现在我们要处理它,稍后我们将查看它。然后我们找出 sender_address 是什么。然后我们跳过一些信息,因为我们要把 in_msg_body 传递到相应的函数中。因此,在这里我们并不关心这个问题。
但接下来,我们要关心的是 fwd_fee. 在这里,我们估算出上一笔交易支付了多少钱,现在我们想知道这个金额,以便在合约中实际保留一定的金额。因此,我们要估算下一笔交易的费用。这是一个有趣的逻辑,你必须学会。
你已经知道如何撰写信息了,因为我们在前面的章节中已经做了介绍,包括 send_draw_message. 你已经知道 send_draw_message 接受一个包含所有这些参数的Cell。这就是为什么我们可以在这里读出它的原因,因为这些参数是以比特形式存在于这个信息Cell中的。
  1. int op = in_msg_body~load_uint(32);/ h& S3 `$ T3 B% [1 ]

  2. 4 w1 `) Y, p1 w; m; A# x
  3.   if (op == op::transfer()) { ;; outgoing transfer
    : Q$ c2 T. r2 X% ~, W
  4.     send_tokens(in_msg_body, sender_address, msg_value, fwd_fee);4 M6 _  ^% |6 u. H  D8 k% @
  5.     return ();7 @, }6 K8 Q! ~# C$ s. y  I) g, d
  6.   }
    $ x! Z9 k; s; ~0 p2 X# O. N

  7. & M) P( p& i0 P, {* j& B
  8.   if (op == op::internal_transfer()) { ;; incoming transfer" ]1 |# S* m+ q1 N; s
  9.     receive_tokens(in_msg_body, sender_address, my_balance, fwd_fee, msg_value);) [/ M. E- t, h) ?6 Q
  10.     return ();9 j; n: Q, N7 S
  11.   }! Y( l% n7 c+ l& `
  12. " s9 T$ o. r, L% L0 a8 j1 K
  13.   if (op == op::burn()) { ;; burn9 Z, s7 |! L/ ^
  14.     burn_tokens(in_msg_body, sender_address, msg_value, fwd_fee);  W  b2 q' ]' X( o* p% J- p6 W5 o5 D
  15.     return ();- E" g( P' h" }' u+ y0 ]
  16.   }
复制代码
好了,接下来我们来看看操作代码. 此时此刻,我们已经算出了界限,算出了下一次交易成本应该保留多少。现在我们来看看操作代码。我们可以看到这些 send_tokens 并传递 in_msg_body. 我们传递正文中的所有内容--发件人地址和信息值。你还记得吗 msg_value 就是这条信息带来了多少钱。而 fwd_fee 是估算值,基本上是这条信息之前转发所花费的确切金额,我们将其传递给 send_tokens 中。
转让代币
我们一起来看看:
  1. () send_tokens (slice in_msg_body, slice sender_address, int msg_value, int fwd_fee) impure {
      X+ A/ h' k" T, L+ R# x8 x
  2.   int query_id = in_msg_body~load_uint(64);
    1 s' V: p- i& o0 M
  3.   int jetton_amount = in_msg_body~load_coins();. j% b! w6 n2 ?  |8 O1 F  @/ |
  4.   slice to_owner_address = in_msg_body~load_msg_addr();. l+ B) f/ w0 d: [; r
  5.   force_chain(to_owner_address);
    ; {- G9 {+ Y7 [
  6.   (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
    $ A) r0 B& Y  }/ Q
  7.   balance -= jetton_amount;
复制代码
发送代币就在这里。所以我们读取 query_id 因为我们已经读出了操作码。我们得到 jetton_amount 然后读取地址。所以我们要执行 force_chain, 然后确保我们在正确的链上。然后,我们从存储器中读取所有数据。我们将余额减少一位数,现在我们要做一些检查。
6 c- ]2 r2 Y7 p+ X( d/ O7 G8 r' T4 j
  1. throw_unless(705, equal_slices(owner_address, sender_address));
    3 E5 c# R* P% R. a) K2 E  k1 {
  2. throw_unless(706, balance >= 0);
复制代码
因此,我们要确保 sender_address 是所有者。所有者是一些真实的钱包合约,是拥有这个 Jetton 钱包合约的真实钱包合约。它保存在存储器中。我们要确保向我们发送信息的发件人的地址与这个钱包的所有者地址相同。如果不是这样,我们就会出错。然后,我们要检查在减少 Jetton 金额后,余额是否仍大于或等于 0。
  1. cell state_init = calculate_jetton_wallet_state_init(to_owner_address, jetton_master_address, jetton_wallet_code);. ]1 A  t' p% V
  2. slice to_wallet_address = calculate_jetton_wallet_address(state_init);
    2 f* }4 V9 U  b4 M( m5 h
  3. slice response_address = in_msg_body~load_msg_addr();
    ) Q' G6 `8 V- W& A# |7 X3 F
  4. cell custom_payload = in_msg_body~load_dict();
      Q% {& D# S5 {0 Y/ J4 V. u
  5. int forward_ton_amount = in_msg_body~load_coins();
      I6 G% b. X) Z* c( l/ d
  6. throw_unless(708, slice_bits(in_msg_body) >= 1);8 n( k! l4 O: f" N: g/ S5 M
  7. slice either_forward_payload = in_msg_body;* Z& L# @" K' }) F: p
  8. var msg = begin_cell()
    ! J& |, w1 ]4 _- ?4 R& @
  9.   .store_uint(0x18, 6)
    ( f9 j, v: }: K3 O. U) C: G
  10.   .store_slice(to_wallet_address)
    0 h8 a$ i! ^, h
  11.   .store_coins(0)8 B% [: F( ^" I7 r4 H" v
  12.   .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
    6 m3 O4 _9 u$ G( K* l4 k
  13.   .store_ref(state_init);
    + x9 `8 X/ p/ m6 f
  14. var msg_body = begin_cell()
    9 ?- O7 n- V/ S# A
  15.   .store_uint(op::internal_transfer(), 32)
    # W: _* C0 Z2 v# A6 v
  16.   .store_uint(query_id, 64)0 a9 B! z+ `' `# L& K$ B& e
  17.   .store_coins(jetton_amount)
    " c  ]) W+ |  J6 B; q$ r# x3 |+ R
  18.   .store_slice(owner_address)
    % n) A% R# w# H! I
  19.   .store_slice(response_address)/ g6 |7 x8 ]# R+ p4 P- i: b  U
  20.   .store_coins(forward_ton_amount)2 P+ d1 P- q6 H7 ~1 R1 E! z" A
  21.   .store_slice(either_forward_payload)
    ( @# Y: O2 y' L" l; v( w, b- R
  22.   .end_cell();
复制代码
现在,我们开始准备发送用于部署此钱包合约的消息。我们使用 Jetton utils 文件夹。我们不会在这里深入讨论,因为你已经了解了它的工作原理。因此,我们计算 state_init, 找出我们要发送信息的钱包地址。然后我们读取 response_address. 那么 in_msg_body 将包含谁获得了访问权,以及在这一系列交易完成后,谁应该获得其余数据的通知。然后,我们就可以将 custom_payload 和 forward_ton_amount, 这样我们就知道应该在该交易中放入多少Ton。
于是,我们开始编写发送Jetton的信息。我们要发送Jetton的对象可能没有Jetton钱包合约。这就是为什么我们需要附加 state_init 到这条信息:如果没有,我们将部署合约。
因此,我们一开始就在标志上设置了相同的快捷方式。接下来是目的地。我们现在不放任何硬币,因为我们会在发送时再放。接下来,我们将组合所有这些功能--有些功能还无法使用,有些功能将由验证器重写。接下来,我们在信息核心之后编写信息正文。
这里有一些有趣的东西是你之前没有遇到过的。我们实际上可以继续这段代码,因为我们没有在 var msg 中结束Cell。我们可以继续把某些东西放到这个Cell中。
所以我们稍后会在信息正文组成后将 store_ref 放入该信息正文中。再次强调,这是内部转账。我们把 query_id,  jetton_amount, owner_address, response_address, 要转发多少Ton数,以及可能保留在 in_msg_body 中的转发有效载荷放在其中.
  1. msg = msg.store_ref(msg_body);& d9 }7 d* p. Q; U
  2.   int fwd_count = forward_ton_amount ? 2 : 1;4 w* w% H6 J5 T( p- R% n' E
  3.   throw_unless(709, msg_value >
    * ^; a6 K9 `% |( b9 n6 [
  4.                      forward_ton_amount +
    4 K* }6 O$ {  q9 w) E: m
  5.                      ;; 3 messages: wal1->wal2,  wal2->owner, wal2->response4 P! q6 M' _( `* x: s
  6.                      ;; but last one is optional (it is ok if it fails). [, h7 Y( z- X0 b& l8 e, y2 y; ^2 d
  7.                      fwd_count * fwd_fee +
    ) }+ {% c& |; Q7 X" \/ t# s7 b
  8.                      (2 * gas_consumption() + min_tons_for_storage()));
    , e" o( u# ?, K0 Z. f9 C
  9.                      ;; universal message send fee calculation may be activated here
      u$ T! C9 O: ]' S- w
  10.                      ;; by using this instead of fwd_fee1 w8 h1 x  N6 o, e1 Y5 D
  11.                      ;; msg_fwd_fee(to_wallet, msg_body, state_init, 15)
    . Y. F' ]7 x$ }% d! u6 f/ G8 j
  12. * m+ H, q- {4 o& M0 L1 ~4 Y  H
  13.   send_raw_message(msg.end_cell(), 64); ;; revert on errors
    2 N0 C* B$ n6 j: U, `
  14.   save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
    0 W( U4 h  V0 I! ~' t7 }: }6 f0 |
  15. }
复制代码
好吧,我们去 fwd_count. 我们在这里检查执行这笔交易后,我们的合约是否还有足够的资金存活。只有在这个时候,我们才会结束信息Cell并发送这条Cell。通过 64,我们将继续传递该消息中的资金。您应该在文档中了解更多关于这种模式和标志的信息,我们已经讨论过这个问题。当然,在完成所有这些操作后,您还可以保存新数据和新余额。
很好,这就是转账函数。主钱包将调用这个函数。例如,你有一个普通的钱包,比如 Tonhub 或 Tonkeeper。这个钱包合约将触发 send_tokens. 或者还有其他逻辑。但你必须是这个 Jetton 钱包合约的所有者,才能触发 send_tokens 操作。
接收代币
下一个是接收代币:
  1. () receive_tokens (slice in_msg_body, slice sender_address, int my_ton_balance, int fwd_fee, int msg_value) impure {$ Z* w) b; M* E% p; d
  2.   ;; NOTE we can not allow fails in action phase since in that case there will be0 A5 i0 G$ J. Z9 q+ I9 u$ Y# ]
  3.   ;; no bounce. Thus check and throw in computation phase.8 g- {5 p% F, I1 X3 R8 a; |
  4.   (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
    ) s4 G% Q# K% g5 I) o# z$ Z
  5.   int query_id = in_msg_body~load_uint(64);
    7 [) W& Y& C0 w. b: w
  6.   int jetton_amount = in_msg_body~load_coins();
    7 j0 O" |2 ]' C- ?! q
  7.   balance += jetton_amount;
    ; n5 \1 m$ y% S" N1 E
  8.   slice from_address = in_msg_body~load_msg_addr();0 _6 L# ~& E, @1 _
  9.   slice response_address = in_msg_body~load_msg_addr();
      |7 Q# a1 k+ c, K- q8 x) E
  10.   throw_unless(707,! N: L* k1 w( I0 Y2 Q' t8 C
  11.       equal_slices(jetton_master_address, sender_address)* `. v2 Q& A9 K% G3 b) i
  12.       |* h0 y% i, ?5 K$ m! B8 D
  13.       equal_slices(calculate_user_jetton_wallet_address(from_address, jetton_master_address, jetton_wallet_code), sender_address)" O! W% }, T+ v4 M2 F& f4 {
  14.   );' e5 X5 [# k9 U2 _9 z
  15.   int forward_ton_amount = in_msg_body~load_coins();
    2 B$ g9 h: p' ]; h' X$ M

  16. 2 a' h: C2 y5 x+ F4 M, X
  17.   int ton_balance_before_msg = my_ton_balance - msg_value;/ a. d) M# \$ v) e2 v) b
  18.   int storage_fee = min_tons_for_storage() - min(ton_balance_before_msg, min_tons_for_storage());
    0 W% }- g+ I: [: M& u/ `
  19.   msg_value -= (storage_fee + gas_consumption());
    % @+ J/ e- Y0 C5 O; G* N
  20.   if(forward_ton_amount) {
    : l: O. D9 n) w1 f- g
  21.     msg_value -= (forward_ton_amount + fwd_fee);
    ! \) m+ G4 J" z* R+ T2 r
  22.     slice either_forward_payload = in_msg_body;& E; b! I1 u2 N) f- |

  23. 9 T% v) r4 u& O0 d# j$ }) R
  24.     var msg_body = begin_cell()4 O/ n/ e8 x3 @6 y) v" t& u! `: A+ s
  25.         .store_uint(op::transfer_notification(), 32)
    8 |6 X6 f: [' F0 r8 T9 K
  26.         .store_uint(query_id, 64)
    & u; |, j0 g. t, A
  27.         .store_coins(jetton_amount)
    5 G2 _/ j7 A& a5 ?
  28.         .store_slice(from_address)
    9 E9 M5 {. l. H1 U# h( L4 f; V
  29.         .store_slice(either_forward_payload); H; N4 ?3 `  \1 v/ Y3 F! p
  30.         .end_cell();* F2 Z, y9 K3 W1 S- [
  31. # B0 @2 F4 `# o, {, S, m
  32.     var msg = begin_cell()
    ; `, q. u, [! l: g+ a
  33.       .store_uint(0x10, 6) ;; we should not bounce here cause receiver can have uninitialized contract
    . K4 Y! R2 p/ i/ F( U+ h
  34.       .store_slice(owner_address)
    " O7 N% |) ~. L! t% G
  35.       .store_coins(forward_ton_amount)
    4 l. R, D2 m; M' v. l! ]
  36.       .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)* U; w0 c) T4 a8 R
  37.       .store_ref(msg_body);
    * K4 D; n9 X, g; U" g

  38. 3 T& X( s: \# t
  39.     send_raw_message(msg.end_cell(), 1);; P9 S; V4 Q1 t
  40.   }0 o0 a9 Y' u7 e* s* A  {
  41. 0 e. Q0 H5 q) Y0 w
  42.   if ((response_address.preload_uint(2) != 0) & (msg_value > 0)) {
    : d: E, Q6 t1 k8 G
  43.     var msg = begin_cell()5 O# Y/ Y/ K- }
  44.       .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 010000
    7 u4 t- e* G+ b4 H7 L. O: ?9 `" `
  45.       .store_slice(response_address)
    ' C; r* ~5 f0 m. J
  46.       .store_coins(msg_value)
      G( _% h# U# X  n
  47.       .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
    $ ?; E( a1 \& b0 }# J0 R
  48.       .store_uint(op::excesses(), 32)7 w$ @6 l' Z% p, a
  49.       .store_uint(query_id, 64);: @  z9 c6 F0 H. b
  50.     send_raw_message(msg.end_cell(), 2);
      c9 b( g/ r5 |8 y8 S) X- l1 |
  51.   }
    ) R+ n: E: q4 S5 M: o, g; s) V% M7 \
  52. 8 N; F" a0 y! B& E1 H, D5 b' ~9 p( ]
  53.   save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);' P1 c) v: N; Z$ Z+ q# M! A
  54. }
复制代码
以下是接收代币的方法。同样,你要从存储器中读取所有数据。我们增加余额。我们要检查给我们发送 Jettons、发送增加余额信息的是主合约还是 Jetton 钱包的其他合约。然后我们读取 forward_ton_amount,以确保我们有足够的资金继续执行合约。如果有 forward_ton_amount, 我们用 forward_ton_amountforward_payload, 如果那里确实有的话。当然,如果存在访问权限,我们也需要将其发送出去。因此,如果指定了响应地址,我们就要编写一条信息,将该交易链上的所有访问权限发送到响应地址。然后,我们将新数据保存在这里。无论发生什么变化,都只会改变余额。好极了。这就是我们处理接收代币的方法。
8 i" w7 P& u  p  @# U! n7 w( j7 {. P. o0 l" W5 k
燃烧代币
还剩下两个问题:我们如何实际燃烧它们以及如何执行 on_bouce。你看,我们不会深入研究我们编写的每一行和每一个逻辑Cell,因为这将花费我们几个小时的时间来概述。我希望你能从更高层次理解逻辑。我们可以为每一个合约编写一个章节,来说明这里可以处理的每一个逻辑案例。但我希望你能理解高层次的逻辑,因为你不会自己创建这些合约。你只需要了解它们是如何工作的,这样你就会真正使用这些函数,因为大多数 Jetton 合约都在使用它们。你将知道如何在你的案例中使用它们。
  1. () burn_tokens (slice in_msg_body, slice sender_address, int msg_value, int fwd_fee) impure {7 R5 v6 @3 t" }+ p: l. L
  2.   ;; NOTE we can not allow fails in action phase since in that case there will be5 a% b* o+ R0 x. x4 S' M
  3.   ;; no bounce. Thus check and throw in computation phase.
    $ T7 d/ l/ `/ L
  4.   (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();/ d$ v$ h) d3 ^5 P2 G
  5.   int query_id = in_msg_body~load_uint(64);
    " L9 c+ j0 Q* J8 x
  6.   int jetton_amount = in_msg_body~load_coins();
    0 Q+ V* s! L' m' m' ^
  7.   slice response_address = in_msg_body~load_msg_addr();
    8 K1 d" W& a0 \
  8.   ;; ignore custom payload
    + u/ _+ `/ b4 I& u& }; ?
  9.   ;; slice custom_payload = in_msg_body~load_dict();
    - T& u# r' I8 E6 V. _6 Y. o& n
  10.   balance -= jetton_amount;* G0 w* D& O. V5 ~
  11.   throw_unless(705, equal_slices(owner_address, sender_address));+ e0 m0 p; X* p: t
  12.   throw_unless(706, balance >= 0);
    ' S+ f4 }8 }7 s+ |( \, U
  13.   throw_unless(707, msg_value > fwd_fee + 2 * gas_consumption());, g1 r6 D5 Q5 g0 D
  14. ; \" X8 g' l" u" C
  15.   var msg_body = begin_cell()
    ! V- a5 I# W: t$ ?, Z: d( Z' r
  16.       .store_uint(op::burn_notification(), 32), G( \# R" J, P/ E
  17.       .store_uint(query_id, 64)- j! K; ~2 [0 ~+ t6 R7 K
  18.       .store_coins(jetton_amount)
    5 C1 ^' o8 ^! R
  19.       .store_slice(owner_address)6 G- ^) \6 O2 i  s
  20.       .store_slice(response_address)$ A; s( o/ B6 ]# G! u9 N
  21.       .end_cell();- N; F+ S( [1 j* Q( x
  22. $ u" @/ ^' J5 G9 i; Y( F
  23.   var msg = begin_cell()
      k# q0 ?5 J* N5 L/ Z
  24.     .store_uint(0x18, 6)
    + T3 j# |9 Z: x" ]) J
  25.     .store_slice(jetton_master_address)- }5 w% f7 }& g+ ?# d
  26.     .store_coins(0)
    . a) ?5 S# v: d* a- B5 E3 J  |( W
  27.     .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
    ; K4 d0 F  M  ?/ t8 T
  28.     .store_ref(msg_body);2 A0 v  F; z* d4 B7 A' k9 f

  29. % }! \+ [6 g6 T& M$ o* j/ N
  30.   send_raw_message(msg.end_cell(), 64);
    " z* a; P' W0 j/ `+ T: M

  31. ' G% h: o0 I+ F* y# B
  32.   save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
    % N2 g* U( t' N! D0 s( O3 ^
  33. }
复制代码
基本上是这样、burn_tokens 就是为了减少。我们读取数据,得到 query_id, 然后我们检查发送者是否是所有者,如果余额仍然大于 0。因此,我们要检查发送者是否是所有者,余额是否仍大于 0。我们要确保附带的金额足够,在我们发送这笔交易后,合约本身将继续存活,并能够支付租金。然后,我们再撰写烧毁通知,就像我们检查它一样。
因此,我们有 burn_notification. 我们将发送 burn_notification 作为主合约的信息主体。我们写上了金额、所有者和回复。你还记得,作为响应,我们还实现了向谁发送响应,对吗?实际上,我们组成了信息Cell。我们将信息主体放在这里,然后发送包含这些信息和信息主体的原始信息。然后我们发送 save_data 因为余额发生了变化。
反弹
本课最后要复习的内容是 on_bounce:
  1. () on_bounce (slice in_msg_body) impure {
    & x( t& i2 L5 ]: a
  2.   in_msg_body~skip_bits(32); ;; 0xFFFFFFFF
    5 Y% T* W- z. d
  3.   (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();5 h3 _1 @, h4 i% v6 u: E
  4.   int op = in_msg_body~load_uint(32);
    . A6 p# Q3 {3 J. @+ b% u/ Z7 F
  5.   throw_unless(709, (op == op::internal_transfer()) | (op == op::burn_notification()));) o; Z+ q2 s4 a' p
  6.   int query_id = in_msg_body~load_uint(64);+ Q) G7 F( b# y3 h! a6 v( T8 L
  7.   int jetton_amount = in_msg_body~load_coins();2 i* S  k! i; U* y# `0 ?) V. b
  8.   balance += jetton_amount;
    % f0 d2 Z! X# w6 i5 b
  9.   save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);6 Y! d: M1 o3 {. d1 [! W
  10. }
复制代码
当邮件被退回时,我们该怎么办?跳转时会有一定的错误,因此我们要读取余额,检查操作代码是否为 internal_transfer 或 burn_notification 反弹消息。如果是这样,我们就必须把另一份合约没有接受的Jetton数量重新纳入我们的合约。
结论
这就是 Jetton 钱包合约的核心逻辑。我希望它不会让你感到困惑。这是一个很好的例子,让你真正读懂代码,看看它有多复杂。如果你花一些时间阅读这些合约,会对你有很大帮助。如果你不明白某些东西,比如Cell中的行或位,你可以很容易地分组阅读或阅读文档。本课的目的是让你更深入地了解 Jettons 的工作原理,这样你就能理解不同合约与标准交互的逻辑。
在下一课中,我们将仔细研究一种不同类型的代币,即非同质化代币(NFT)。这对你来说会很有趣,因为我们有collections合约和item合约。你们已经通过了这个非常困难的部分,现在你们明白了可以有多个合约。Jetton 不是一个单一的合约,而是一个合约系统。对于 NFT,你们也将非常感兴趣地看到它们是如何根据标准在底层工作的。我们很快就会再见面!

! G7 u$ X; S& f
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则