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

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

回答

收藏

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

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

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

3 s- }' P, d2 h- E- i' |' g) X2 LNFT Item合约
让我们打开并查看 contract.
  1. int min_tons_for_storage() asm "50000000 PUSHINT"; ;; 0.05 TON6 B) {' {& H+ ]7 g/ v! {. O
复制代码
就像在前面的课程中一样,我们看到有一些常量显示了我们应该在合约上存储的最低代币数量,以便能够支付租金。

+ {: v2 J# C) A5 v+ w, \
  1. (int, int, slice, slice, cell) load_data() {- _3 H' W. a9 `
  2.     slice ds = get_data().begin_parse();
    9 A  E: X  @" x/ K" [
  3.     var (index, collection_address) = (ds~load_uint(64), ds~load_msg_addr());
    7 r2 a- N- O- F+ L
  4.     if (ds.slice_bits() > 0) {
    3 [4 \$ ^3 n+ O; T+ i; F/ F& ~) d4 M
  5.       return (-1, index, collection_address, ds~load_msg_addr(), ds~load_ref());
    ( s5 N6 U' l/ O8 x( j! J$ ?
  6.     } else {  * a' C% w! J, B! _! }) b
  7.       return (0, index, collection_address, null(), null()); ;; nft not initialized yet
    0 g; t% T+ {. r% h% w6 J
  8.     }
    " S, ]. ?$ B  u  h6 G7 E" R
  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 {% i2 X; |1 @: ^" K/ H% T1 ~! N
  2.     set_data(
    0 M+ |# |, {% L1 W/ H. C! Y
  3.         begin_cell()) C2 Q) ^/ B7 Z) m/ @; m
  4.             .store_uint(index, 64)
    ( H; c8 K5 r& K+ t6 C" b
  5.             .store_slice(collection_address)
    3 c; S8 \8 u% G7 Y; M3 z
  6.             .store_slice(owner_address)
    ' C6 T! y' V* @
  7.             .store_ref(content)3 H4 t; b) d0 s6 U* |$ y
  8.             .end_cell()
    6 u& h' z+ w: P9 u) G4 L
  9.     );. m4 Z* h: k3 O. }) K5 K
  10. }
复制代码
我们还看到 store_data 就像我们调查的其他合约一样,这只是正常情况。
+ A- |# s3 }' z- A
  1. () send_msg(slice to_address, int amount, int op, int query_id, builder payload, int send_mode) impure inline {5 k6 E$ K5 f; g
  2.   var msg = begin_cell()
    + Q7 r* m0 U/ [  L) ?: E
  3.     .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 010000! A% \; V$ O% ?- N* r2 T7 w- ?' U  N
  4.     .store_slice(to_address)
    $ f+ T; t& W' |7 o3 N, A
  5.     .store_coins(amount)
    ; I1 y9 p/ E1 l4 Q3 V
  6.     .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1), t# K7 n% q# _) |! I& K# z
  7.     .store_uint(op, 32)
    8 X0 d! K, M9 W# L) e
  8.     .store_uint(query_id, 64);" x# P" _3 |" U- m
  9. 6 n' f5 L+ b% @& _5 C
  10.   if (~ builder_null?(payload)) {
    8 _! L6 m7 G; v' T% \+ K: o
  11.     msg = msg.store_builder(payload);( ?' `7 {4 o1 l, Z- W
  12.   }
复制代码
在这里,我们封装了发送消息的逻辑,因此我们可以在合约的其他逻辑中使用这个函数。因此,只要我们想发送信息,就可以使用这个函数。它接受目标地址、我们应附加的金额、操作代码、查询 ID、可能的有效载荷(这次是以生成器格式)以及消息的发送模式。这就是我们发送信息的逻辑。
我们的另一个函数是 transfer_ownership. 让我们稍后再研究,一旦我们在 recv_internal 逻辑,因为它是这里最重要的功能。
  1. () recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
      F# c; L  ?& o! q
  2.     if (in_msg_body.slice_empty?()) { ;; ignore empty messages
    ! `. S; ~0 m: m
  3.         return ();
    3 C& i( u% O& J' u2 M( q; r# a
  4.     }/ L, C. H' r9 [3 ^+ v

  5. / T' p) p  x1 U5 r. n
  6.     slice cs = in_msg_full.begin_parse();$ Y$ m) q* T! M: G; K
  7.     int flags = cs~load_uint(4);
    ! V6 e& @. ~! T2 B. R/ l
  8. 6 w' N; W! o  y$ e0 l+ T
  9.     if (flags & 1) { ;; ignore all bounced messages
    7 Y+ r. i: v0 d, ?
  10.         return ();
    . K, M; C8 F. J. L0 ~3 T
  11.     }
    4 P$ E4 F5 W! s0 P: A
  12.     slice sender_address = cs~load_msg_addr();
    # ?1 Y% m3 Z; D, N5 F, v; K

  13. + G7 w+ ~& T9 ?: u# U# N' z
  14.     cs~load_msg_addr(); ;; skip dst
    & W, ~! k+ R- f4 k/ _$ Y2 X
  15.     cs~load_coins(); ;; skip value* }8 u6 b+ d' G+ I! i# u0 X
  16.     cs~skip_bits(1); ;; skip extracurrency collection
    ! D! ?8 a+ e" A
  17.     cs~load_coins(); ;; skip ihr_fee+ J: v/ h$ m" U4 A. j9 x* \* z
  18.     int fwd_fee = muldiv(cs~load_coins(), 3, 2); ;; we use message fwd_fee for estimation of forward_payload costs) w6 H$ {/ j+ H4 E

  19. ' O  K2 |, L7 X0 @+ P
  20.     (int init?, int index, slice collection_address, slice owner_address, cell content) = load_data();8 B7 T& g/ m5 S$ Y% O
  21.     if (~ init?) {
    , n4 n/ g$ I8 y# C2 B
  22.       throw_unless(405, equal_slices(collection_address, sender_address));
    / H' P! I( ~! J9 I4 _9 o
  23.       store_data(index, collection_address, in_msg_body~load_msg_addr(), in_msg_body~load_ref());
    * O9 p$ R7 L, ?% c) W
  24.       return ();0 L* j7 D0 N  ~8 W% ^# m
  25.     }
复制代码
我们来看看它是怎么做的。我们再次忽略空的 in_msg_body 消息,读取标志,忽略退回的信息,并检查发件人是谁。现在我们跳过一些内容,比如信息的目标地址之类的,因为你已经知道这个Cell的内容是什么了。接下来是 fwd_fee 再次根据上一笔交易估算发送下一笔交易的成本。
然后加载数据。正如你所记得的,我们加载数据的方式与这里的实现方式相同。因此,我们要检查它是否未初始化,然后我们期待实际获得这些数据,期待 in_msg_body 以获得所有者的地址和带有元数据的Cell。但这只能通过 collection_address, 因此我们要检查它是否等于发件人地址。然后我们就可以初始化 NFT Item了。
  1. int op = in_msg_body~load_uint(32);  Q7 g' z4 ?1 c2 l5 [4 x
  2.   int query_id = in_msg_body~load_uint(64);8 W( P4 k7 [2 _6 a5 |
  3. ! ^, r2 I, ^* |
  4.   if (op == op::transfer()) {
    1 a8 D4 c# p5 c5 V, H; P0 B
  5.     transfer_ownership(my_balance, index, collection_address, owner_address, content, sender_address, query_id, in_msg_body, fwd_fee);
    - w$ Y8 w/ ?) ^' E
  6.     return ();8 ?4 k$ e) u/ O2 s6 K' e
  7.   }
    * ^$ o9 d$ S$ [) @0 F3 y; E% s: P" W
  8.   if (op == op::get_static_data()) {( p( J0 j1 b* j8 B0 E0 }  w
  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
    7 B; |+ d4 X, Z* E- j% i1 t0 u
  10.     return ();
    : P! S" A8 S% c
  11.   }
复制代码
接下来是操作代码,可以是 op::transfer 或 op::get_static_data. 这是我们要处理的两个可能值。当我们说 get_static_data, 我们会立即向发送静态数据请求的用户发送一条信息。我们只是报告数据,所以我们会向他发送 index 和 collection_address. 这是 get_static_data 函数。正如你所看到的,这是我们的第一个 send_ msg 用例
, |: v- [/ {/ J$ f7 f% G
  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 {
      a3 ^% Q, q7 R$ l2 N
  2.     throw_unless(401, equal_slices(sender_address, owner_address));6 x/ U% O) N! @$ S! w# `

  3. * {) Y2 A2 h& l6 m& ^; {
  4.     slice new_owner_address = in_msg_body~load_msg_addr();
    6 \$ E- Y6 m8 b
  5.     force_chain(new_owner_address);
    0 ^4 j! D4 A6 Z/ d3 y
  6.     slice response_destination = in_msg_body~load_msg_addr();
    , R7 E/ x/ K% c  W1 ?
  7.     in_msg_body~load_int(1); ;; this nft don't use custom_payload+ Y, P9 A) r3 Q9 e/ i5 r2 ]9 R
  8.     int forward_amount = in_msg_body~load_coins();
    - R. f# ~5 ~' L0 v$ G5 [$ S' Z
  9.     throw_unless(708, slice_bits(in_msg_body) >= 1);
    ! I" x0 M9 @0 K" \

  10. 0 G& [1 a! l+ M
  11.     int rest_amount = my_balance - min_tons_for_storage();( P3 a" E$ _4 P% j9 [7 m
  12.     if (forward_amount) {
    3 O4 A- D) ]! r  Z. p
  13.       rest_amount -= (forward_amount + fwd_fees);
    % ?" a$ \2 {" w  K# O# E
  14.     }
    " A+ ^" q' M. A9 l
  15.     int need_response = response_destination.preload_uint(2) != 0; ;; if NOT addr_none: 00
    2 ?2 s% K& v# S* m
  16.     if (need_response) {0 _* Y, \2 ?" L7 {2 w! F
  17.       rest_amount -= fwd_fees;4 S* n: {. O9 |) H* A: F+ r
  18.     }
    5 k* ], }- I# A  ~2 b8 _# s- c
  19. " P/ _/ n9 \4 C: H3 d
  20.     throw_unless(402, rest_amount >= 0); ;; base nft spends fixed amount of gas, will not check for response4 c0 R9 q. b1 |1 ^; h5 z* g; O
  21. 6 H4 Z' v9 k  \5 d5 y
  22.     if (forward_amount) {
    4 r( r6 F3 Y. w; c8 K3 _0 {0 \
  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+ Q% y* E$ h: J% q# \
  24.     }0 R. Z, l1 v. [  q, O
  25.     if (need_response) {
    . d4 [2 ]* g3 U9 w; q: @% w
  26.       force_chain(response_destination);
    4 _+ C0 J* g8 ?
  27.       send_msg(response_destination, rest_amount, op::excesses(), query_id, null(), 1); ;; paying fees, revert on errors
    9 ]6 @# g3 B" B) f
  28.     }
    ) b: B6 E- `! G( q
  29. ( g( }; y; i" j5 e' I* f2 m
  30.     store_data(index, collection_address, new_owner_address, content);8 x( t; v. Z* Y4 d! K4 a* R! J
  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 是这里的核心函数之一。您拥有一件物品,有时您会将其所有权转让给其他人或出售,这取决于交易的类型。
& Z6 [8 h0 B  t, ?$ w6 w4 n
SBT item合约
NFT 可以在另一个层面运作。查看 Getgems (https://getgems.io/) 市场。它们有自己的合约,可以出售这类资产。我们现在不研究这些市场合约。不过,我还是要告诉你一个地方,在那里你可以学到很多关于 NFT 的知识 — GetGems github — 在这里,我还想为大家介绍一种 NFT。
GetGems repository 正在主办一些不同的 NFT 合约、藏品、销售和市场以及一些拍卖活动。这里是了解更多与 NFT 藏品、物品及其相关的合约的好地方。但我想向您展示一份确切的合约,我们可以对其进行深入研究。
sbt-item 是一种灵魂绑定代币(SBT)。灵魂绑定与 NFT 非常相似,但不同之处在于灵魂绑定代币不能擅自转让给其他人。我们仔细看看它的代码。
  1. global int storage::index;
    $ P  q" t0 e- h
  2. global int init?;
    ! X1 X: ?, U% O7 k- p
  3. global slice storage::collection_address;
    - S( e1 f' _' }' E7 v9 j8 z
  4. global slice storage::owner_address;
    ) S2 l. m; q- y2 U' y
  5. global slice storage::authority_address;: I# ^/ I- j: e! W1 I% u: P, I
  6. global cell storage::content;
    * j% I0 x/ ~) i( L, M8 p+ L) ?
  7. global int storage::revoked_at;  d7 W0 ~' I( ~! {# @
  8. () load_data() impure {
    1 ]+ S1 \8 s) C  N6 D6 K
  9.     slice ds = get_data().begin_parse();
    9 q" I" o/ V+ r4 z; K4 B- @7 T! x
  10. 9 ]7 G( E4 ^: d% Z, z
  11.     storage::index              = ds~load_uint(64);
    . T* e8 U) O" k2 z2 n. Z% F
  12.     storage::collection_address = ds~load_msg_addr();
    # _# F& c# w* [
  13.     init?                       = false;
    6 n8 n: W/ `/ _- L/ V! A2 M5 ]
  14. . r# G) r& U* N, C& D6 D4 ~9 T- H6 n
  15.     if (ds.slice_bits() > 0) {5 f; v6 p/ t4 s, c% B+ ^& G
  16.         init?                      = true;
    ( O4 S& r0 A- V- X; l1 w
  17.         storage::owner_address     = ds~load_msg_addr();) B1 `4 A+ ^# i, o6 o
  18.         storage::content           = ds~load_ref();
    & {: Y- F" s4 b+ e; M7 p9 z
  19.         storage::authority_address = ds~load_msg_addr();
    / g( o  L( |) ~, t: {1 j$ ?
  20.         storage::revoked_at        = ds~load_uint(64);
    - Z* I( o9 p6 I4 T6 d5 K$ f9 z. x
  21.     }7 n0 k4 `0 h2 f
  22. }
复制代码
这里有一些变量: storage::index, storage::collection_address. 我们还定义了其他全局变量,这只是 GetGems 实现的一个例子。你不必用同样的方法。我想在这里重点谈谈其他部分:

4 C3 Y/ S0 z# d; q: G% K
  1. if (op == op::request_owner()) {
    6 y6 S! r0 E% F" f
  2.     slice dest = in_msg_body~load_msg_addr();
    - b/ t  m3 o; J& ~- E# h) X1 E
  3.     cell body = in_msg_body~load_ref();
    ) D1 B. `5 Y# x, e! q
  4.     int with_content = in_msg_body~load_uint(1);" H; N, p1 R2 E

  5. ! n) w7 T8 n0 S7 q% B& x
  6.     var msg = begin_cell()
    2 m( v5 n  L# |8 C5 q
  7.             .store_uint(storage::index, 256)* B% E: U. {, l, i& B# X
  8.             .store_slice(sender_address)7 v$ T% s1 `" R( `) P: p
  9.             .store_slice(storage::owner_address)
    . `5 Z" n: R% Q! D# u. e! }
  10.             .store_ref(body)+ R# a& j, }, e) \* q
  11.             .store_uint(storage::revoked_at, 64)% V' G8 [6 ?/ J
  12.             .store_uint(with_content, 1);* E( n3 T9 K* ?/ |: }
  13. % d3 s8 o3 S( N* z" n) N0 Q3 N
  14.     if (with_content != 0) {7 b9 l% z' ]# d
  15.         msg = msg.store_ref(storage::content);
    5 V6 W) P5 O7 @+ H: J
  16.     }
    5 Y% x: n" A) P1 D

  17. ! F8 h: D( ?! F& \+ M* }
  18.     ;; mode 64 = carry all the remaining value of the inbound message
    " ?8 e/ D! {0 H9 j3 `0 @1 t
  19.     send_msg(flag::regular() | flag::bounce(), dest, 0, op::owner_info(), query_id, msg, 64);% `" M6 V& w  s! [2 S
  20.     return ();. y4 v8 k9 ]+ H  U/ t7 \. y1 z; \
  21. }
    6 Z! K3 O( I, p
  22. if (op == op::prove_ownership()) {
    6 r' ^6 ^* {: T* k  x* g
  23.     throw_unless(401, equal_slices(storage::owner_address, sender_address));9 f' r8 l3 ~# c
  24. + d* Z+ t& N' O9 ?: f4 K
  25.     slice dest = in_msg_body~load_msg_addr();" u4 c  X8 v; U5 y0 U  I
  26.     cell body = in_msg_body~load_ref();% @2 a; y% [# E3 N* v$ w
  27.     int with_content = in_msg_body~load_uint(1);
    ) v2 p9 V; K# g

  28.   W, e- r8 ?3 L! y& [" n- T
  29.     var msg = begin_cell()$ j: N/ v% n4 u% \6 v* k
  30.             .store_uint(storage::index, 256)
    ) f+ J( y  n  K& p+ S3 e$ r
  31.             .store_slice(storage::owner_address)  Q! C& f: X) m* V$ h
  32.             .store_ref(body)
    : j! i' I9 ]1 k. E
  33.             .store_uint(storage::revoked_at, 64)5 u: B( q- C, j2 k8 r5 b" U
  34.             .store_uint(with_content, 1);
    3 G" e6 P" @; W6 p. H. T$ g
  35. ! n- ^' A, \: U# S0 V! E0 r
  36.     if (with_content != 0) {2 `1 e3 ^/ u, E6 G' q7 }* j
  37.         msg = msg.store_ref(storage::content);
    + O1 \! x# W) }$ U7 }. d" {; m7 P
  38.     }, V% N' K1 i( l
  39. 6 Y* S$ A, H- ?  \, m3 q
  40.     ;; mode 64 = carry all the remaining value of the inbound message
    5 l1 H0 R$ M, @9 _% e
  41.     send_msg(flag::regular() | flag::bounce(), dest, 0, op::ownership_proof(), query_id, msg, 64);# y3 m: E0 F: s- N/ }5 i, c
  42.     return ();. H# A" |8 ]+ N( h$ r# g
  43. }3 c- @- r/ X4 m; A8 v( @
  44. if (op == op::get_static_data()) {, `' o7 \6 L4 j1 T7 X6 A2 l* e( l
  45.     var msg = begin_cell().store_uint(storage::index, 256).store_slice(storage::collection_address);; q4 x9 F' u& l
  46. : x$ E" H; N; n0 ^
  47.     ;; mode 64 = carry all the remaining value of the inbound message
    ' J7 `% w( K& ^5 E
  48.     send_msg(flag::regular(), sender_address, 0, op::report_static_data(), query_id, msg, 64);
    1 Q: X! S+ s; m. i& |
  49.     return ();) d" ]4 S6 H2 L# s
  50. }4 Z/ P6 R+ h2 d0 x- j- g2 c; h
  51. if (op == op::destroy()) {6 P0 m) r% }! b0 L- `
  52.     throw_unless(401, equal_slices(storage::owner_address, sender_address));
    ' n2 m% z9 H2 r  E- B( [

  53.   J0 d% m5 L, e! Y" r% D# }
  54.     send_msg(flag::regular(), sender_address, 0, op::excesses(), query_id, null(), 128);
    . {8 F' Z" ?1 G- D

  55. 6 X" y% g+ I  F  }5 v' \! V/ ~7 A- H
  56.     storage::owner_address = null_addr();0 y1 X! d0 q0 {) y
  57.     storage::authority_address = null_addr();
    6 g4 Q. s7 {$ c9 e
  58.     store_data();
    * ^8 M; W* j' T" F. c2 X! v' r
  59.     return ();3 S2 c0 ]0 J# S6 |/ E0 c8 K
  60. }- c$ M/ q2 h" C* R" g* C
  61. if (op == op::revoke()) {
    & p# L5 K1 s: q
  62.     throw_unless(401, equal_slices(storage::authority_address, sender_address));
    # [' s7 Q% |* F3 s9 l$ Q9 N
  63.     throw_unless(403, storage::revoked_at == 0);1 u/ }- r2 z* w6 x0 N3 I3 L

  64. 8 B. a7 r; H! z! N, \6 A- L
  65.     storage::revoked_at = now();- v) p$ |! h) N: ]+ d
  66.     store_data();% T( h2 r" S& F
  67.     return ();9 ]" h9 ~& [% J3 l" o" ?
  68. }. ^# S3 s. X2 `# `
  69. if (op == op::take_excess()) {
    ! G' o8 {1 T# y3 e2 W# B( t) y: M0 I2 S
  70.     throw_unless(401, equal_slices(storage::owner_address, sender_address));
    % G% f# ^2 Q# ], }9 p4 Z" j

  71. : x6 K. V3 r9 p# T# \+ u
  72.     ;; reserve amount for storage
    5 J1 r. l9 r% B
  73.     raw_reserve(min_tons_for_storage(), 0);
    $ o" W% _; K1 [
  74. ; z2 @4 @( Y+ C1 C
  75.     send_msg(flag::regular(), sender_address, 0, op::excesses(), query_id, null(), 128);
      ^( I( u% j6 |( ]5 e
  76.     return ();
    1 y  ]4 b+ L2 i6 ]! Q: F2 e1 A
  77. }+ K; V" E3 a0 Y  G7 ~- B8 Y: A
  78. if (op == op::transfer()) {: _$ r3 @9 ?; p2 o; }+ H& _2 X
  79.     throw(413);4 C  M+ D  W* E: ]
  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 {  F, H0 G; v6 Q1 R5 R, t9 ?+ c
  2.   var ds = get_data().begin_parse();% i' s3 q3 ]6 I$ I
  3.   return
    3 c9 {2 F: B( ^
  4.     (ds~load_msg_addr(), ;; owner_address
    ) E; L6 e% ]2 a
  5.      ds~load_uint(64), ;; next_item_index
    # k6 u# P/ g9 J' i' L5 p/ V0 K5 O
  6.      ds~load_ref(), ;; content
    ! v* C( X% ?' {* Y
  7.      ds~load_ref(), ;; nft_item_code
    4 }6 T. N& B! |# [8 ^
  8.      ds~load_ref()  ;; royalty_params
    ' O5 U( C! D; q6 ]' ?; A
  9.      );
    5 \3 U8 v" b6 [2 |0 e  R7 \" Y, V
  10. }
