1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-15 04:32:21 +00:00
ton/crypto/func/auto-tests/legacy_tests/tele-nft-item/nft-item.fc
EmelyanenkoK 6b49d6a382
Add legacy_tester for existing funC contracts (#588)
* Add legacy_tester for existing funC contracts

* Add storage-contracts and pragma options
2023-01-12 12:33:15 +03:00

367 lines
17 KiB
Text

#include "stdlib.fc";
#include "common.fc";
int send_money(int my_balance, slice address, int value) impure {
int amount_to_send = min(my_balance - min_tons_for_storage, value);
if (amount_to_send > 0) {
send_msg(address, amount_to_send, op::fill_up, cur_lt(), null(), 2); ;; ignore errors
my_balance -= amount_to_send;
}
return my_balance;
}
(int, slice, cell) maybe_end_auction(int my_balance, slice owner, cell auction, cell royalty_params, int is_external) impure {
(cell auction_state, cell auction_config) = unpack_auction(auction);
(cell last_bid, int min_bid, int end_time) = unpack_auction_state(auction_state);
if (now() < end_time) {
return (my_balance, owner, auction);
}
if (is_external) {
accept_message();
}
;; should end auction
if (null?(last_bid)) {
;; no stakes were made
;; NB: owner is not null now
return (my_balance, owner, null());
}
(slice beneficiary_address, _, _, _, _, _) = unpack_auction_config(auction_config);
(slice bidder_address, int bid, int bid_ts) = unpack_last_bid(last_bid);
(int royalty_num, int royalty_denom, slice royalty_address) = unpack_nft_royalty_params(royalty_params);
send_msg(bidder_address, 0, op::ownership_assigned, cur_lt(),
begin_cell()
.store_slice(owner)
.store_int(0, 1)
.store_uint(op::teleitem_bid_info, 32)
.store_grams(bid)
.store_uint(bid_ts, 32),
1); ;; paying fees, revert on errors
if ((royalty_num > 0) & (royalty_denom > 0) & ~ equal_slices(royalty_address, beneficiary_address)) {
int royalty_value = min(bid, muldiv(bid, royalty_num, royalty_denom));
bid -= royalty_value;
my_balance = send_money(my_balance, royalty_address, royalty_value);
}
my_balance = send_money(my_balance, beneficiary_address, bid);
return (my_balance, bidder_address, null());
}
(int, cell) process_new_bid(int my_balance, slice new_bid_address, int new_bid, cell auction) impure {
(cell auction_state, cell auction_config) = unpack_auction(auction);
(cell old_last_bid, int min_bid, int end_time) = unpack_auction_state(auction_state);
throw_if(err::too_small_stake, new_bid < min_bid);
(slice beneficiary_address, int initial_min_bid, int max_bid, int min_bid_step, int min_extend_time, _) = unpack_auction_config(auction_config);
cell new_last_bid = pack_last_bid(new_bid_address, new_bid, now());
int new_end_time = max(end_time, now() + min_extend_time);
if ((max_bid > 0) & (new_bid >= max_bid)) {
;; for maybe_end_auction
new_end_time = 0;
}
;; step is at least GR$1
int new_min_bid = max(new_bid + one_ton, (new_bid * (100 + min_bid_step) + 99) / 100);
ifnot (cell_null?(old_last_bid)) {
(slice old_bidder_address, int old_bid, _) = unpack_last_bid(old_last_bid);
int to_send = min(my_balance - min_tons_for_storage, old_bid);
if (to_send > 0) {
send_msg(old_bidder_address, to_send, op::outbid_notification, cur_lt(), null(), 1);
my_balance -= to_send;
}
}
cell new_auction_state = pack_auction_state(new_last_bid, new_min_bid, new_end_time);
return (my_balance, pack_auction(new_auction_state, auction_config));
}
cell prepare_auction(cell auction_config) {
(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);
;; check beneficiary address
parse_std_addr(beneficiary_address);
if ((initial_min_bid < 2 * min_tons_for_storage) | ((max_bid != 0) & (max_bid < initial_min_bid)) |
(min_bid_step <= 0) | (min_extend_time > 60 * 60 * 24 * 7) | (duration > 60 * 60 * 24 * 365)) {
return null();
}
cell auction_state = pack_auction_state(null(), initial_min_bid, now() + duration);
return pack_auction(auction_state, auction_config);
}
cell deploy_item(int my_balance, slice msg) {
;; Do not throw errors here!
(slice bidder_address, int bid, cell token_info, cell nft_content, cell auction_config, cell royalty_params) = unpack_teleitem_msg_deploy(msg);
cell auction = prepare_auction(auction_config);
if (cell_null?(auction)) {
return null();
}
(my_balance, cell new_auction) = process_new_bid(my_balance, bidder_address, bid, auction);
(my_balance, slice owner, new_auction) = maybe_end_auction(my_balance, zero_address(), new_auction, royalty_params, 0);
cell content = pack_item_content(nft_content, null(), token_info);
return pack_item_state(owner, content, new_auction, royalty_params);
}
slice transfer_ownership(int my_balance, slice owner_address, slice in_msg_body, int fwd_fees) impure inline {
(int query_id, slice new_owner_address, slice response_destination, cell custom_payload, int forward_amount, slice forward_payload)
= unpack_nft_cmd_transfer(in_msg_body);
force_chain(new_owner_address);
int rest_amount = my_balance - min_tons_for_storage;
if (forward_amount) {
rest_amount -= (forward_amount + fwd_fees);
}
int need_response = response_destination.preload_uint(2) != 0; ;; if NOT addr_none: 00
if (need_response) {
rest_amount -= fwd_fees;
}
throw_unless(err::not_enough_funds, rest_amount >= 0); ;; base nft spends fixed amount of gas, will not check for response
if (forward_amount) {
send_msg(new_owner_address, forward_amount, op::ownership_assigned, query_id,
begin_cell().store_slice(owner_address).store_slice(forward_payload), 1); ;; paying fees, revert on errors
}
if (need_response) {
force_chain(response_destination);
send_msg(response_destination, rest_amount, op::excesses, query_id, null(), 1); ;; paying fees, revert on errors
}
return new_owner_address;
}
cell change_dns_record(cell dns, slice in_msg_body) {
int key = in_msg_body~load_uint(256);
int has_value = in_msg_body.slice_refs() > 0;
if (has_value) {
cell value = in_msg_body~load_ref();
dns~udict_set_ref(256, key, value);
} else {
dns~udict_delete?(256, key);
}
return dns;
}
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
int my_balance = pair_first(get_balance());
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) { ;; ignore all bounced messages
return ();
}
slice sender_address = cs~load_msg_addr();
cs~load_msg_addr(); ;; skip dst
cs~load_grams(); ;; skip value
cs~load_maybe_ref(); ;; skip extracurrency collection
cs~load_grams(); ;; skip ihr_fee
int fwd_fee = muldiv(cs~load_grams(), 3, 2); ;; we use message fwd_:fee for estimation of forward_payload costs
int op = in_msg_body.slice_empty?() ? 0 : in_msg_body~load_uint(32);
(cell config, cell state) = unpack_item_data();
(int index, slice collection_address) = unpack_item_config(config);
if (equal_slices(collection_address, sender_address)) {
throw_unless(err::forbidden_not_deploy, op == op::teleitem_msg_deploy);
if (cell_null?(state)) {
cell new_state = deploy_item(my_balance, in_msg_body);
ifnot (cell_null?(new_state)) {
return save_item_data(config, new_state);
}
}
slice bidder_address = in_msg_body~load_msg_addr(); ;; first field in teleitem_msg_deploy
send_msg(bidder_address, 0, op::teleitem_return_bid, cur_lt(), null(), 64); ;; carry all the remaining value of the inbound message
return ();
}
throw_if(err::uninited, cell_null?(state));
(slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
if (op == op::get_royalty_params) {
int query_id = in_msg_body~load_uint(64);
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
return ();
}
if (op == op::nft_cmd_get_static_data) {
int query_id = in_msg_body~load_uint(64);
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
return ();
}
int is_topup = (op == 0) & equal_slices(in_msg_body, "#topup") & (in_msg_body.slice_refs() == 0);
if (is_topup) {
return ();
}
ifnot (cell_null?(auction)) {
;; sender do not pay for auction with its message
my_balance -= msg_value;
(my_balance, owner_address, auction) = maybe_end_auction(my_balance, owner_address, auction, royalty_params, 0);
if (cell_null?(auction)) {
cell new_state = pack_item_state(owner_address, content, auction, royalty_params);
save_item_data(config, new_state);
}
my_balance += msg_value;
}
if (op == op::teleitem_cancel_auction) {
throw_if(err::no_auction, cell_null?(auction));
throw_unless(err::forbidden_auction, equal_slices(sender_address, owner_address));
int query_id = in_msg_body~load_uint(64);
(cell auction_state, cell auction_config) = unpack_auction(auction);
(cell last_bid, int min_bid, int end_time) = unpack_auction_state(auction_state);
throw_unless(err::already_has_stakes, cell_null?(last_bid));
cell new_state = pack_item_state(owner_address, content, null(), royalty_params);
if (query_id) {
send_msg(sender_address, 0, op::teleitem_ok, query_id, null(), 64); ;; carry all the remaining value of the inbound message
}
return save_item_data(config, new_state);
}
ifnot (cell_null?(auction)) {
throw_unless(err::forbidden_not_stake, op == 0);
(my_balance, auction) = process_new_bid(my_balance, sender_address, msg_value, auction);
(my_balance, owner_address, auction) = maybe_end_auction(my_balance, owner_address, auction, royalty_params, 0);
cell new_state = pack_item_state(owner_address, content, auction, royalty_params);
return save_item_data(config, new_state);
}
if (op == 0) {
throw_unless(err::forbidden_topup, equal_slices(sender_address, owner_address)); ;; only owner can fill-up balance, prevent coins lost right after the auction
;; if owner send bid right after auction he can restore it by transfer response message
return ();
}
if (op == op::teleitem_start_auction) {
throw_unless(err::forbidden_auction, equal_slices(sender_address, owner_address));
int query_id = in_msg_body~load_uint(64);
cell new_auction_config = in_msg_body~load_ref();
cell new_auction = prepare_auction(new_auction_config);
throw_if(err::invalid_auction_config, cell_null?(new_auction));
cell new_state = pack_item_state(owner_address, content, new_auction, royalty_params);
if (query_id) {
send_msg(sender_address, 0, op::teleitem_ok, query_id, null(), 64); ;; carry all the remaining value of the inbound message
}
return save_item_data(config, new_state);
}
if (op == op::nft_cmd_transfer) {
throw_unless(err::forbidden_transfer, equal_slices(sender_address, owner_address));
slice new_owner_address = transfer_ownership(my_balance, owner_address, in_msg_body, fwd_fee);
cell new_state = pack_item_state(new_owner_address, content, auction, royalty_params);
return save_item_data(config, new_state);
}
if (op == op::change_dns_record) { ;; change dns record
int query_id = in_msg_body~load_uint(64);
throw_unless(err::forbidden_change_dns, equal_slices(sender_address, owner_address));
(cell nft_content, cell dns, cell token_info) = unpack_item_content(content);
cell new_dns = change_dns_record(dns, in_msg_body);
cell new_content = pack_item_content(nft_content, new_dns, token_info);
cell new_state = pack_item_state(owner_address, new_content, auction, royalty_params);
send_msg(sender_address, 0, op::teleitem_ok, query_id, null(), 64); ;; carry all the remaining value of the inbound message
return save_item_data(config, new_state);
}
throw(err::unknown_op);
}
() recv_external(slice in_msg) impure {
int my_balance = pair_first(get_balance());
(cell config, cell state) = unpack_item_data();
(slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
(my_balance, owner_address, auction) = maybe_end_auction(my_balance, owner_address, auction, royalty_params, -1);
cell new_state = pack_item_state(owner_address, content, auction, royalty_params);
return save_item_data(config, new_state);
}
;;
;; GET Methods
;;
(int, int, slice, slice, cell) get_nft_data() method_id {
(cell config, cell state) = unpack_item_data();
(int item_index, slice collection_address) = unpack_item_config(config);
if (cell_null?(state)) {
return (0, item_index, collection_address, zero_address(), null());
}
(slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
(cell nft_content, cell dns, cell token_info) = unpack_item_content(content);
return (-1, item_index, collection_address, owner_address, nft_content);
}
slice get_full_domain() method_id {
(cell config, cell state) = unpack_item_data();
(slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
(cell nft_content, cell dns, cell token_info) = unpack_item_content(content);
(slice token_name, slice domain) = unpack_token_info(token_info);
return begin_cell().store_slice(domain).store_slice(token_name).store_int(0, 8).end_cell().begin_parse();
}
slice get_telemint_token_name() method_id {
(cell config, cell state) = unpack_item_data();
(slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
(cell nft_content, cell dns, cell token_info) = unpack_item_content(content);
(slice token_name, slice domain) = unpack_token_info(token_info);
return token_name;
}
(slice, int, int, int, int) get_telemint_auction_state() method_id {
(cell config, cell state) = unpack_item_data();
(slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
throw_if (err::no_auction, cell_null?(auction));
(cell auction_state, cell auction_config) = unpack_auction(auction);
(cell last_bid, int min_bid, int end_time) = unpack_auction_state(auction_state);
(slice bidder_address, int bid, int bid_ts) = (null(), 0, 0);
ifnot (cell_null?(last_bid)) {
(bidder_address, bid, bid_ts) = unpack_last_bid(last_bid);
}
return (bidder_address, bid, bid_ts, min_bid, end_time);
}
(slice, int, int, int, int, int) get_telemint_auction_config() method_id {
(cell config, cell state) = unpack_item_data();
(slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
if (cell_null?(auction)) {
;; Do not throw error, so it is easy to check if get_telemint_auction_config method exists
return (null(), 0, 0, 0, 0, 0);
}
(cell auction_state, cell auction_config) = unpack_auction(auction);
(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);
return (beneficiary_address, initial_min_bid, max_bid, min_bid_step, min_extend_time, duration);
}
(int, int, slice) royalty_params() method_id {
(cell config, cell state) = unpack_item_data();
(slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
(int numerator, int denominator, slice destination) = unpack_nft_royalty_params(royalty_params);
return (numerator, denominator, destination);
}
(int, cell) dnsresolve(slice subdomain, int category) method_id {
(cell config, cell state) = unpack_item_data();
(slice owner_address, cell content, cell auction, cell royalty_params) = unpack_item_state(state);
(cell nft_content, cell dns, cell token_info) = unpack_item_content(content);
int subdomain_bits = slice_bits(subdomain);
throw_unless(err::bad_subdomain_length, subdomain_bits % 8 == 0);
int starts_with_zero_byte = subdomain.preload_int(8) == 0;
throw_unless(err::no_first_zero_byte, starts_with_zero_byte);
if (subdomain_bits > 8) { ;; more than "." requested
category = "dns_next_resolver"H;
}
if (category == 0) { ;; all categories are requested
return (8, dns);
}
(cell value, int found) = dns.udict_get_ref?(256, category);
return (8, value);
}