本帖最后由 riyad 于 2025-3-15 05:47 编辑
7 N- a# @! E/ Q6 e1 `' y( J
3 k7 r; Y: z) e3 n8 y( S2 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 {3 G7 ^8 N( h1 B# |
- var ds = get_data().begin_parse();
8 W. o4 J( {1 S5 m* p - return , g2 n% b* S" V) h- C
- (ds~load_msg_addr(), ;; owner_address( r8 r3 V5 D3 k, H/ b
- ds~load_uint(64), ;; next_item_index8 I, a/ p8 B! n9 ?6 P! M8 S Q
- ds~load_ref(), ;; content
! e2 o1 @- d/ J5 m8 i2 }+ Y4 c - ds~load_ref(), ;; nft_item_code
. P- }& A" r. i; s0 j - ds~load_ref() ;; royalty_params0 C& g" G1 _/ b8 Y U1 e* }( M
- );
* _$ ^1 |* I6 Z3 ]+ X0 o+ F8 p - }
复制代码 在这里,我们有 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 { H( p7 S( p0 c, w% S+ v0 _( [, |) V4 [
- set_data(begin_cell()0 a$ n3 P: T5 `0 Y- O
- .store_slice(owner_address)% {) U' h/ J7 P* ^7 X6 |. T
- .store_uint(next_item_index, 64)8 ^% k1 J7 @/ t. O" j& p6 L; S" x
- .store_ref(content)
! q! x [3 Y0 X. @, I - .store_ref(nft_item_code)
l8 f6 p! M: @) g - .store_ref(royalty_params)
7 t/ U" {. U; H7 ^! N* W - .end_cell());
1 {7 I/ m% u" R( z; S - }9 }5 a2 g @+ w
1 w r; i2 I1 ^9 Y! K- cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {
, |3 P( K8 W, K' T - cell data = begin_cell().store_uint(item_index, 64).store_slice(my_address()).end_cell();
5 q# r; v$ D1 F4 q8 v - return begin_cell().store_uint(0, 2).store_dict(nft_item_code).store_dict(data).store_uint(0, 1).end_cell();1 `# u& v4 ~* C R% o
- }
+ U2 O( V, V( { - : I w# _$ [. X5 Z+ u
- slice calculate_nft_item_address(int wc, cell state_init) {8 r1 O; J( N3 y
- return begin_cell().store_uint(4, 3)- F7 J0 v- H) e; Q
- .store_int(wc, 8)1 G& [ f$ d1 { J1 d( Z1 V
- .store_uint(cell_hash(state_init), 256)' Z7 `2 l9 g( |0 v
- .end_cell()
6 e9 Z, f+ \2 N$ f' ^ - .begin_parse();
0 w3 t6 P) \$ l - }
复制代码就像 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 {
$ t$ [' d- Z, g; i. r3 T& y8 F - cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);- ~7 ^6 n2 V' q$ L7 r
- slice nft_address = calculate_nft_item_address(workchain(), state_init);$ x! u+ j! ~$ x' t1 @, M6 V& ]
- var msg = begin_cell()4 ~: V2 |4 C: ~: U8 R
- .store_uint(0x18, 6)0 Q9 v' a6 x) Z$ ~; g
- .store_slice(nft_address)
1 [; {% r7 h$ J$ P7 L - .store_coins(amount) r- U- ]# n' ~
- .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
' H! m: o- b1 \0 {: c7 @ - .store_ref(state_init)2 |3 u$ [; m: w+ g: _1 }( t5 I
- .store_ref(nft_content);
0 C# ~- c# o) R. z/ f! ]0 h - send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
7 ~0 f$ e- ]' A5 l- w n3 Z - }1 r$ i; S0 p* i# j% D' _' C- q" [
6 Q7 m- R" F/ e9 K4 u* Q8 Y$ n) l- () send_royalty_params(slice to_address, int query_id, slice data) impure inline {
5 U3 ~% s; G7 E - var msg = begin_cell()
: v; R6 Z4 R0 m0 \ - .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 0110008 j3 k9 H* F- k* X
- .store_slice(to_address)
/ P$ a1 }$ B/ s* L6 ? - .store_coins(0)
* [, M1 g: |& v/ M' q - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
k6 q2 o/ a; H% q' N+ k - .store_uint(op::report_royalty_params(), 32)
2 b7 w# U! ~6 H - .store_uint(query_id, 64)" ~; J* c/ s$ m2 c+ j) t# A# i9 e
- .store_slice(data);% a ~- Y" J9 E! X3 l: A
- send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message
/ T8 ?4 e, v W) O# u, ` - }
复制代码这一切都要按照标准的要求进行。由于您必须遵守标准,因此您的合约将被视为 NFT 合约。
3 [2 @% `: l) p: m* I- () recv_internal(cell in_msg_full, slice in_msg_body) impure {
% }( o7 Q) B& e3 V/ f" c - if (in_msg_body.slice_empty?()) { ;; ignore empty messages9 ~6 G* R, x+ B9 \5 ^
- return ();
0 W* v* D7 W" m# z+ R - }
. o, B" P( M; @: n - slice cs = in_msg_full.begin_parse();) i8 e0 I2 M3 C+ ~' K0 I4 w
- int flags = cs~load_uint(4);
0 j9 \ c2 y. I3 @6 y$ j% e3 N - ' m6 {* I3 u s$ F( v! L& B8 c
1 l4 o' Q. f! d! u# X- if (flags & 1) { ;; ignore all bounced messages- y; V, s8 W( Q. i5 t; N
- return ();
7 L% b& F$ f8 j1 I0 }/ z. u; t3 A1 t - }& L x) ]' L! O+ Y. S8 s ]
- slice sender_address = cs~load_msg_addr();
: J5 a0 P% O' p6 R
- G( {3 G, w. r2 {& g7 |- int op = in_msg_body~load_uint(32);5 x3 I0 O9 y- c$ l1 Y
- int query_id = in_msg_body~load_uint(64);
' B7 z- Y. z% S+ t+ z3 f - , g# o% U4 f* p& ~
- var (owner_address, next_item_index, content, nft_item_code, royalty_params) = load_data();
复制代码 我们接收到一条内部信息,我们发现我们不想处理空的正文信息,因此我们要检查它是否为空。然后我们检查标记,忽略跳转信息,得到 sender_address, 阅读 in_msg_body, 运行代码、 query_id, 然后是本地存储中的所有内容。 A% l6 x4 G5 k" p8 S, g
- if (op == op::get_royalty_params()) {
2 a9 I' W: c' Q# o, X1 J - send_royalty_params(sender_address, query_id, royalty_params.begin_parse());% v/ ?: V H1 j% z7 ?* M
- return ();
' i/ {7 {, ]/ c# k3 i% e0 c* S* J6 R - }
`" y% {1 d2 Q% d
6 a: ?8 a1 [- f) _/ w9 U2 O- 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 {2 O2 y) b, m, T7 v
- var msg = begin_cell()
3 P) y+ c, Q& R+ T - .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
+ ]3 n5 Z9 b# L/ ?; e% o - .store_slice(to_address): d/ x6 }8 U* ~3 r& F* N
- .store_coins(0)
7 v' ]- _0 {8 G$ H: K) z - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)0 y1 |5 i, \: z. I4 ^% A8 v
- .store_uint(op::report_royalty_params(), 32)+ N( m/ q- l# j( f
- .store_uint(query_id, 64)* W& f* C3 ?9 ^9 Z0 |/ r
- .store_slice(data);4 [( X- ]7 j8 F) D
- send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message
' S8 ] b" h0 Z7 S# G8 L - }
复制代码 我们看看 send_royalty_params 已经实现。我们得到 to_address,我们得到 query_id, 和 slice_data, 这个片段是 royalty_params. 我们不想让这条信息跳转,因此将快捷标志改为 0x10. 这一切你们都很熟悉。我们将其发送给 to_address, 我们将操作代码、 query_id, 当然,我们把这些 royalty_params. 就是这么简单。你已经知道它是如何工作的了。对于版税参数,我们甚至不检查是谁要求的。接下来呢?- if (op == 1) { ;; deploy new nft
" V9 ]! w) u: ~, ^ H5 Q4 T2 s5 q - int item_index = in_msg_body~load_uint(64);
" ~: Z' D2 H$ x2 ^ - throw_unless(402, item_index <= next_item_index);
7 ^* g, w6 Z6 ~5 s) C - var is_last = item_index == next_item_index;
# V4 r1 {7 ?1 L5 u - deploy_nft_item(item_index, nft_item_code, in_msg_body~load_coins(), in_msg_body~load_ref());
6 k/ I5 v2 z0 w" m) ]" \ - if (is_last) {
. N4 q( ^7 h$ n5 U# c - next_item_index += 1;
6 G. ~# N: a, _; O7 w - save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);
- A# a+ ^7 D: V4 ]4 }) x9 d - }
9 o& g r8 u3 g7 ]8 f' Y: ` - return (); t: I( A, I8 w; K+ y) g
- }
复制代码 我们看到这个操作代码,这意味着我们要部署一个新的 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 {" `2 s0 g+ {' i
- cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);3 J/ x3 ~4 t$ E, S8 L
- slice nft_address = calculate_nft_item_address(workchain(), state_init);
4 c! l! ^* i7 n, v8 O+ q w& s - var msg = begin_cell()% F/ e4 A; g" K; b) \
- .store_uint(0x18, 6)
* A5 r! C( }6 K/ i( _9 R - .store_slice(nft_address)" O$ S m0 m$ ^. G
- .store_coins(amount)! h; q" i, z' y% J
- .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1): y4 T) u+ V$ W6 h$ U- a. f5 k4 X1 K
- .store_ref(state_init)
, }' S L* B! W' _$ n - .store_ref(nft_content);) c% B7 N2 J3 z8 z- v( n
- send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
, i8 A7 b" e3 `3 l - }
复制代码现在来看看这里是如何实现部署功能的。 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+ [& l0 n" k) }- N0 E8 j0 e2 A
- int counter = 0;
?& S0 m3 W% {* o$ T2 R1 q3 V - cell deploy_list = in_msg_body~load_ref();, z; ?+ J6 o$ b$ c; f3 d; L
- do {
$ g; B# G3 t/ M) g/ Q1 o& t; h3 ? - var (item_index, item, f?) = deploy_list~udict::delete_get_min(64);
7 L3 V. b( y* ~* x" L4 E# u Q4 r; i - if (f?) {
6 x" d9 Q i2 l# [( k/ ^" i - counter += 1;7 W3 Y% r" g- W8 y
- if (counter >= 250) { ;; Limit due to limits of action list size9 Q; ]% C- f( [- g
- throw(399);
# n, f) D( a6 ]5 o8 y2 z. o - }9 Q- g! F6 z/ p5 @; Q: P: S
- 8 S2 K) {" l/ ]8 O: H% Q- d
- throw_unless(403 + counter, item_index <= next_item_index);
, z: n' u* ^# W" v- v1 G8 e1 k" h - deploy_nft_item(item_index, nft_item_code, item~load_coins(), item~load_ref());5 m$ Q, v8 F/ [& w: V7 M3 U: Y
- if (item_index == next_item_index) {( g6 D. A$ Z! S5 D2 X/ P( n1 w0 }
- next_item_index += 1;$ u9 A/ V3 ]8 `; L$ T+ G2 ~
- }% M2 j0 _$ G1 c. ^4 h6 Z) ~
- }& L) K- T- P$ k! a% t
- } until ( ~ f?);, j) p; C+ ]3 h$ |1 C! f. Z2 ^
- save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);7 w# n* p' B% I+ e6 F2 t ]
- return ();
! E$ [" d- D8 s! L - }
7 K1 }$ q5 X9 r. v: p, c
8 S0 p0 O @% B/ U- if (op == 3) { ;; change owner7 p% ^2 v/ b: _) h8 R
- slice new_owner = in_msg_body~load_msg_addr();" x7 o: b" o( l& m7 Y
- save_data(new_owner, next_item_index, content, nft_item_code, royalty_params);: F5 d. S* Y4 s8 V
- return ();
复制代码还有两个操作代码我们应该弄清楚。 Change owner 只是一个标准的,非常简单易懂。所以,我就不深入探讨了。 更重要的是 batch deploy of new nfts, 要复杂一些。对于批量部署,你实际上是把一个Cell--比如你把一个Cell放在 msg_body. 这个Cell将有一个字典,因此您将浏览整个字典,一步一步地部署您的 NFT item。确保你的计数器永远不会超过 250, 因为这是你可以在这里部署 NFT item 的最大合约数。此外,你最好确保跟踪你的下一个 item 索引,以及它在本地存储中是如何不断增加的。当然,运行 deploy_nft_item 功能。 + X2 p! U; [( t6 w! d/ ? {
这份合约比我们与Jetton签订的合约要简单得多。同样重要的是,在 TON 中,所有东西都是一个合约。一个非常简单的逻辑可以创建一个复杂的合约系统。例如,在 Jettons 中,我们有矿工、钱包,而钱包又在部署其他钱包。在 NFT 中,我们有 collections, collections 部署的item也是合约。 在下一课中,我们将回顾 NFT item合约。我们将看看它是如何根据我们给出的初始状态进行初始化的。这将不仅仅是一个单一的合约,因为item可以有不同类型的合约。我们将在下一课讨论它们。我们很快就会再见面。 9 d' Z" t- i% A, |$ b8 C
|