复制代码
这样,您就可以实际部署 SBT item collection。此外,您还可以创建一个独立的没有collection SBT item。我们不会掌握这些item的编程,我只是想向大家展示它在更大范围内的工作原理。

) f. \8 u$ z+ @1 t/ ^7 ~
结论9 c8 d3 L. r( u; J( I7 \  x. I
从第 3 章中非常简单的逻辑开始,我们已经了解了 NFT 或 Jettons 等复杂合约中的许多内容。这就是一个很好的例子,你可以一步一步地学习某些语法和概念,然后就可以学习更复杂的逻辑。希望这几节课能帮助你理解 Jettons 和 NFTs 的真正含义,以及如何处理它们的代码库--弄清楚它们是如何工作的,并在使用 TON 构建时学习更多可用的语法和架构。
非常感谢您的关注。对你们来说,这可能是最难的一章,虽然我们没有编写任何代码;我们只是在阅读一些你们从未见过的东西,而且非常复杂。你们能坚持到最后一课,我真的很骄傲,我期待着在接下来的章节中看到你们。希望你们喜欢目前的课程。
. d! t& G, w7 A+ e' M
( K7 h  {0 d: P0 R5 Y
* H* _$ c( k; i4 r
/ E9 h. E# r) e! N* j/ T$ f

( U% O# C2 c" G/ Y  q4 P
) b, A# Y! u8 _3 X! G* W! U  Y/ i3 B8 m* `' u4 z! L

' Q* u) W  e8 ^/ @8 u
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则