ton-core/tact

Does the code size of FunC file will cause different Gas Cost when importing in Tact?

Closed this issue · 1 comments

For example, here is the code for the full NFT Collection followed by TEP62

;; NFT collection smart contract

;; storage scheme
;; default#_ royalty_factor:uint16 royalty_base:uint16 royalty_address:MsgAddress = RoyaltyParams;
;; storage#_ owner_address:MsgAddress next_item_index:uint64
;;           ^[collection_content:^Cell common_content:^Cell]
;;           nft_item_code:^Cell
;;           royalty_params:^RoyaltyParams
;;           = Storage;

(slice, int, cell, cell, cell) load_data() inline {
  var ds = get_data().begin_parse();
  return 
    (ds~load_msg_addr(), ;; owner_address
     ds~load_uint(64), ;; next_item_index
     ds~load_ref(), ;; content
     ds~load_ref(), ;; nft_item_code
     ds~load_ref()  ;; royalty_params
     );
}

() save_data(slice owner_address, int next_item_index, cell content, cell nft_item_code, cell royalty_params) impure inline {
  set_data(begin_cell()
    .store_slice(owner_address)
    .store_uint(next_item_index, 64)
    .store_ref(content)
    .store_ref(nft_item_code)
    .store_ref(royalty_params)
    .end_cell());
}

cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {
  cell data = begin_cell().store_uint(item_index, 64).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();
}

slice calculate_nft_item_address(int wc, cell state_init) {
  return begin_cell().store_uint(4, 3)
                     .store_int(wc, 8)
                     .store_uint(cell_hash(state_init), 256)
                     .end_cell()
                     .begin_parse();
}

() deploy_nft_item(int item_index, cell nft_item_code, int amount, 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(amount)
            .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(), 1); ;; pay transfer fees separately, revert on errors
}

() send_royalty_params(slice to_address, int query_id, slice data) impure inline {
  var msg = begin_cell()
    .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
    .store_slice(to_address)
    .store_coins(0)
    .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
    .store_uint(op::report_royalty_params(), 32)
    .store_uint(query_id, 64)
    .store_slice(data);
  send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message
}

() recv_internal(cell in_msg_full, slice in_msg_body) impure {
    if (in_msg_body.slice_empty?()) { ;; ignore empty messages
        return ();
    }
    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();

    int op = in_msg_body~load_uint(32);
    int query_id = in_msg_body~load_uint(64);

    var (owner_address, next_item_index, content, nft_item_code, royalty_params) = load_data();

    if (op == op::get_royalty_params()) {
        send_royalty_params(sender_address, query_id, royalty_params.begin_parse());
        return ();
    }

    throw_unless(401, equal_slices(sender_address, owner_address));
    
  
    if (op == 1) { ;; deploy new nft
      int item_index = in_msg_body~load_uint(64);
      throw_unless(402, item_index <= next_item_index);
      var is_last = item_index == next_item_index;
      deploy_nft_item(item_index, nft_item_code, in_msg_body~load_coins(), in_msg_body~load_ref());
      if (is_last) {
        next_item_index += 1;
        save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);
      }
      return ();
    }
    if (op == 2) { ;; batch deploy of new nfts
      int counter = 0;
      cell deploy_list = in_msg_body~load_ref();
      do {
        var (item_index, item, f?) = deploy_list~udict::delete_get_min(64);
        if (f?) {
          counter += 1;
          if (counter >= 250) { ;; Limit due to limits of action list size
            throw(399);
          }

          throw_unless(403 + counter, item_index <= next_item_index);
          deploy_nft_item(item_index, nft_item_code, item~load_coins(), item~load_ref());
          if (item_index == next_item_index) {
            next_item_index += 1;
          }
        }
      } until ( ~ f?);
      save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);
      return ();
    }
    if (op == 3) { ;; change owner
      slice new_owner = in_msg_body~load_msg_addr();
      save_data(new_owner, next_item_index, content, nft_item_code, royalty_params);
      return ();
    }
    throw(0xffff);
}

;; Get methods

(int, cell, slice) get_collection_data() method_id {
  var (owner_address, next_item_index, content, _, _) = load_data();
  slice cs = content.begin_parse();
  return (next_item_index, cs~load_ref(), owner_address);
}

slice get_nft_address_by_index(int index) method_id {
    var (_, _, _, nft_item_code, _) = load_data();
    cell state_init = calculate_nft_item_state_init(index, nft_item_code);
    return calculate_nft_item_address(workchain(), state_init);
}

(int, int, slice) royalty_params() method_id {
     var (_, _, _, _, royalty) = load_data();
     slice rs = royalty.begin_parse();
     return (rs~load_uint(16), rs~load_uint(16), rs~load_msg_addr());
}

cell get_nft_content(int index, cell individual_nft_content) method_id {
  var (_, _, content, _, _) = load_data();
  slice cs = content.begin_parse();
  cs~load_ref();
  slice common_content = cs~load_ref().begin_parse();
  return (begin_cell()
                      .store_uint(1, 8) ;; offchain tag
                      .store_slice(common_content)
                      .store_ref(individual_nft_content)
          .end_cell());
}
  • What should we do to implement the receive functions? Like the full recv_internal()
  • What should we do to import the Get Method like get_nft_content?

Importing func is for small hacks that doesn't possible right now in Tact, not full sources of func contracts. It would generate conflicting code (like recv_internal) and would just crash.

Size of a code is not that important in our tests, we tweaked Tact to be gas-optimized and it turns out better than space-optimized.