本帖最后由 riyad 于 2025-3-15 05:47 编辑
3 P+ C7 \* g" H, u4 q2 v5 I5 P
' g, v1 s4 t4 T这里还有两种类型的合约: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 {
) |2 p* M' L- U6 D - var ds = get_data().begin_parse();* g& F: S7 f4 Y1 [0 `
- return ) X0 }, H# m8 r
- (ds~load_msg_addr(), ;; owner_address' c+ P2 D2 O, ?8 N. F5 p3 O
- ds~load_uint(64), ;; next_item_index
0 ?& Y$ _5 A! u$ |2 G' ^ - ds~load_ref(), ;; content- E7 ?- N, Y: f5 \) ]
- ds~load_ref(), ;; nft_item_code* ]5 q5 V. H' p% }. {2 P2 t
- ds~load_ref() ;; royalty_params
6 t' v9 L4 c# z: L& j8 s - );7 V1 m" p6 l8 y
- }
复制代码 在这里,我们有 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 {
' j# V0 S' _ i% f; d& E# O - set_data(begin_cell()
v: F/ R; s) R) e& X8 {8 m - .store_slice(owner_address)- {/ K3 I, e5 X) r5 ?
- .store_uint(next_item_index, 64)6 g8 @: ]& [& O4 r
- .store_ref(content)4 w& Y/ I: E7 K: Z$ I# s* A
- .store_ref(nft_item_code)
1 Q* e+ S: S. O% w9 U0 O2 k - .store_ref(royalty_params)
2 q1 L7 U% F) ]* ~% k4 P - .end_cell());
. ?; ^' O, D6 [& O* R( ^% n - }
% q ?2 [' q5 K& j2 ^
+ ?2 o4 } ~. n" J$ H% w; G4 H5 Z- cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {$ L7 z/ ?# ^( g! g
- cell data = begin_cell().store_uint(item_index, 64).store_slice(my_address()).end_cell();
; Y. T9 N6 G/ i4 J2 M4 K" W. t - return begin_cell().store_uint(0, 2).store_dict(nft_item_code).store_dict(data).store_uint(0, 1).end_cell();
2 {7 v, j! Y5 h" D* a - }
$ k+ E8 i" J( g) k8 P2 }4 p% w - $ H4 C7 H. C+ K6 O7 _3 q& V
- slice calculate_nft_item_address(int wc, cell state_init) {
7 H+ h( F1 A: b% O; J9 a9 U) n - return begin_cell().store_uint(4, 3)
2 |& |& F+ J B- P5 e% g - .store_int(wc, 8)8 y/ Q- N9 _' A. ~( [
- .store_uint(cell_hash(state_init), 256)2 a5 q" _& G1 g R1 @
- .end_cell()
3 O* ~2 Z$ `$ }9 ^4 h6 f - .begin_parse();7 m- X. C' O; W3 e
- }
复制代码就像 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 {
, @) X0 D- s* ]2 l" ^7 ^+ O6 c - cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);
9 f) l ^0 k' L' w( l7 m& c( ?5 h( E - slice nft_address = calculate_nft_item_address(workchain(), state_init);
* m5 R: d* H$ u5 g: O - var msg = begin_cell(). k0 U$ \1 H6 s3 m! y' d: b
- .store_uint(0x18, 6)
' C. W7 C9 }0 u" a. | - .store_slice(nft_address)
; G: }% }% n6 q$ Z. z - .store_coins(amount)' i7 \ T/ `; n+ H: C$ X' v# ~+ k
- .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)- s" n( g) \6 Y# g4 f0 z" E% T; \
- .store_ref(state_init)! H$ e3 N/ R9 e' x. ?
- .store_ref(nft_content);! E. Y+ }. ? L
- send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors. ^2 L$ ` K! u+ |
- }5 x1 {$ @. a/ C* \% r
9 k s O( _' `( X- () send_royalty_params(slice to_address, int query_id, slice data) impure inline {0 B9 n6 ?- L f% {$ P2 p; T* u
- var msg = begin_cell()
- g' G& J8 j2 l8 }; Z+ J - .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000* o6 ~/ ~5 w5 d: B
- .store_slice(to_address), Z. F1 P4 z6 l+ U0 _5 u
- .store_coins(0)0 \# ~- }, w, O
- .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
7 W3 y7 X2 Z1 G% b - .store_uint(op::report_royalty_params(), 32)0 x$ e! F( {4 |2 p C/ i
- .store_uint(query_id, 64)7 w0 k8 j/ O0 ^& [
- .store_slice(data);
- F2 X* k3 W/ |- ^3 ] - send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message
! m/ h* K" C0 w3 X - }
复制代码这一切都要按照标准的要求进行。由于您必须遵守标准,因此您的合约将被视为 NFT 合约。
6 V$ E& x2 k) @( s+ \) Y+ p- () recv_internal(cell in_msg_full, slice in_msg_body) impure {! W: ^3 [6 b: l% p0 i0 |
- if (in_msg_body.slice_empty?()) { ;; ignore empty messages" F/ z9 n. w- }6 j; R
- return ();
1 N, U, l1 D: o) K0 c/ s. ~6 M& W - }) c% S( _; P4 q+ Y; O
- slice cs = in_msg_full.begin_parse();
; M" u& Y: ]* k9 G - int flags = cs~load_uint(4);
6 C$ O% r/ t* Y) Z9 ]) h5 O! o
- U% _) J6 ^4 H. y
, R* H( C0 T& |/ b7 z+ M S1 q+ t- if (flags & 1) { ;; ignore all bounced messages- |, X* F3 Z' M
- return ();
' c/ M2 y) n2 N8 T - }7 ]' o7 H& z( g' O& [ c3 y
- slice sender_address = cs~load_msg_addr();
4 y! y. L+ ]6 c S2 H
7 z; u* Y" `- p- int op = in_msg_body~load_uint(32);; |0 ^3 `5 F2 q( G0 ~& a: o0 `5 o& ]$ y
- int query_id = in_msg_body~load_uint(64);
! a+ W0 y: ~9 A* u% b - 3 H# F( q" B' _' J6 [
- var (owner_address, next_item_index, content, nft_item_code, royalty_params) = load_data();
复制代码 我们接收到一条内部信息,我们发现我们不想处理空的正文信息,因此我们要检查它是否为空。然后我们检查标记,忽略跳转信息,得到 sender_address, 阅读 in_msg_body, 运行代码、 query_id, 然后是本地存储中的所有内容。
" m Y, J# E8 o! ]- if (op == op::get_royalty_params()) {
/ `, v$ ]* F ~5 j - send_royalty_params(sender_address, query_id, royalty_params.begin_parse());0 R8 J9 C1 K# n# }
- return ();
; w" P- R1 a R _. G+ O1 z8 ?6 E% W - }3 q# U4 \' r; q, @" S" B5 J+ m7 B O
- 8 e& p9 f. E. v! i
- 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 {
( q7 f7 O: }: g- t - var msg = begin_cell()8 n7 O7 p. Z/ e& X+ m- g5 F) `
- .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000! b+ |; R- G- x- S6 b7 `
- .store_slice(to_address) x" `0 c5 X' A+ a) r. T) X: b, F
- .store_coins(0)
/ s. w$ B+ Q# B8 w' ~$ |$ |% _ - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
6 q5 c! O; E# w6 F' K - .store_uint(op::report_royalty_params(), 32)1 Q* j5 W) k4 x: ~
- .store_uint(query_id, 64)
! x& Z8 X" ]/ p; r8 Y/ O0 h - .store_slice(data);. i' L' v7 N( y
- send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message4 {) }! H3 R" c7 u2 J
- }
复制代码 我们看看 send_royalty_params 已经实现。我们得到 to_address,我们得到 query_id, 和 slice_data, 这个片段是 royalty_params. 我们不想让这条信息跳转,因此将快捷标志改为 0x10. 这一切你们都很熟悉。我们将其发送给 to_address, 我们将操作代码、 query_id, 当然,我们把这些 royalty_params. 就是这么简单。你已经知道它是如何工作的了。对于版税参数,我们甚至不检查是谁要求的。接下来呢?- if (op == 1) { ;; deploy new nft
/ y: g2 z0 R( u$ Y" f1 m& o - int item_index = in_msg_body~load_uint(64);2 w2 @+ `' N+ a3 R; |# L
- throw_unless(402, item_index <= next_item_index);
! o! {# m- M, d! K4 R2 P6 E - var is_last = item_index == next_item_index; $ e% ^# ]. F! x9 Q( G
- deploy_nft_item(item_index, nft_item_code, in_msg_body~load_coins(), in_msg_body~load_ref());
) f* v/ N1 I# G5 U5 h. l - if (is_last) {
: h- f' E+ b2 a+ B4 K - next_item_index += 1;: ^. \* W" }. V* q6 ?" \% p$ D
- save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);; p, C- I; T3 y
- }
$ [, Y+ \* ?7 V. |9 r) M; @ - return ();9 _3 E, t( V" Z8 l. [) u; L; n
- }
复制代码 我们看到这个操作代码,这意味着我们要部署一个新的 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 {3 r: J& M7 I& i$ L8 \5 g* }) _
- cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);
+ A5 |/ a* u3 b+ p2 M5 l+ p - slice nft_address = calculate_nft_item_address(workchain(), state_init);
6 c) i( G" D# D- `2 O - var msg = begin_cell()
, B; l% V6 X' D7 Y; b - .store_uint(0x18, 6). j. I( a+ Z& X3 w& U1 B6 C
- .store_slice(nft_address)6 t; R8 d4 J [; S. H
- .store_coins(amount)
/ d9 y- m" k% Z$ Z - .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)) z' w) L! `3 y; @# z- O
- .store_ref(state_init) I4 p7 X9 L$ L$ M" n8 ^, P" c! y
- .store_ref(nft_content);
+ T* n0 q7 j5 Z7 h* h- I8 { - send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
" h, s5 w2 ^' w4 o x" i( B6 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
6 ?- Z+ o% I, [; b) n, U' ]5 I - int counter = 0;
) g, M( g5 s! ^3 ?- U' A - cell deploy_list = in_msg_body~load_ref();( S3 @, D: q, y- n
- do {% A+ `3 I' }) @# [" \
- var (item_index, item, f?) = deploy_list~udict::delete_get_min(64);
" [. u' x( c$ {3 n! w2 G - if (f?) {3 K+ Z+ ]( I8 g/ y
- counter += 1;$ {! g- B) a$ p8 z: \4 T3 i4 G1 X
- if (counter >= 250) { ;; Limit due to limits of action list size
, ^' E$ _- j: e9 y" K4 w& D( w0 l - throw(399);; b% F" y) V0 B( x+ ~& a, P
- }
5 J5 `! O' T p7 b) s9 a - 3 q+ f. L& N" a( H% W
- throw_unless(403 + counter, item_index <= next_item_index);) |1 @' a% M# z
- deploy_nft_item(item_index, nft_item_code, item~load_coins(), item~load_ref());& b; Y/ ^0 W# R
- if (item_index == next_item_index) {
! ^+ P+ v* @/ X5 n& m - next_item_index += 1;* J; V% S: ]' D2 w U& Y9 e
- }! f! n. u3 a& C3 N
- }7 s' \* [, u2 G1 r: \* [
- } until ( ~ f?);
: c% E' ^# p1 G, v - save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);7 ]: H! d" i" T2 [: `9 R6 c
- return ();& a( J/ Y0 M2 |8 f6 v( J
- }
8 a3 `: Q; {5 ^9 c/ y - 2 Q5 H# k! y; \5 x8 b" k$ ^
- if (op == 3) { ;; change owner
4 ?( \/ ^- d9 x - slice new_owner = in_msg_body~load_msg_addr();+ D5 m2 r2 f2 I% u/ _7 U% u9 V+ C" ^
- save_data(new_owner, next_item_index, content, nft_item_code, royalty_params);/ A* {6 M. c: e, S
- return ();
复制代码还有两个操作代码我们应该弄清楚。 Change owner 只是一个标准的,非常简单易懂。所以,我就不深入探讨了。 更重要的是 batch deploy of new nfts, 要复杂一些。对于批量部署,你实际上是把一个Cell--比如你把一个Cell放在 msg_body. 这个Cell将有一个字典,因此您将浏览整个字典,一步一步地部署您的 NFT item。确保你的计数器永远不会超过 250, 因为这是你可以在这里部署 NFT item 的最大合约数。此外,你最好确保跟踪你的下一个 item 索引,以及它在本地存储中是如何不断增加的。当然,运行 deploy_nft_item 功能。
, A( l- [; P2 Q% D: J1 @
这份合约比我们与Jetton签订的合约要简单得多。同样重要的是,在 TON 中,所有东西都是一个合约。一个非常简单的逻辑可以创建一个复杂的合约系统。例如,在 Jettons 中,我们有矿工、钱包,而钱包又在部署其他钱包。在 NFT 中,我们有 collections, collections 部署的item也是合约。 在下一课中,我们将回顾 NFT item合约。我们将看看它是如何根据我们给出的初始状态进行初始化的。这将不仅仅是一个单一的合约,因为item可以有不同类型的合约。我们将在下一课讨论它们。我们很快就会再见面。 & R% L; _# ^0 `/ O7 Y% T D9 B
|