Ton

Форк
0
367 строк · 16.7 Кб
1
#include "stdlib.fc";
2
#include "common.fc";
3

4
int send_money(int my_balance, slice address, int value) impure {
5
    int amount_to_send = min(my_balance - min_tons_for_storage, value);
6
    if (amount_to_send > 0) {
7
        send_msg(address, amount_to_send, op::fill_up, cur_lt(), null(), 2); ;; ignore errors
8
        my_balance -= amount_to_send;
9
    }
10
    return my_balance;
11
}
12

13
(int, slice, cell) maybe_end_auction(int my_balance, slice owner, cell auction, cell royalty_params, int is_external) impure {
14
    (cell auction_state, cell auction_config) = unpack_auction(auction);
15
    (cell last_bid, int min_bid, int end_time) = unpack_auction_state(auction_state);
16
    if (now() < end_time) {
17
        return (my_balance, owner, auction);
18
    }
19
    if (is_external) {
20
        accept_message();
21
    }
22
    ;; should end auction
23
    if (null?(last_bid)) {
24
        ;; no stakes were made
25
        ;; NB: owner is not null now
26
        return (my_balance, owner, null());
27
    }
28
    (slice beneficiary_address, _, _, _, _, _) = unpack_auction_config(auction_config);
29
    (slice bidder_address, int bid, int bid_ts) = unpack_last_bid(last_bid);
30
    (int royalty_num, int royalty_denom, slice royalty_address) = unpack_nft_royalty_params(royalty_params);
31

32
    send_msg(bidder_address, 0, op::ownership_assigned, cur_lt(),
33
            begin_cell()
34
                    .store_slice(owner)
35
                    .store_int(0, 1)
36
                    .store_uint(op::teleitem_bid_info, 32)
37
                    .store_grams(bid)
38
                    .store_uint(bid_ts, 32),
39
            1); ;; paying fees, revert on errors
40

41
    if ((royalty_num > 0) & (royalty_denom > 0) & ~ equal_slices(royalty_address, beneficiary_address)) {
42
        int royalty_value = min(bid, muldiv(bid, royalty_num, royalty_denom));
43
        bid -= royalty_value;
44
        my_balance = send_money(my_balance, royalty_address, royalty_value);
45
    }
46

47
    my_balance = send_money(my_balance, beneficiary_address, bid);
48

49
    return (my_balance, bidder_address, null());
50
}
51

52
(int, cell) process_new_bid(int my_balance, slice new_bid_address, int new_bid, cell auction) impure {
53
    (cell auction_state, cell auction_config) = unpack_auction(auction);
54
    (cell old_last_bid, int min_bid, int end_time) = unpack_auction_state(auction_state);
55
    throw_if(err::too_small_stake, new_bid < min_bid);
56
    (slice beneficiary_address, int initial_min_bid, int max_bid, int min_bid_step, int min_extend_time, _) = unpack_auction_config(auction_config);
57
    cell new_last_bid = pack_last_bid(new_bid_address, new_bid, now());
58
    int new_end_time = max(end_time, now() + min_extend_time);
59
    if ((max_bid > 0) & (new_bid >= max_bid)) {
60
        ;; for maybe_end_auction
61
        new_end_time = 0;
62
    }
63
    ;; step is at least GR$1
64
    int new_min_bid = max(new_bid + one_ton, (new_bid * (100 + min_bid_step) + 99) / 100);
65
    ifnot (cell_null?(old_last_bid)) {
66
        (slice old_bidder_address, int old_bid, _) = unpack_last_bid(old_last_bid);
67
        int to_send = min(my_balance - min_tons_for_storage, old_bid);
68
        if (to_send > 0) {
69
            send_msg(old_bidder_address, to_send, op::outbid_notification, cur_lt(), null(), 1);
70
            my_balance -= to_send;
71
        }
72
    }
73
    cell new_auction_state = pack_auction_state(new_last_bid, new_min_bid, new_end_time);
74
    return (my_balance, pack_auction(new_auction_state, auction_config));
75
}
76

77
cell prepare_auction(cell auction_config) {
78
    (slice beneficiary_address, int initial_min_bid, int max_bid, int min_bid_step, int min_extend_time, int duration) = unpack_auction_config(auction_config);
79
    ;; check beneficiary address
80
    parse_std_addr(beneficiary_address);
81
    if ((initial_min_bid < 2 * min_tons_for_storage) | ((max_bid != 0) & (max_bid < initial_min_bid)) |
82
            (min_bid_step <= 0) | (min_extend_time > 60 * 60 * 24 * 7) | (duration > 60 * 60 * 24 * 365)) {
83
        return null();
84
    }
85
    cell auction_state = pack_auction_state(null(), initial_min_bid, now() + duration);
86
    return pack_auction(auction_state, auction_config);
87
}
88

