Browse Source

Commenting code + changed the storage schema a bit

dev/signature-instant-buy
Lev 2 years ago
parent
commit
38c05c2e7c
  1. 4
      contracts/imports/op-codes.fc
  2. 104
      contracts/main.fc

4
contracts/imports/op-codes.fc

@ -1,5 +1,9 @@
int op::transfer() asm "0x5fcc3d14 PUSHINT"; int op::transfer() asm "0x5fcc3d14 PUSHINT";
;; The operator for message to the owner after transfer
int op::ownership_assigned() asm "0x05138d91 PUSHINT"; int op::ownership_assigned() asm "0x05138d91 PUSHINT";
;; The op for the response message to the owner after transfer
int op::excesses() asm "0xd53276db PUSHINT"; int op::excesses() asm "0xd53276db PUSHINT";
int op::get_static_data() asm "0x2fcb26a2 PUSHINT"; int op::get_static_data() asm "0x2fcb26a2 PUSHINT";
int op::report_static_data() asm "0x8b771735 PUSHINT"; int op::report_static_data() asm "0x8b771735 PUSHINT";

104
contracts/main.fc

@ -10,11 +10,27 @@
int min_tons_for_storage() asm "1000000000 PUSHINT"; ;; 1 TON int min_tons_for_storage() asm "1000000000 PUSHINT"; ;; 1 TON
;; =============== storage =============================
;;
;; Storage
;;
;; cell nft_item_code
;; uint256 index --- The index of this item in the collection
;; MsgAddressInt collection_address
;; MsgAddressInt owner_address
;; cell content --- The key-value map with the content both as item and a collection)
;; cell domain --- e.g contains "alice" (without ending \0) for "alice.ton" domain
;; cell auction --- auction info: (address max_bid_address, coins max_bid, uint32 end_time)
;; int last_fill_up_time
;; todo: Move auction_start_time, auction_start_duration, auction_end_duration, auction_prolongation, price multiplicator to the auction data (also, make it updateable)
;; todo: Also, add functionality to buy a domain instantly (without auction) - and the corresponding settings
const auction_start_duration = 604800; ;; 1 week = 60 * 60 * 24 * 7; in testnet 5 min const auction_start_duration = 604800; ;; 1 week = 60 * 60 * 24 * 7; in testnet 5 min
const auction_end_duration = 3600; ;; 1 hour = 60 * 60; in testnet 1 min const auction_end_duration = 3600; ;; 1 hour = 60 * 60; in testnet 1 min
const auction_prolongation = 3600; ;; 1 hour = 60 * 60; in testnet 1 min const auction_prolongation = 3600; ;; 1 hour = 60 * 60; in testnet 1 min
;; Parse the auction data
;; MsgAddressInt max_bid_address ;; MsgAddressInt max_bid_address
;; Coins max_bid_amount ;; Coins max_bid_amount
;; int auction_end_time ;; int auction_end_time
@ -27,6 +43,7 @@ const auction_prolongation = 3600; ;; 1 hour = 60 * 60; in testnet 1 min
} }
} }
;; Serialize the auction data
cell pack_auction(slice max_bid_address, int max_bid_amount, int auction_end_time) { cell pack_auction(slice max_bid_address, int max_bid_amount, int auction_end_time) {
return begin_cell() return begin_cell()
.store_slice(max_bid_address) .store_slice(max_bid_address)
@ -35,37 +52,35 @@ cell pack_auction(slice max_bid_address, int max_bid_amount, int auction_end_tim
.end_cell(); .end_cell();
} }
;; =============== storage =============================
;;
;; Storage
;;
;; content:^Cell
;; nft_item_code:^Cell
;; uint256 index
;; MsgAddressInt collection_address
;; MsgAddressInt owner_address
;; cell domain - e.g contains "alice" (without ending \0) for "alice.ton" domain
;; cell auction - auction info
;; int last_fill_up_time
;; cell content, cell nft_item_code, uint256 index, address collection_address,
;; address owner_address, cell domain, cell auction, int last_fill_up_time
(cell, cell, int, int, slice, slice, cell, cell, int) load_data() { (cell, cell, int, int, slice, slice, cell, cell, int) load_data() {
slice ds = get_data().begin_parse(); slice ds = get_data().begin_parse();
cell content = ds~load_ref(); ;; content
cell code = ds~load_ref(); ;; code cell code = ds~load_ref(); ;; code
var (index, collection_address) = (ds~load_uint(256), ds~load_msg_addr()); var (index, collection_address) = (ds~load_uint(256), ds~load_msg_addr());
if (ds.slice_bits() > 0) { if (ds.slice_bits() > 0) {
return (content, code, -1, index, collection_address, ds~load_msg_addr(), ds~load_ref(), ds~load_dict(), ds~load_uint(64)); slice owner = ds~load_msg_addr();
cell content = ds~load_ref(); ;; content
return (content, code, -1, index, collection_address, owner, ds~load_ref(), ds~load_dict(), ds~load_uint(64));
} else { } else {
return (content, code, 0, index, collection_address, null(), null(), null(), 0); ;; nft not initialized yet return (null(), code, 0, index, collection_address, null(), null(), null(), 0); ;; nft not initialized yet
} }
} }
;; The initial state of a new NFT item with a given index and code
;; Includes the code, the index, and the collection address - everything else should be initialized by the item itself
cell calculate_nft_item_state_init(int item_index, cell nft_item_code) { cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {
cell data = begin_cell().store_uint(item_index, 256).store_slice(my_address()).end_cell(); cell data = begin_cell()
.store_ref(nft_item_code)
.store_uint(item_index, 256)
.store_slice(my_address())
.end_cell();
return begin_cell().store_uint(0, 2).store_dict(nft_item_code).store_dict(data).store_uint(0, 1).end_cell(); return begin_cell().store_uint(0, 2).store_dict(nft_item_code).store_dict(data).store_uint(0, 1).end_cell();
} }
;; As noted in https://ton.org/tblkch.pdf part 1.7.3, the nft address should be calculated using the hash of the state
;; This function generates the address for the new NFT
slice calculate_nft_item_address(int wc, cell state_init) { slice calculate_nft_item_address(int wc, cell state_init) {
return begin_cell() return begin_cell()
.store_uint(4, 3) .store_uint(4, 3)
@ -75,14 +90,15 @@ slice calculate_nft_item_address(int wc, cell state_init) {
.begin_parse(); .begin_parse();
} }
() store_data(cell collection_content, cell nft_item_code, int index, slice collection_address, slice owner_address, cell domain, cell auction, int last_fill_up_time) impure { ;; Serialize the data using the storage schema
() store_data(cell content, cell nft_item_code, int index, slice collection_address, slice owner_address, cell domain, cell auction, int last_fill_up_time) impure {
set_data( set_data(
begin_cell() begin_cell()
.store_ref(collection_content)
.store_ref(nft_item_code) .store_ref(nft_item_code)
.store_uint(index, 256) .store_uint(index, 256)
.store_slice(collection_address) .store_slice(collection_address)
.store_slice(owner_address) .store_slice(owner_address)
.store_ref(content)
.store_ref(domain) .store_ref(domain)
.store_dict(auction) .store_dict(auction)
.store_uint(last_fill_up_time, 64) .store_uint(last_fill_up_time, 64)
@ -106,35 +122,68 @@ slice calculate_nft_item_address(int wc, cell state_init) {
send_raw_message(msg.end_cell(), send_mode); send_raw_message(msg.end_cell(), send_mode);
} }
() transfer_ownership(int my_balance, cell collection_content, cell nft_item_code, int index, slice collection_address, slice owner_address, slice sender_address, int query_id, slice in_msg_body, int fwd_fees, cell domain, cell auction) impure inline { ;; Transfer the ownership of the item
;; in_msg_body schema:
;; address new_owner
;; address response_destination (can be 0 if no response needed)
;; coins to forward + coins to pay the fees
() transfer_ownership(int my_balance, cell collection_content, cell nft_item_code, int index,
slice collection_address, slice owner_address, slice sender_address, int query_id, slice in_msg_body,
int fwd_fees, cell domain, cell auction) impure inline {
slice new_owner_address = in_msg_body~load_msg_addr(); slice new_owner_address = in_msg_body~load_msg_addr();
force_chain(new_owner_address); force_chain(new_owner_address);
slice response_destination = in_msg_body~load_msg_addr(); slice response_destination = in_msg_body~load_msg_addr();
in_msg_body~load_int(1); ;; this nft don't use custom_payload in_msg_body~load_int(1); ;; this nft don't use custom_payload
int forward_amount = in_msg_body~load_coins(); int forward_amount = in_msg_body~load_coins();
;; The balance needs to be kept above `min_tons_for_storage()` (1 TON)
int rest_amount = my_balance - min_tons_for_storage(); int rest_amount = my_balance - min_tons_for_storage();
if (forward_amount) { if (forward_amount) {
;; The forward_amount has been already added to the balance,
;; but we will have to give it back, so we need to subtract it as well as the message commission
rest_amount -= (forward_amount + fwd_fees); rest_amount -= (forward_amount + fwd_fees);
} }
;; If the address starts with 00, it means that the response is not needed
int need_response = response_destination.preload_uint(2) != 0; ;; if NOT addr_none: 00 int need_response = response_destination.preload_uint(2) != 0; ;; if NOT addr_none: 00
if (need_response) { if (need_response) {
;; The response is needed, so we consider the commission for the outbound message
rest_amount -= fwd_fees; rest_amount -= fwd_fees;
} }
throw_unless(402, rest_amount >= 0); ;; base nft spends fixed amount of gas, will not check for response throw_unless(402, rest_amount >= 0); ;; base nft spends fixed amount of gas, will not check for response
if (forward_amount) { if (forward_amount) {
send_msg(new_owner_address, forward_amount, op::ownership_assigned(), query_id, begin_cell().store_slice(owner_address).store_slice(in_msg_body), 1); ;; paying fees, revert on errors send_msg(
new_owner_address, forward_amount,
op::ownership_assigned(), query_id,
begin_cell().store_slice(owner_address).store_slice(in_msg_body),
1); ;; paying fees, revert on errors
} }
if (need_response) { if (need_response) {
force_chain(response_destination); force_chain(response_destination);
;; Send the response, which includes the balance of the NFT item except for 1 TON
;; Basically, it means that the person transfers the NFT to someone else, but keeps the balance
send_msg(response_destination, rest_amount, op::excesses(), query_id, null(), 1); ;; paying fees, revert on errors send_msg(response_destination, rest_amount, op::excesses(), query_id, null(), 1); ;; paying fees, revert on errors
} }
;; Update the owner address and the fill-up time
store_data(collection_content, nft_item_code, index, collection_address, new_owner_address, domain, auction, now()); store_data(collection_content, nft_item_code, index, collection_address, new_owner_address, domain, auction, now());
} }
;; Create a new item
() deploy_nft_item(int item_index, cell nft_item_code, cell nft_content) impure {
cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);
slice nft_address = calculate_nft_item_address(workchain(), state_init);
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(nft_address)
.store_coins(0)
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
.store_ref(state_init)
.store_ref(nft_content);
send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message, fee deducted from amount
}
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure { () recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) { ;; bounce back empty messages if (in_msg_body.slice_empty?()) { ;; bounce back empty messages
throw(0xffff); throw(0xffff);
@ -148,8 +197,16 @@ slice calculate_nft_item_address(int wc, cell state_init) {
return (); return ();
} }
slice sender_address = cs~load_msg_addr(); slice sender_address = cs~load_msg_addr();
;; Un-serialize all the data
(cell content, cell item_code, int init?, int index, slice collection_address, slice owner_address, cell domain, cell auction, int last_fill_up_time) = load_data(); (cell content, cell item_code, int init?, int index, slice collection_address, slice owner_address, cell domain, cell auction, int last_fill_up_time) = load_data();
;; This means that we are initializing the new contract
;; The initialization message has this structure:
;; address initial_owner_address (for an auction)
;; slice domain (the domain for the nft)
;; todo: for instant deploys of subdomains or buying them, this structure should be changed
;; todo: we should also be able to pass additional content here
if (~ init?) { if (~ init?) {
;; The initialization message has to come from the collection
throw_unless(405, equal_slices(collection_address, sender_address)); throw_unless(405, equal_slices(collection_address, sender_address));
slice from_address = in_msg_body~load_msg_addr(); slice from_address = in_msg_body~load_msg_addr();
cell domain = in_msg_body~load_ref(); cell domain = in_msg_body~load_ref();
@ -161,13 +218,16 @@ slice calculate_nft_item_address(int wc, cell state_init) {
if (months > 12) { if (months > 12) {
months = 12; months = 12;
} }
;; The auction duration becomes shorter over time
int duration = auction_start_duration - (auction_start_duration - auction_end_duration) * months / 12; int duration = auction_start_duration - (auction_start_duration - auction_end_duration) * months / 12;
int auction_end_time = now() + duration; int auction_end_time = now() + duration;
;; We initialize the contract with the auction and content
store_data(content, item_code, index, collection_address, zero_address(), domain, pack_auction(from_address, msg_value, auction_end_time), now()); store_data(content, item_code, index, collection_address, zero_address(), domain, pack_auction(from_address, msg_value, auction_end_time), now());
return (); return ();
} }
if (init? & equal_slices(collection_address, sender_address)) { ;; todo: add comments ;; If the item is initialized and the message comes from the collection
if (init? & equal_slices(collection_address, sender_address)) { ;; todo: add comments here
slice from_address = in_msg_body~load_msg_addr(); slice from_address = in_msg_body~load_msg_addr();
send_msg(from_address, 0, 0, cur_lt(), null(), 64); ;; carry all the remaining value of the inbound message send_msg(from_address, 0, 0, cur_lt(), null(), 64); ;; carry all the remaining value of the inbound message
return (); return ();

Loading…
Cancel
Save