本帖最后由 riyad 于 2025-3-15 05:47 编辑 + M% R, [0 J4 p0 ~: L
S6 O) [1 v+ D这里还有两种类型的合约:collections合约和 NFT item智能合约。collections合约就是部署 NFT item智能合约。这里有一个很好的例子,如果你想发布一个包含 10,000 个item的集合,你将部署 10,001 个智能合约。您将自行部署。 在item智能合约中,所有者将不断变化。此外,collections的所有者也可以更改。你需要理解这个逻辑。我们要审查的第一个合约是 NFT collections,然后审查 NFT item智能合约。再次强调,这不仅仅是一个合约,这是一个智能合约系统。 NFT collection 智能合约和其他合约一样,都是从读取存储的封装逻辑开始的: - (slice, int, cell, cell, cell) load_data() inline {, z" h6 V, A. S( J
- var ds = get_data().begin_parse();
: K9 P7 A& i' j6 ~ - return
" W2 {) A! @1 k$ N - (ds~load_msg_addr(), ;; owner_address
4 c v! i* n& d0 k) i - ds~load_uint(64), ;; next_item_index
" F# _9 p' x3 |! p3 c% |7 k8 e5 Z; E% N - ds~load_ref(), ;; content t O3 K/ [0 J* u/ @# P& \
- ds~load_ref(), ;; nft_item_code
2 f; [7 k$ B1 A# y% O# Y. ] - ds~load_ref() ;; royalty_params: N) m* P! N" K. t; u3 |
- ); r2 q# X) A' O1 T' S
- }
复制代码 在这里,我们有 owner_address, next_item_index, 我们有 content. 同样,这 content 是根据元数据标准制作的cell,在同一标准中也有提供 repository 我们已经复习过了。那么我们有 nft_item_code 的cell royalty_params. 我们很快就会了解到更多。- () save_data(slice owner_address, int next_item_index, cell content, cell nft_item_code, cell royalty_params) impure inline {! y; X4 f1 @) E8 x
- set_data(begin_cell()8 f1 y4 C$ o6 G7 @ `
- .store_slice(owner_address): Q R" m* l& u' ?9 U
- .store_uint(next_item_index, 64)# m4 p. S% P& T6 m( K7 f
- .store_ref(content) t5 B: W3 m/ ]4 Z, e' r
- .store_ref(nft_item_code)
6 {$ g! X9 l0 g1 a$ a& [* x) F. b - .store_ref(royalty_params)
. Z) p% H. s' L! I - .end_cell());: A* j2 }. j2 D+ K0 M S$ w
- }$ g( B% p6 |; b7 N
* @ i F% v: c7 n9 V- cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {
, Z- T; X. Z6 R; e; G1 i& ] - cell data = begin_cell().store_uint(item_index, 64).store_slice(my_address()).end_cell();
; \6 o+ H) O* V: q6 x- ] - return begin_cell().store_uint(0, 2).store_dict(nft_item_code).store_dict(data).store_uint(0, 1).end_cell();! |( @ z! a9 a1 C" w* E3 T
- }! Z2 M' J! W6 V8 i+ y. {7 V
. s+ m! Z0 t; R# Q7 t$ H* k- slice calculate_nft_item_address(int wc, cell state_init) {
7 E, M# k, @: b6 k/ ?; f - return begin_cell().store_uint(4, 3)
$ g( F) Z- M% G' x* f3 c - .store_int(wc, 8), E$ r% H3 B6 D* |! k* t$ {
- .store_uint(cell_hash(state_init), 256)* |% i! n8 `" Q7 Z
- .end_cell()
5 H: l" t3 X' @9 { - .begin_parse();# |7 Y1 W6 k1 H8 ?
- }
复制代码就像 load_data, 我们还有 save_data 来重写本地存储的cell。在上一个使用 Jettons 的示例中,我们实际上看到了这些 calculate_nft_item_state_init 和 calculate_nft_item_address 函数封装在不同的文件中。但在这里,我们将它们放在了这个标准实现中的同一个文件中。这是因为我们在 NFT item合约中并没有真正使用这些函数,所以这就是我们不必将其保存在单独文件中的原因。大家还记得,在 Jettons 中,我们在矿工合约和钱包合约中都使用了这些函数。但在这里,我们把它们都放在一个文件中。 您可能不需要对 calculate_nft_item_state_init 进行更多的解释,因为你在Jettons的课程中学习了一个非常复杂的函数。这与 calculate_nft_item_address 相似,因为你们已经经历过这些。我们还传递我们熟悉的 load_data 和 save_data . 接下来我们看到 deploy_nft_item 和 send_royalty_params 函数: - () deploy_nft_item(int item_index, cell nft_item_code, int amount, cell nft_content) impure {
$ f' a' b R* P/ n$ p% n( { - cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);& p+ k1 P% c6 X0 g
- slice nft_address = calculate_nft_item_address(workchain(), state_init);
1 ^$ T7 F. P: Y' W+ c4 \ - var msg = begin_cell(); l s8 O" Y3 H. ]4 L
- .store_uint(0x18, 6)/ K9 `: ]; ]. O6 b) L
- .store_slice(nft_address)
( O& o: n A5 U - .store_coins(amount)( F: V$ ^3 ?4 b; T
- .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
) ?8 ^$ q7 q, |) F% z" X - .store_ref(state_init)
* ^' t F6 f |. w6 v1 Y7 h# p - .store_ref(nft_content);
: v ]5 K3 g# r; J8 T# i! U# k - send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors3 ` g3 B" n1 C/ c0 Y/ f
- }
; p& @* O X) w - & P+ M4 ]% b. j: {" I5 K. S
- () send_royalty_params(slice to_address, int query_id, slice data) impure inline {
" h, J& }, f2 C# m2 y8 Q% V - var msg = begin_cell()
, ^; g) X5 K* K; x. i - .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
0 O# j' `0 ~+ b# O - .store_slice(to_address)% _1 ?# K" f! ~( V1 g
- .store_coins(0). D. j5 K3 {1 M) V
- .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
& i) I e# n& z9 `2 z/ E - .store_uint(op::report_royalty_params(), 32)% Q0 o6 d+ [* x W$ s& m+ P' i
- .store_uint(query_id, 64)
" d/ R" I- i; ]8 z8 H; p - .store_slice(data);
0 x1 B& r5 E9 g; q - send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message% u4 f1 P1 M2 F" k
- }
复制代码这一切都要按照标准的要求进行。由于您必须遵守标准,因此您的合约将被视为 NFT 合约。
7 t# i1 K8 [& X0 ~/ d. s6 q, Q- () recv_internal(cell in_msg_full, slice in_msg_body) impure {
0 \$ @ S3 i! u' Z& i2 Z# H - if (in_msg_body.slice_empty?()) { ;; ignore empty messages4 ~5 E: P' c, h0 u2 u9 L
- return ();
! U# Z- q6 e8 O8 r" J* O - }
* w# w1 U# [5 ^ ?* h! r9 W - slice cs = in_msg_full.begin_parse();
* S4 R( Z" ?) m$ g8 R - int flags = cs~load_uint(4);
. t6 t( @- }/ G0 V
5 Y$ a% y, J) _% n* j4 c' { Y# c- 9 V4 q1 U. y4 }8 Y, N' m# f
- if (flags & 1) { ;; ignore all bounced messages
; u( ^5 ^0 T6 t0 ?( S - return ();+ B! b$ O0 x) {% y4 L, }/ t
- }
6 q$ j' ]& I w - slice sender_address = cs~load_msg_addr();
$ H% e* K0 V+ ^' x& T4 C
0 u2 M9 G7 U% U) y- int op = in_msg_body~load_uint(32);
/ X3 S* z; u( ^* S/ N - int query_id = in_msg_body~load_uint(64);
) E ~6 w$ k3 X! |" b- p - ( C1 M5 z. G1 @6 F. |
- var (owner_address, next_item_index, content, nft_item_code, royalty_params) = load_data();
复制代码 我们接收到一条内部信息,我们发现我们不想处理空的正文信息,因此我们要检查它是否为空。然后我们检查标记,忽略跳转信息,得到 sender_address, 阅读 in_msg_body, 运行代码、 query_id, 然后是本地存储中的所有内容。: ]7 p* @) r. Q6 t! l
- if (op == op::get_royalty_params()) {
, v7 F2 Z+ D: J" S* _4 ? - send_royalty_params(sender_address, query_id, royalty_params.begin_parse());
: q+ N( Y% T4 s4 d; x - return ();
/ ], q( Z& I; _) N- s' u - }
/ j! m" Q/ ?* l& o* N2 [ - # A1 F9 s9 ]/ o( E* f5 p/ Q
- throw_unless(401, equal_slices(sender_address, owner_address));
复制代码 如果操作码为 get_royalty_params, 那我们将发送版税参数。如你所见,我们从本地存储读取版税参数,并将版税参数传递给 begin_parse. royalty_params 是一个Cell,我们要传递一个片段,这样它就能被立即读取或写入我们要发送的信息中。- () send_royalty_params(slice to_address, int query_id, slice data) impure inline {# h" L! F$ F$ ^) g- q+ n
- var msg = begin_cell()* W/ T @/ L: F, m
- .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
/ Y. e2 Y; Z4 r. o( u# L - .store_slice(to_address)
. x& f1 Y; O( |/ \1 k - .store_coins(0) u6 Y) w+ _! t; C3 N) w6 s5 j
- .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)7 S# U; Z' z5 h$ h9 E
- .store_uint(op::report_royalty_params(), 32)+ U- Q; [5 J/ q3 `
- .store_uint(query_id, 64)7 [: O4 y; O, n( L1 G8 V" y
- .store_slice(data);0 b, J9 [( g. c! A" w
- send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message+ T8 b# E& y V- K; L' I
- }
复制代码 我们看看 send_royalty_params 已经实现。我们得到 to_address,我们得到 query_id, 和 slice_data, 这个片段是 royalty_params. 我们不想让这条信息跳转,因此将快捷标志改为 0x10. 这一切你们都很熟悉。我们将其发送给 to_address, 我们将操作代码、 query_id, 当然,我们把这些 royalty_params. 就是这么简单。你已经知道它是如何工作的了。对于版税参数,我们甚至不检查是谁要求的。接下来呢?- if (op == 1) { ;; deploy new nft
/ T+ r$ g9 I: y' M" \4 u" C - int item_index = in_msg_body~load_uint(64);) F3 s4 V2 e& n
- throw_unless(402, item_index <= next_item_index);
- ^/ y# K; b' k. f - var is_last = item_index == next_item_index; # ]" h# s& p7 H$ N
- deploy_nft_item(item_index, nft_item_code, in_msg_body~load_coins(), in_msg_body~load_ref());
. E2 L" L% J2 L* B1 a" l8 E2 \ - if (is_last) {
" j# v( X. Z0 N" ?! {+ `5 D9 v0 v0 @ - next_item_index += 1;) y" N& M3 ~3 q" ]) e1 `9 q
- save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);
8 M) b7 G9 |1 ]+ x - }
' V) S* G. f+ u% | m; b! S - return ();
" Q( p/ q! X. e* ?) ^ - }
复制代码 我们看到这个操作代码,这意味着我们要部署一个新的 NFT。这只能由所有者完成。这里最重要的是索引。您需要跟踪当前 Collections 的索引。例如 item_index 您要在这里部署的 next_item_index 存储在我们的数据库中。如果它们相等,我们实际上会增加 next_item_index 并将其再次放入存储中,这样我们的索引就会增加。你可以部署先前的索引 NFT,也可以部署当前的索引 NFT,然后增加 next_item_index.- () deploy_nft_item(int item_index, cell nft_item_code, int amount, cell nft_content) impure {/ G7 g# R+ t) Y& J
- cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);
8 t6 y# m' N( G4 w - slice nft_address = calculate_nft_item_address(workchain(), state_init);
$ O; H: s$ A2 ~- b - var msg = begin_cell()
6 q3 f" N: y6 _ - .store_uint(0x18, 6)
; n+ l+ z1 C8 f! F2 ?8 {- k% p - .store_slice(nft_address)
4 `8 N+ m/ R0 x7 k; ` - .store_coins(amount)+ f5 o6 x# b9 b
- .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)9 y5 v8 }, h% h% t
- .store_ref(state_init)
+ z5 z- Y! o2 U# } - .store_ref(nft_content);7 C1 B$ ~ m4 q+ ^+ t% y
- send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors3 p f. x. @ r5 X, Z/ N; X
- }
复制代码现在来看看这里是如何实现部署功能的。 deploy_nft_item 部署合约,这就是为什么我们在这里计算 state_init 的原因。我们正在与 state_init 一起计算地址。 此外,我们正在发送一条消息,其中包含我们放置在此处的一定金额。 让我们看看如何在部署时获得这笔金额。在 msg_body, 根据标准,您将看到您想要传递的代币数量,因为我们将其设置为 amount. 因此,我们用 state_init 来编写这条信息,然后发送并部署此合约。我们将在下一课回顾 NFT item合约时了解本地存储方案。部署 NFT item比我们使用 Jettons 时要简单得多,但工作方式类似。 - if (op == 2) { ;; batch deploy of new nfts
! m2 n1 |/ n; w6 b* h8 M& u - int counter = 0;+ n( ^! p& t( g9 ~
- cell deploy_list = in_msg_body~load_ref();; K- m: r+ \: l' G9 e$ h% g
- do {% |- o( h6 ^% h
- var (item_index, item, f?) = deploy_list~udict::delete_get_min(64);
; J$ Q- a, i% E" t& G3 \; \1 Y& r - if (f?) {9 Z0 g2 T. |( `6 S5 U# e
- counter += 1;0 ?# V7 a. M% M, U- r. c. g" b% @* ~
- if (counter >= 250) { ;; Limit due to limits of action list size v/ C% h0 s& B( O1 j0 I
- throw(399);0 f+ S. V+ _' q& j+ u
- }1 o: Y$ S$ S8 B0 ]! q
3 b9 M" t6 p! a3 ]* D$ z) X5 M0 w- throw_unless(403 + counter, item_index <= next_item_index);
+ x- m& {8 d* w! `% H. b) }9 l - deploy_nft_item(item_index, nft_item_code, item~load_coins(), item~load_ref());
. _2 J2 b9 }2 P+ \. u$ h - if (item_index == next_item_index) {+ \9 K1 v" C5 B" I' U p# b& i: Q$ y
- next_item_index += 1;
) t) }$ a8 E! T- Y- r - }
4 {- O5 H& b! p) w y0 ] - }/ g y3 j- c/ w" O
- } until ( ~ f?);
- @; Q3 r1 I! p3 ^2 B- e0 L - save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);; q+ m- X2 M7 G/ G9 R7 B1 r
- return ();
0 P% i4 l) a( j: w' J - }, S7 I9 J* D# r
- " n, M: Q7 ^; j/ |& S
- if (op == 3) { ;; change owner
0 d* V% m: I* t u - slice new_owner = in_msg_body~load_msg_addr();
! g5 v2 h) Z0 c( Y - save_data(new_owner, next_item_index, content, nft_item_code, royalty_params);
( R# e7 ^" ^# [" i7 i1 C3 L - return ();
复制代码还有两个操作代码我们应该弄清楚。 Change owner 只是一个标准的,非常简单易懂。所以,我就不深入探讨了。 更重要的是 batch deploy of new nfts, 要复杂一些。对于批量部署,你实际上是把一个Cell--比如你把一个Cell放在 msg_body. 这个Cell将有一个字典,因此您将浏览整个字典,一步一步地部署您的 NFT item。确保你的计数器永远不会超过 250, 因为这是你可以在这里部署 NFT item 的最大合约数。此外,你最好确保跟踪你的下一个 item 索引,以及它在本地存储中是如何不断增加的。当然,运行 deploy_nft_item 功能。
0 _0 D! V/ a) U+ v; D1 C
这份合约比我们与Jetton签订的合约要简单得多。同样重要的是,在 TON 中,所有东西都是一个合约。一个非常简单的逻辑可以创建一个复杂的合约系统。例如,在 Jettons 中,我们有矿工、钱包,而钱包又在部署其他钱包。在 NFT 中,我们有 collections, collections 部署的item也是合约。 在下一课中,我们将回顾 NFT item合约。我们将看看它是如何根据我们给出的初始状态进行初始化的。这将不仅仅是一个单一的合约,因为item可以有不同类型的合约。我们将在下一课讨论它们。我们很快就会再见面。 1 o" w1 P* c- t8 O4 \+ }. s v0 v
|