89
cell deploy_item(int my_balance, slice msg) {
90
    ;; Do not throw errors here!
91
    (slice bidder_address, int bid, cell token_info, cell nft_content, cell auction_config, cell royalty_params) = unpack_teleitem_msg_deploy(msg);
92
    cell auction = prepare_auction(auction_config);
93
    if (cell_null?(auction)) {
94
        return null();
95
    }
96
    (my_balance, cell new_auction) = process_new_bid(my_balance, bidder_address, bid, auction);
97
    (my_balance, slice owner, new_auction) = maybe_end_auction(my_balance, zero_address(), new_auction, royalty_params, 0);
98
    cell content = pack_item_content(nft_content, null(), token_info);
99
    return pack_item_state(owner, content, new_auction, royalty_params);
100

101
}
102

103
slice transfer_ownership(int my_balance, slice owner_address, slice in_msg_body, int fwd_fees) impure inline {
104
    (int query_id, slice new_owner_address, slice response_destination, cell custom_payload, int forward_amount, slice forward_payload)
105
            = unpack_nft_cmd_transfer(in_msg_body);
106

107
    force_chain(new_owner_address);
108

109
    int rest_amount = my_balance - min_tons_for_storage;
110
    if (forward_amount) {
111
        rest_amount -= (forward_amount + fwd_fees);
112
    }
113
    int need_response = response_destination.preload_uint(2) != 0; ;; if NOT addr_none: 00
114
    if (need_response) {
115
        rest_amount -= fwd_fees;
116
    }
117

118
    throw_unless(err::not_enough_funds, rest_amount >= 0); ;; base nft spends fixed amount of gas, will not check for response
119

120
    if (forward_amount) {
121
        send_msg(new_owner_address, forward_amount, op::ownership_assigned, query_id,
122
                begin_cell().store_slice(owner_address).store_slice(forward_payload), 1); ;; paying fees, revert on errors
123

124
    }
125
    if (need_response) {
126
        force_chain(response_destination);
127
        send_msg(response_destination, rest_amount, op::excesses, query_id, null(), 1); ;; paying fees, revert on errors
128
    }
129

130
    return new_owner_address;
131
}
132

133
cell change_dns_record(cell dns, slice in_msg_body) {
134
    int key = in_msg_body~load_uint(256);
135
    int has_value = in_msg_body.slice_refs() > 0;
136

137
    if (has_value) {
138
        cell value = in_msg_body~load_ref();
139
        dns~udict_set_ref(256, key, value);
140
    } else {
141
        dns~udict_delete?(256, key);
142
    }
143

144
    return dns;
145
}
146

147
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
148
    int my_balance = pair_first(get_balance());
149
    slice cs = in_msg_full.begin_parse();
150
    int flags = cs~load_uint(4);
151

152
    if (flags & 1) { ;; ignore all bounced messages
153
        return ();
154
    }
155
    slice sender_address = cs~load_msg_addr();
156

157
    cs~load_msg_addr(); ;; skip dst
158
    cs~load_grams(); ;; skip value
159
    cs~load_maybe_ref(); ;; skip extracurrency collection
160
    cs~load_grams(); ;; skip ihr_fee
161
    int fwd_fee = muldiv(cs~load_grams(), 3, 2); ;; we use message fwd_:fee for estimation of forward_payload costs
162

163
    int op = in_msg_body.slice_empty?() ? 0 : in_msg_body~load_uint(32);
164

165
    (cell config, cell state) = unpack_item_data();
166
    (int index, slice collection_address) = unpack_item_config(config);
167

168
    if (equal_slices(collection_address, sender_address)) {
169
        throw_unless(err::forbidden_not_deploy, op == op::teleitem_msg_deploy);
170
        if (cell_null?(state)) {
171
            cell new_state = deploy_item(my_balance, in_msg_body);
172
            ifnot (cell_null?(new_state)) {
173
                return save_item_data(config, new_state);
174
            }
175
        }
176
        slice bidder_address = in_msg_body~load_msg_addr(); ;; first field in teleitem_msg_deploy
177
        send_msg(bidder_address, 0, op::teleitem_return_bid, cur_lt(), null(), 64); ;; carry all the remaining value of the inbound message
178
        return ();
179
    }
180

181
    throw_if(err::uninited, cell_null?(state));
