Ton
173 строки · 6.1 Кб
1;; NFT collection smart contract
2
3;; storage scheme
4;; default#_ royalty_factor:uint16 royalty_base:uint16 royalty_address:MsgAddress = RoyaltyParams;
5;; storage#_ owner_address:MsgAddress next_item_index:uint64
6;; ^[collection_content:^Cell common_content:^Cell]
7;; nft_item_code:^Cell
8;; royalty_params:^RoyaltyParams
9;; = Storage;
10
11#include "op-codes.fc";
12#include "stdlib.fc";
13#include "params.fc";
14
15(slice, int, cell, cell, cell) load_data() inline {
16var ds = get_data().begin_parse();
17return
18(ds~load_msg_addr(), ;; owner_address
19ds~load_uint(64), ;; next_item_index
20ds~load_ref(), ;; content
21ds~load_ref(), ;; nft_item_code
22ds~load_ref() ;; royalty_params
23);
24}
25
26() save_data(slice owner_address, int next_item_index, cell content, cell nft_item_code, cell royalty_params) impure inline {
27set_data(begin_cell()
28.store_slice(owner_address)
29.store_uint(next_item_index, 64)
30.store_ref(content)
31.store_ref(nft_item_code)
32.store_ref(royalty_params)
33.end_cell());
34}
35
36cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {
37cell data = begin_cell().store_uint(item_index, 64).store_slice(my_address()).end_cell();
38return begin_cell().store_uint(0, 2).store_dict(nft_item_code).store_dict(data).store_uint(0, 1).end_cell();
39}
40
41slice calculate_nft_item_address(int wc, cell state_init) {
42return begin_cell().store_uint(4, 3)
43.store_int(wc, 8)
44.store_uint(cell_hash(state_init), 256)
45.end_cell()
46.begin_parse();
47}
48
49() deploy_nft_item(int item_index, cell nft_item_code, int amount, cell nft_content) impure {
50cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);
51slice nft_address = calculate_nft_item_address(workchain(), state_init);
52var msg = begin_cell()
53.store_uint(0x18, 6)
54.store_slice(nft_address)
55.store_coins(amount)
56.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
57.store_ref(state_init)
58.store_ref(nft_content);
59send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
60}
61
62() send_royalty_params(slice to_address, int query_id, slice data) impure inline {
63var msg = begin_cell()
64.store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool packages:MsgAddress -> 011000
65.store_slice(to_address)
66.store_coins(0)
67.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
68.store_uint(op::report_royalty_params(), 32)
69.store_uint(query_id, 64)
70.store_slice(data);
71send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message
72}
73
74() recv_internal(cell in_msg_full, slice in_msg_body) impure {
75if (in_msg_body.slice_empty?()) { ;; ignore empty messages
76return ();
77}
78slice cs = in_msg_full.begin_parse();
79int flags = cs~load_uint(4);
80
81if (flags & 1) { ;; ignore all bounced messages
82return ();
83}
84slice sender_address = cs~load_msg_addr();
85
86int op = in_msg_body~load_uint(32);
87int query_id = in_msg_body~load_uint(64);
88
89var (owner_address, next_item_index, content, nft_item_code, royalty_params) = load_data();
90
91if (op == op::get_royalty_params()) {
92send_royalty_params(sender_address, query_id, royalty_params.begin_parse());
93return ();
94}
95
96throw_unless(401, equal_slices(sender_address, owner_address));
97
98
99if (op == 1) { ;; deploy new nft
100int item_index = in_msg_body~load_uint(64);
101throw_unless(402, item_index <= next_item_index);
102var is_last = item_index == next_item_index;
103deploy_nft_item(item_index, nft_item_code, in_msg_body~load_coins(), in_msg_body~load_ref());
104if (is_last) {
105next_item_index += 1;
106save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);
107}
108return ();
109}
110if (op == 2) { ;; batch deploy of new nfts
111int counter = 0;
112cell deploy_list = in_msg_body~load_ref();
113do {
114var (item_index, item, f?) = deploy_list~udict::delete_get_min(64);
115if (f?) {
116counter += 1;
117if (counter >= 250) { ;; Limit due to limits of action list size
118throw(399);
119}
120
121throw_unless(403 + counter, item_index <= next_item_index);
122deploy_nft_item(item_index, nft_item_code, item~load_coins(), item~load_ref());
123if (item_index == next_item_index) {
124next_item_index += 1;
125}
126}
127} until ( ~ f?);
128save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);
129return ();
130}
131if (op == 3) { ;; change owner
132slice new_owner = in_msg_body~load_msg_addr();
133save_data(new_owner, next_item_index, content, nft_item_code, royalty_params);
134return ();
135}
136if (op == 4) { ;; change content
137save_data(owner_address, next_item_index, in_msg_body~load_ref(), nft_item_code, in_msg_body~load_ref());
138return ();
139}
140throw(0xffff);
141}
142
143;; Get methods
144
145(int, cell, slice) get_collection_data() method_id {
146var (owner_address, next_item_index, content, _, _) = load_data();
147slice cs = content.begin_parse();
148return (next_item_index, cs~load_ref(), owner_address);
149}
150
151slice get_nft_address_by_index(int index) method_id {
152var (_, _, _, nft_item_code, _) = load_data();
153cell state_init = calculate_nft_item_state_init(index, nft_item_code);
154return calculate_nft_item_address(0, state_init);
155}
156
157(int, int, slice) royalty_params() method_id {
158var (_, _, _, _, royalty) = load_data();
159slice rs = royalty.begin_parse();
160return (rs~load_uint(16), rs~load_uint(16), rs~load_msg_addr());
161}
162
163cell get_nft_content(int index, cell individual_nft_content) method_id {
164var (_, _, content, _, _) = load_data();
165slice cs = content.begin_parse();
166cs~load_ref();
167slice common_content = cs~load_ref().begin_parse();
168return (begin_cell()
169.store_uint(1, 8) ;; offchain tag
170.store_slice(common_content)
171.store_ref(individual_nft_content)
172.end_cell());
173}
174