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

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

回答

收藏

7.5 NFT 项目/SBT 项目智能合约

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

在本课中,我们将回顾经典的 NFT 项目的代码。此外,我们还将学习 SoulBound (SBT) Item。

" x" k* p  e! o1 [# ZNFT Item合约
让我们打开并查看 contract.
  1. int min_tons_for_storage() asm "50000000 PUSHINT"; ;; 0.05 TON: z: E' \3 I: P! k
复制代码
就像在前面的课程中一样,我们看到有一些常量显示了我们应该在合约上存储的最低代币数量,以便能够支付租金。
6 m# l6 _/ u: l/ S% ~, _
  1. (int, int, slice, slice, cell) load_data() {
    # E: g6 Z$ H  D7 K$ ]
  2.     slice ds = get_data().begin_parse();- H" k  o0 h# X1 ]2 Y' M& D
  3.     var (index, collection_address) = (ds~load_uint(64), ds~load_msg_addr());4 G) f* T- ?2 |  v' S3 R# Q
  4.     if (ds.slice_bits() > 0) {
    , u. N- T5 F8 P% G
  5.       return (-1, index, collection_address, ds~load_msg_addr(), ds~load_ref());6 Z# i0 c: U* D# p+ {- X
  6.     } else {  
    , G; w- V0 c' B$ b
  7.       return (0, index, collection_address, null(), null()); ;; nft not initialized yet" B. k6 }% i: Y4 }- B7 `
  8.     }
    2 K) k6 ]. O$ `3 C8 z
  9. }
复制代码
然后我们看到 load_data. 这个 load_data 与我们以前看到的有些不同。通常,我们只是从本地存储区读取数据,但在这里,我们有一些更复杂的逻辑。因此,我们打开本地存储的Cell进行读取,然后读出 index 和 collection_address. 我们知道什么是索引和 Collections 地址。  
然后,我们尝试读取 NFT Item的元数据。首先,我们检查它是否存在。如果是,我们返回 –1, 以及Collections地址、所有者地址和元数据。或者我们只返回 0, 索引和Collections地址时,没有所有者和元数据。这意味着 NFT 尚未初始化。基于这一点,我们将在后面的代码中做出一些决定。
  1. () store_data(int index, slice collection_address, slice owner_address, cell content) impure {6 F) H# w( F8 t. |+ @" w1 d& k8 y5 f
  2.     set_data(; z# ^% Y# d0 q! J$ C1 a  a+ O
  3.         begin_cell()
    ' y3 B/ a8 W( }
  4.             .store_uint(index, 64): t, O/ O2 d8 K  A5 F- y7 ]
  5.             .store_slice(collection_address)6 e5 d& B% B4 |' x" Y$ p- w
  6.             .store_slice(owner_address)
    : R1 p) q: g  F
  7.             .store_ref(content)7 n' A. q4 g8 E3 C, g
  8.             .end_cell()8 u  G+ b, V7 i- R* I, F
  9.     );9 X7 |4 s7 |/ E! ?6 i4 F
  10. }
复制代码
我们还看到 store_data 就像我们调查的其他合约一样,这只是正常情况。
7 w* i& r" \. u. h$ r7 B) ?
  1. () send_msg(slice to_address, int amount, int op, int query_id, builder payload, int send_mode) impure inline {1 ?& `' j4 x/ |# y9 I% k5 J0 Y
  2.   var msg = begin_cell()/ l1 q5 \! O/ C
  3.     .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 010000
    / M& F2 p8 [& v' Y3 r7 B; K
  4.     .store_slice(to_address)
    ; t: H# c% W! ^$ ?* ?* q
  5.     .store_coins(amount)* ^0 y$ X/ b. i9 D; |/ M3 _
  6.     .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
    ' y: T' t* @1 [3 l3 E7 A- H
  7.     .store_uint(op, 32)
    # @& o4 Y3 Z6 g5 D
  8.     .store_uint(query_id, 64);
    : t0 L4 E2 P0 a* s! Q8 F6 E) R$ O

  9. . X0 b- n; d& j4 |  E# ]+ U* f
  10.   if (~ builder_null?(payload)) {- c, ^4 h( b+ w. S
  11.     msg = msg.store_builder(payload);- U0 {  K: Y' H+ S" h: i
  12.   }
复制代码
在这里,我们封装了发送消息的逻辑,因此我们可以在合约的其他逻辑中使用这个函数。因此,只要我们想发送信息,就可以使用这个函数。它接受目标地址、我们应附加的金额、操作代码、查询 ID、可能的有效载荷(这次是以生成器格式)以及消息的发送模式。这就是我们发送信息的逻辑。
我们的另一个函数是 transfer_ownership. 让我们稍后再研究,一旦我们在 recv_internal 逻辑,因为它是这里最重要的功能。
  1. () recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {3 `' g# _8 I* @: Q
  2.     if (in_msg_body.slice_empty?()) { ;; ignore empty messages$ t  w6 B: }+ v) g3 H2 R) `- T
  3.         return ();
    . k7 V# m( x  x$ ^: V9 x  O# P, f) p
  4.     }
    # D* P3 v9 n" @
  5. & H0 }4 y6 O( i
  6.     slice cs = in_msg_full.begin_parse();
    ( j6 y( z" u! v* U
  7.     int flags = cs~load_uint(4);
    5 T6 ^& }% O9 T) ^) R6 D) Z
  8. ) ?7 y  Y, E0 k& [
  9.     if (flags & 1) { ;; ignore all bounced messages5 [. ?* m2 ]* L& W/ U1 ^: a3 `" u( _
  10.         return ();
    : p! S1 s% U6 H
  11.     }
    # D: B7 \0 l. F- H8 t2 g. \
  12.     slice sender_address = cs~load_msg_addr();# R: W+ v+ G5 X' I, A0 v* q
  13. 9 `0 `/ g, D( u
  14.     cs~load_msg_addr(); ;; skip dst
    ( i! `/ P  [5 j
  15.     cs~load_coins(); ;; skip value
    * I- O" X$ S6 U6 b
  16.     cs~skip_bits(1); ;; skip extracurrency collection# l0 h; W: E+ p2 D
  17.     cs~load_coins(); ;; skip ihr_fee5 I& x# d- q0 Z0 N% e, Z9 Q  x- z
  18.     int fwd_fee = muldiv(cs~load_coins(), 3, 2); ;; we use message fwd_fee for estimation of forward_payload costs
    8 C0 u/ s) X# e8 {
  19. * R  P* v" ?9 L0 j7 j, d
  20.     (int init?, int index, slice collection_address, slice owner_address, cell content) = load_data();
    4 b! c- z2 h8 @# }0 m# x, C" b( F
  21.     if (~ init?) {
    0 S# y' P# u9 L( W
  22.       throw_unless(405, equal_slices(collection_address, sender_address));$ e# r0 P8 X/ U; K5 N
  23.       store_data(index, collection_address, in_msg_body~load_msg_addr(), in_msg_body~load_ref());
    2 Y7 ]; X$ M1 ~% D
  24.       return ();
    + ]/ ~; G5 `. K! |& U
  25.     }
复制代码
我们来看看它是怎么做的。我们再次忽略空的 in_msg_body 消息,读取标志,忽略退回的信息,并检查发件人是谁。现在我们跳过一些内容,比如信息的目标地址之类的,因为你已经知道这个Cell的内容是什么了。接下来是 fwd_fee 再次根据上一笔交易估算发送下一笔交易的成本。
然后加载数据。正如你所记得的,我们加载数据的方式与这里的实现方式相同。因此,我们要检查它是否未初始化,然后我们期待实际获得这些数据,期待 in_msg_body 以获得所有者的地址和带有元数据的Cell。但这只能通过 collection_address, 因此我们要检查它是否等于发件人地址。然后我们就可以初始化 NFT Item了。
  1. int op = in_msg_body~load_uint(32);
    * H/ u* D8 `5 p1 _( I2 b) Q7 M- Q
  2.   int query_id = in_msg_body~load_uint(64);
    * A+ _9 X2 p( n, b. W7 \

  3. - G, R6 N7 u4 e! f
  4.   if (op == op::transfer()) {
    , ?! k! h2 K2 q9 G8 B5 a# Y" }
  5.     transfer_ownership(my_balance, index, collection_address, owner_address, content, sender_address, query_id, in_msg_body, fwd_fee);
    2 y+ a' ]0 C6 {0 U1 m. L0 n
  6.     return ();
    # e5 {- [, }8 W& Y' z  m8 }# w
  7.   }0 R% @( T/ y  d! \2 L$ z' d5 z
  8.   if (op == op::get_static_data()) {: u' ~9 W2 |0 N2 w) ~) B" G+ V
  9.     send_msg(sender_address, 0, op::report_static_data(), query_id, begin_cell().store_uint(index, 256).store_slice(collection_address), 64);  ;; carry all the remaining value of the inbound message
    , y: Y9 ^% O: a& r
  10.     return ();* ^+ @# I  U& [& i; p$ C
  11.   }
复制代码
接下来是操作代码,可以是 op::transfer 或 op::get_static_data. 这是我们要处理的两个可能值。当我们说 get_static_data, 我们会立即向发送静态数据请求的用户发送一条信息。我们只是报告数据,所以我们会向他发送 index 和 collection_address. 这是 get_static_data 函数。正如你所看到的,这是我们的第一个 send_ msg 用例
! m8 U! L# b0 o2 c( X4 j
  1. () transfer_ownership(int my_balance, int index, slice collection_address, slice owner_address, cell content, slice sender_address, int query_id, slice in_msg_body, int fwd_fees) impure inline {
    ' B( G, D- T% {/ ]
  2.     throw_unless(401, equal_slices(sender_address, owner_address));3 F) [. J/ L& x+ F
  3. : u0 Z3 k! h/ s% X% ~
  4.     slice new_owner_address = in_msg_body~load_msg_addr();" v' x( ?1 R' U* g; z' _7 s% _# U2 b
  5.     force_chain(new_owner_address);
    . M- C1 @2 z+ _' B1 Q
  6.     slice response_destination = in_msg_body~load_msg_addr();: M  d; {9 ?+ x' H
  7.     in_msg_body~load_int(1); ;; this nft don't use custom_payload* R4 T  J9 Q& k
  8.     int forward_amount = in_msg_body~load_coins();
    & ?: o% s* }8 B+ f- {0 k
  9.     throw_unless(708, slice_bits(in_msg_body) >= 1);
    # U) P4 r# Y. H/ ]  s/ E  N
  10. & j# d4 r  d3 X( G  ^: N
  11.     int rest_amount = my_balance - min_tons_for_storage();3 F) E6 ]+ h1 T+ v( n
  12.     if (forward_amount) {
    " p4 E/ |5 t- k$ Q, p
  13.       rest_amount -= (forward_amount + fwd_fees);7 ?4 v$ q9 X' P  E
  14.     }
    . _6 U) m% ?% [( Z+ `
  15.     int need_response = response_destination.preload_uint(2) != 0; ;; if NOT addr_none: 00. \" U. o! ^( E& @7 K& ]
  16.     if (need_response) {
    3 M7 T9 [, _0 X- U
  17.       rest_amount -= fwd_fees;
    # |7 M8 x( \; j+ Y
  18.     }
    2 N/ _. L- ?( R! p$ u2 ?
  19. 2 r- {) h. W0 p* u# j
  20.     throw_unless(402, rest_amount >= 0); ;; base nft spends fixed amount of gas, will not check for response# l0 D1 ~. \! E! t" r/ ]

  21. ! i4 g" s$ _: F& a* }
  22.     if (forward_amount) {
    0 J, }- w; Q) u, F5 Y3 n; B1 H
  23.       send_msg(new_owner_address, forward_amount, op::ownership_assigned(), query_id, begin_cell().store_slice(owner_address).store_slice(in_msg_body), 1);  ;; paying fees, revert on errors: Y2 x# D) ?/ ^3 R' O) _
  24.     }" [+ {* K4 W2 b  _
  25.     if (need_response) {
    3 r3 J( M, }8 A) k2 h5 n0 H4 c
  26.       force_chain(response_destination);
    6 |! P# @5 e8 c% W* K9 [
  27.       send_msg(response_destination, rest_amount, op::excesses(), query_id, null(), 1); ;; paying fees, revert on errors
    * G) V: s% C2 R
  28.     }
    1 y6 J+ {- C7 E: ?; c7 v

  29. ( O) M; e" c3 s) B. w2 N
  30.     store_data(index, collection_address, new_owner_address, content);9 `6 n4 p4 a) W4 n5 h5 ~
  31. }
复制代码
这里有一个转移所有权的功能。首先,我们要检查物品的所有者地址是否就是发送信息的人,因为只有他才能转移所有权。然后,我们读出新所有者的地址。我们 force_chain 以确保他在同一条链上。然后我们检查是否有 response_destination, 因此,一旦我们转移了这个所有权,我们是否应该告诉某人向另一个人发送信息。我们检查 forward_amount 是多少,如果有人要求的话。然后,我们就可以计算出,在发出信息之后,我们就会有足够的钱 forward_amount 到目的地。然后,我们要弄清楚是否需要回应,如果存在 response_destination 。如果存在,其剩余金额必须大于 0。
下面我就向大家简单介绍一下它的工作原理。如果存在 forward_amount ,我们将发送一条信息,通知用户所有权已由该 forward_amount 分配。如果我们需要回应,我们将 force_chain 以确保响应目的地在同一链上。我们发送的回复通常包括超额费用部分和所有额外资金;它们只是被转发到 response_destination. 然后,我们只需使用 new_owner_address 保存数据。基本上,这是唯一会发生变化的地方、 transfer_ownership 是这里的核心函数之一。您拥有一件物品,有时您会将其所有权转让给其他人或出售,这取决于交易的类型。
' G2 y% W; S+ Z# r# h7 `& W
SBT item合约
NFT 可以在另一个层面运作。查看 Getgems (https://getgems.io/) 市场。它们有自己的合约,可以出售这类资产。我们现在不研究这些市场合约。不过,我还是要告诉你一个地方,在那里你可以学到很多关于 NFT 的知识 — GetGems github — 在这里,我还想为大家介绍一种 NFT。
GetGems repository 正在主办一些不同的 NFT 合约、藏品、销售和市场以及一些拍卖活动。这里是了解更多与 NFT 藏品、物品及其相关的合约的好地方。但我想向您展示一份确切的合约,我们可以对其进行深入研究。
sbt-item 是一种灵魂绑定代币(SBT)。灵魂绑定与 NFT 非常相似,但不同之处在于灵魂绑定代币不能擅自转让给其他人。我们仔细看看它的代码。
  1. global int storage::index;
    $ v0 g! S7 Y/ ~3 a; O+ k- o4 o
  2. global int init?;. X5 A( v8 D9 V; r) C
  3. global slice storage::collection_address;
    , B5 c  Z. _, L$ w8 g4 d
  4. global slice storage::owner_address;/ l9 N& U1 x7 S8 S3 h9 _& d
  5. global slice storage::authority_address;
      O0 U. e) b2 ]3 B# O/ d
  6. global cell storage::content;  r4 r7 }. {8 O# L# Z2 ?! a! O+ I3 B. l
  7. global int storage::revoked_at;' Q6 T# a; {$ }" O/ r6 f
  8. () load_data() impure {: d% J2 [; o% s. n/ L
  9.     slice ds = get_data().begin_parse();2 o: M3 {# L  Z* G
  10. 0 P3 g5 O  b( s9 [4 T2 v. u
  11.     storage::index              = ds~load_uint(64);
    3 F% r7 {  |  N3 ?5 S: _  c
  12.     storage::collection_address = ds~load_msg_addr();) J' {+ p* w7 H# U6 G
  13.     init?                       = false;) C/ W; J$ F8 z7 q
  14.   [, F  j& n# U& @, F( n$ ]- m! h
  15.     if (ds.slice_bits() > 0) {
    : s: z' x" d, A, f" x
  16.         init?                      = true;; b, G3 |: @8 H  P
  17.         storage::owner_address     = ds~load_msg_addr();5 J% m, f" s2 L7 |+ L* e$ X/ ~
  18.         storage::content           = ds~load_ref();
    8 \8 [- W$ Z9 C9 M& _/ E
  19.         storage::authority_address = ds~load_msg_addr();) z/ \$ k6 Q( a* N1 U! O
  20.         storage::revoked_at        = ds~load_uint(64);
    " J+ v1 X) }$ T, }4 o( t! u2 o. Q
  21.     }
    5 |' q/ ~& a0 Q5 k' f2 t
  22. }
复制代码
这里有一些变量: storage::index, storage::collection_address. 我们还定义了其他全局变量,这只是 GetGems 实现的一个例子。你不必用同样的方法。我想在这里重点谈谈其他部分:

4 g! |% x  }0 |! Q/ }) V
  1. if (op == op::request_owner()) {8 c+ m2 y* B+ \( u3 z
  2.     slice dest = in_msg_body~load_msg_addr();
    % k5 ]7 ?) E: O* y
  3.     cell body = in_msg_body~load_ref();5 }3 j2 q/ I; w
  4.     int with_content = in_msg_body~load_uint(1);. q. J" e" H8 m. b% C2 X

  5. / f. C# t1 j# W
  6.     var msg = begin_cell()2 M. z! G' C/ r7 p
  7.             .store_uint(storage::index, 256)
    * e6 _0 |( F' J
  8.             .store_slice(sender_address)
    5 l  c9 `; b  i% l  L  x# z! }
  9.             .store_slice(storage::owner_address)' r9 R! U# E- y1 N
  10.             .store_ref(body)- ]8 `. x5 e( h$ n
  11.             .store_uint(storage::revoked_at, 64)
    * m# Y2 _' v( `
  12.             .store_uint(with_content, 1);5 K" X8 M2 Q( y9 k, T
  13. ( V) B, v, h5 A- Z# e) P+ y# d
  14.     if (with_content != 0) {0 w. P+ l+ E/ Q5 m- y- ~/ F
  15.         msg = msg.store_ref(storage::content);: ^& K6 |' M$ }. I- q
  16.     }( K" ]  M+ ?9 l, k3 m, H* ~
  17. . t( |0 c- H1 @7 b6 w
  18.     ;; mode 64 = carry all the remaining value of the inbound message* X4 h9 K8 l5 z% {+ U0 N/ V- \
  19.     send_msg(flag::regular() | flag::bounce(), dest, 0, op::owner_info(), query_id, msg, 64);$ P% \5 s5 }% n+ @
  20.     return ();/ z: n/ m0 @' [$ f, r
  21. }
    . D# q# [) c) o& L0 O' {3 `" @$ A
  22. if (op == op::prove_ownership()) {
    1 x0 f( X7 m, X
  23.     throw_unless(401, equal_slices(storage::owner_address, sender_address));
    0 U% O6 l$ q6 d
  24. . m8 L3 i- u# t% k
  25.     slice dest = in_msg_body~load_msg_addr();2 `9 ]" s! c* q' A4 @
  26.     cell body = in_msg_body~load_ref();  h/ B9 Z) w1 f
  27.     int with_content = in_msg_body~load_uint(1);  S8 v7 s6 A+ Q5 I, C, v

  28. + O" Z" c3 @7 l# {* ]
  29.     var msg = begin_cell()
    . v7 O3 g3 L" e' Y. h2 c+ V! J
  30.             .store_uint(storage::index, 256)
    1 n5 [4 N" q* g% a' @' w8 |5 t* `7 F9 V
  31.             .store_slice(storage::owner_address)" y# @6 K2 Q, Y7 t- y9 j
  32.             .store_ref(body)
    0 d6 E: R( @: J4 Z' K! r/ Q
  33.             .store_uint(storage::revoked_at, 64)/ v% t* n$ B* X/ F4 g
  34.             .store_uint(with_content, 1);4 t3 d9 @/ @9 ^8 {
  35. 5 v7 N/ b* G# M- T8 b
  36.     if (with_content != 0) {
    9 M; R: ~+ l, Y; t+ M% v
  37.         msg = msg.store_ref(storage::content);0 A+ T$ v& v4 h# @
  38.     }
    4 B3 e) W2 e! W7 Y9 y4 J

  39.   V5 y$ ]% q( O9 z
  40.     ;; mode 64 = carry all the remaining value of the inbound message& }* c" g" v. u3 S
  41.     send_msg(flag::regular() | flag::bounce(), dest, 0, op::ownership_proof(), query_id, msg, 64);2 F! a2 z8 ^9 ~3 M  r0 t4 T
  42.     return ();2 _- z, a" P+ S+ G/ s% F9 ~
  43. }
    - ~; F9 |2 F5 [4 f% j4 l
  44. if (op == op::get_static_data()) {
    + r0 @/ b6 E8 @: s) \# i  \0 c* k
  45.     var msg = begin_cell().store_uint(storage::index, 256).store_slice(storage::collection_address);+ b5 s  i6 d+ ?% v

  46. ) h# w+ w* J) X$ d! V- t/ ~# L' C* f  H
  47.     ;; mode 64 = carry all the remaining value of the inbound message
    ' C+ P. P$ n: R" p; x
  48.     send_msg(flag::regular(), sender_address, 0, op::report_static_data(), query_id, msg, 64);
    . a! @6 `7 o$ S+ H- B
  49.     return ();
    8 y5 B" a* h/ L( n/ O" M5 h
  50. }
    & o  i# \# [& \
  51. if (op == op::destroy()) {
    ) v/ V6 [. Q# s0 E) H  }0 d
  52.     throw_unless(401, equal_slices(storage::owner_address, sender_address));
    $ X& u7 N' x/ ?  d0 _3 s# X$ p
  53. 0 k7 w  `- r! T! u
  54.     send_msg(flag::regular(), sender_address, 0, op::excesses(), query_id, null(), 128);
    7 {9 C9 u, U+ b+ {. \

  55.   W9 A$ {! @0 [; ]+ M" G. e
  56.     storage::owner_address = null_addr();
    0 s- ?9 g* z! p* p0 a
  57.     storage::authority_address = null_addr();
    4 z" h- ~- `7 ^$ }/ R' R
  58.     store_data();
    7 C' Z9 D( z1 V$ T( f# {& S
  59.     return ();' I/ ], m  W4 u7 U& @  T/ S2 j
  60. }
    : ~# `0 L$ R6 o( o5 `7 m/ ]+ n5 ^
  61. if (op == op::revoke()) {; S$ h9 m0 l" ~7 _* N) q' ]8 J) h
  62.     throw_unless(401, equal_slices(storage::authority_address, sender_address));
    " p; j& b+ ?- m+ m7 D7 b! f9 q
  63.     throw_unless(403, storage::revoked_at == 0);& H3 Y2 g  N! G
  64. % h1 d" f) N" `3 t# ?' _
  65.     storage::revoked_at = now();, a; C; t1 I# F& j! H
  66.     store_data();
    : g/ R% w; Y$ N: c. Z
  67.     return ();$ k8 [/ B. V$ s3 K' e5 Q5 k
  68. }
    5 Y+ h- \$ E6 ~9 w
  69. if (op == op::take_excess()) {; p. B! {$ a5 g2 v1 G5 Z* a
  70.     throw_unless(401, equal_slices(storage::owner_address, sender_address));5 p) E* f$ N7 ?& s0 ]! H

  71. 0 X$ q0 f8 t7 Q) ^3 Y  u3 y. a# {
  72.     ;; reserve amount for storage
    6 t! b+ p2 K( g8 x! a+ Q+ H
  73.     raw_reserve(min_tons_for_storage(), 0);5 C& t5 e- k, }5 v, S# N

  74. / g6 b# j, A0 v  v: z4 m% A
  75.     send_msg(flag::regular(), sender_address, 0, op::excesses(), query_id, null(), 128);9 l3 [( u/ g0 L6 k" V4 M* W5 C
  76.     return ();3 D5 ?2 p# R: {( A+ X3 l
  77. }
    , Y$ E5 ?5 Q9 {2 e$ N8 S7 X+ Z
  78. if (op == op::transfer()) {  v* ]* d8 ]8 T: D0 c, b) e
  79.     throw(413);' i0 S  c2 U# f
  80. }
复制代码
如你所见,这里有许多注释,但我们不会逐行深入研究。你可以看到,这个通证将被用于多种不同的用途,但与普通的 NFT 不同。这里是合约,你可以申请这个合约的所有者。一些操作代码可以请求所有权证明。您可以获取静态数据。你还可以销毁或撤销这个标记。你可以拿走多余的东西,比如,存放在这里的一些钱。如果你是这个合约的所有者,你就可以申请。但你显然不能转让它,就像你在这里看到的一样。这是一种完全不同类型的代币,如果你对 NFT 感兴趣,就应该在 GetGems 存储库里多花点时间。
我想向你展示的是,NFT collections 的所有代码可以保持不变,但collections合约部署的 item 可以不同。例如,你可以用 SBT item 替换 NFT item。这样您就会有一个 SBT item collection,然后您只需更改  code 来初始化你的 collection:
  1. (slice, int, cell, cell, cell) load_data() inline {, P( c! c, U- o0 ~  B# T
  2.   var ds = get_data().begin_parse();( V/ U/ _. @  ]: d: ~
  3.   return
    7 o& B8 \( t  Q4 ]! r) j
  4.     (ds~load_msg_addr(), ;; owner_address# Y- _0 d2 k. @) W- j
  5.      ds~load_uint(64), ;; next_item_index
    2 A% g4 E& t5 d/ j
  6.      ds~load_ref(), ;; content0 Y* E9 Z* R7 f3 b1 [3 y
  7.      ds~load_ref(), ;; nft_item_code5 h+ d! s' w6 K  N
  8.      ds~load_ref()  ;; royalty_params
    " C7 B8 E- `5 o
  9.      );; L6 V3 n0 K: L0 W& N9 Y9 n. J
  10. }
复制代码
这样,您就可以实际部署 SBT item collection。此外,您还可以创建一个独立的没有collection SBT item。我们不会掌握这些item的编程,我只是想向大家展示它在更大范围内的工作原理。
- b8 M5 p! c% S; X) b/ W) c, S
结论
* }- {9 Y+ P# H. D8 ], ]1 M0 l
从第 3 章中非常简单的逻辑开始,我们已经了解了 NFT 或 Jettons 等复杂合约中的许多内容。这就是一个很好的例子,你可以一步一步地学习某些语法和概念,然后就可以学习更复杂的逻辑。希望这几节课能帮助你理解 Jettons 和 NFTs 的真正含义,以及如何处理它们的代码库--弄清楚它们是如何工作的,并在使用 TON 构建时学习更多可用的语法和架构。
非常感谢您的关注。对你们来说,这可能是最难的一章,虽然我们没有编写任何代码;我们只是在阅读一些你们从未见过的东西,而且非常复杂。你们能坚持到最后一课,我真的很骄傲,我期待着在接下来的章节中看到你们。希望你们喜欢目前的课程。
( u! \& H' q1 [8 M- ]
8 @% m+ m" Z& p1 }4 y3 ^

% N* A6 }5 J; R3 r& O8 ?6 W( g- L( r- R0 ^7 y. A
- O; Y$ ^; K7 S" f+ U8 K4 q) L( f7 y

: b; \& e$ ]% f4 Y1 u8 O* y5 F& v. u4 c, i& a9 _

) z% k' P9 y" I, u9 V1 d
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则