182
    (slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
183

184
    if (op == op::get_royalty_params) {
185
        int query_id = in_msg_body~load_uint(64);
186
        send_msg(sender_address, 0, op::report_royalty_params, query_id, begin_cell().store_slice(royalty_params.begin_parse()), 64); ;; carry all the remaining value of the inbound message
187
        return ();
188
    }
189

190
    if (op == op::nft_cmd_get_static_data) {
191
        int query_id = in_msg_body~load_uint(64);
192
        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
193
        return ();
194
    }
195

196
    int is_topup = (op == 0) & equal_slices(in_msg_body, "#topup") & (in_msg_body.slice_refs() == 0);
197
    if (is_topup) {
198
        return ();
199
    }
200

201
    ifnot (cell_null?(auction)) {
202
        ;; sender do not pay for auction with its message
203
        my_balance -= msg_value;
204
        (my_balance, owner_address, auction) = maybe_end_auction(my_balance, owner_address, auction, royalty_params, 0);
205
        if (cell_null?(auction)) {
206
            cell new_state = pack_item_state(owner_address, content, auction, royalty_params);
207
            save_item_data(config, new_state);
208
        }
209
        my_balance += msg_value;
210
    }
211

212
    if (op == op::teleitem_cancel_auction) {
213
        throw_if(err::no_auction, cell_null?(auction));
214
        throw_unless(err::forbidden_auction, equal_slices(sender_address, owner_address));
215
        int query_id = in_msg_body~load_uint(64);
216
        (cell auction_state, cell auction_config) = unpack_auction(auction);
217
        (cell last_bid, int min_bid, int end_time) = unpack_auction_state(auction_state);
218
        throw_unless(err::already_has_stakes, cell_null?(last_bid));
219
        cell new_state = pack_item_state(owner_address, content, null(), royalty_params);
220
        if (query_id) {
221
            send_msg(sender_address, 0, op::teleitem_ok, query_id, null(), 64); ;; carry all the remaining value of the inbound message
222
        }
223
        return save_item_data(config, new_state);
224
    }
225

226
    ifnot (cell_null?(auction)) {
227
        throw_unless(err::forbidden_not_stake, op == 0);
228
        (my_balance, auction) = process_new_bid(my_balance, sender_address, msg_value, auction);
229
        (my_balance, owner_address, auction) = maybe_end_auction(my_balance, owner_address, auction, royalty_params, 0);
230
        cell new_state = pack_item_state(owner_address, content, auction, royalty_params);
231
        return save_item_data(config, new_state);
232
    }
233

234
    if (op == 0) {
235
        throw_unless(err::forbidden_topup, equal_slices(sender_address, owner_address)); ;; only owner can fill-up balance, prevent coins lost right after the auction
236
        ;; if owner send bid right after auction he can restore it by transfer response message
237
        return ();
238
    }
239

240
    if (op == op::teleitem_start_auction) {
241
        throw_unless(err::forbidden_auction, equal_slices(sender_address, owner_address));
242
        int query_id = in_msg_body~load_uint(64);
243
        cell new_auction_config = in_msg_body~load_ref();
244
        cell new_auction = prepare_auction(new_auction_config);
245
        throw_if(err::invalid_auction_config, cell_null?(new_auction));
246
        cell new_state = pack_item_state(owner_address, content, new_auction, royalty_params);
247
        if (query_id) {
248
            send_msg(sender_address, 0, op::teleitem_ok, query_id, null(), 64); ;; carry all the remaining value of the inbound message
249
        }
250
        return save_item_data(config, new_state);
251
    }
252

253
    if (op == op::nft_cmd_transfer) {
254
        throw_unless(err::forbidden_transfer, equal_slices(sender_address, owner_address));
255
        slice new_owner_address = transfer_ownership(my_balance, owner_address, in_msg_body, fwd_fee);
256
        cell new_state = pack_item_state(new_owner_address, content, auction, royalty_params);
257
        return save_item_data(config, new_state);
258
    }
259

260
    if (op == op::change_dns_record) { ;; change dns record
261
        int query_id = in_msg_body~load_uint(64);
262
        throw_unless(err::forbidden_change_dns, equal_slices(sender_address, owner_address));
263
        (cell nft_content, cell dns, cell token_info) = unpack_item_content(content);
264
        cell new_dns = change_dns_record(dns, in_msg_body);
265
        cell new_content = pack_item_content(nft_content, new_dns, token_info);
266
        cell new_state = pack_item_state(owner_address, new_content, auction, royalty_params);
267
        send_msg(sender_address, 0, op::teleitem_ok, query_id, null(), 64); ;; carry all the remaining value of the inbound message
268
        return save_item_data(config, new_state);
269
    }
270
    throw(err::unknown_op);
271
}
272

273
() recv_external(slice in_msg) impure {
274
    int my_balance = pair_first(get_balance());
275
    (cell config, cell state) = unpack_item_data();
276
    (slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
277
    (my_balance, owner_address, auction) = maybe_end_auction(my_balance, owner_address, auction, royalty_params, -1);
278
    cell new_state = pack_item_state(owner_address, content, auction, royalty_params);
279
    return save_item_data(config, new_state);
280
}
281

282
;;
283
;;  GET Methods
284
;;
285

286
(int, int, slice, slice, cell) get_nft_data() method_id {
287
    (cell config, cell state) = unpack_item_data();
288
    (int item_index, slice collection_address) = unpack_item_config(config);
289
    if (cell_null?(state)) {
290
        return (0, item_index, collection_address, zero_address(), null());
291
    }
292
    (slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
293
    (cell nft_content, cell dns, cell token_info) = unpack_item_content(content);
294
    return (-1, item_index, collection_address, owner_address, nft_content);
295
}
296

297
slice get_full_domain() method_id {
298
    (cell config, cell state) = unpack_item_data();
299
    (slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
300
    (cell nft_content, cell dns, cell token_info) = unpack_item_content(content);
301
    (slice token_name, slice domain) = unpack_token_info(token_info);
302
    return begin_cell().store_slice(domain).store_slice(token_name).store_int(0, 8).end_cell().begin_parse();
303
}
304

305
slice get_telemint_token_name() method_id {
306
    (cell config, cell state) = unpack_item_data();
307
    (slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
308
    (cell nft_content, cell dns, cell token_info) = unpack_item_content(content);
309
    (slice token_name, slice domain) = unpack_token_info(token_info);
310
    return token_name;
311
}
312

313
(slice, int, int, int, int) get_telemint_auction_state() method_id {
314
    (cell config, cell state) = unpack_item_data();
315
    (slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
316
    throw_if (err::no_auction, cell_null?(auction));
317
    (cell auction_state, cell auction_config) = unpack_auction(auction);
318
    (cell last_bid, int min_bid, int end_time) = unpack_auction_state(auction_state);
319
    (slice bidder_address, int bid, int bid_ts) = (null(), 0, 0);
320
    ifnot (cell_null?(last_bid)) {
321
        (bidder_address, bid, bid_ts) = unpack_last_bid(last_bid);
322
    }
323
    return (bidder_address, bid, bid_ts, min_bid, end_time);
324
}
325

326
(slice, int, int, int, int, int) get_telemint_auction_config() method_id {
327
    (cell config, cell state) = unpack_item_data();
328
    (slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
329
    if (cell_null?(auction)) {
330
        ;; Do not throw error, so it is easy to check if get_telemint_auction_config method exists
331
        return (null(), 0, 0, 0, 0, 0);
332
    }
333
    (cell auction_state, cell auction_config) = unpack_auction(auction);
334
    (slice beneficiary_address, int initial_min_bid, int max_bid, int min_bid_step, int min_extend_time, int duration) =
335
            unpack_auction_config(auction_config);
336
    return (beneficiary_address, initial_min_bid, max_bid, min_bid_step, min_extend_time, duration);
337
}
338

339
(int, int, slice) royalty_params() method_id {
340
    (cell config, cell state) = unpack_item_data();
341
    (slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
342
    (int numerator, int denominator, slice destination) = unpack_nft_royalty_params(royalty_params);
343
    return (numerator, denominator, destination);
344
}
345

346
(int, cell) dnsresolve(slice subdomain, int category) method_id {
347
    (cell config, cell state) = unpack_item_data();
348
    (slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
349
    (cell nft_content, cell dns, cell token_info) = unpack_item_content(content);
350

351
    int subdomain_bits = slice_bits(subdomain);
352
    throw_unless(err::bad_subdomain_length, subdomain_bits % 8 == 0);
353

354
    int starts_with_zero_byte = subdomain.preload_int(8) == 0;
355
    throw_unless(err::no_first_zero_byte, starts_with_zero_byte);
356

357
    if (subdomain_bits > 8) { ;; more than "." requested
358
        category = "dns_next_resolver"H;
359
    }
360

361
    if (category == 0) { ;;  all categories are requested
362
        return (8, dns);
363
    }
364

365
    (cell value, int found) = dns.udict_get_ref?(256, category);
366
    return (8, value);
367
}
368

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.