FunC Journey: Part 2
Greetings reader, this is the second part of the journey through the TON ecosystem. In this part, we will continue to study different aspects of FunC.
Current state of affairs
In the previous part, we got acquainted with smart contracts in the TON network. We figured out why messages are needed, why the c4 register is needed, and also talked about tests. Now let's talk about things that will allow you to create complex smart contracts.
OP and fees
As we continue our journey through the TON ecosystem, we will learn that there are some rules that are recommended for smart contracts to interact with.
Smart contracts in TON interact with each other by sending the so-called internal messages. For the convenience of
interaction between smart contracts, recommendations were developed. The key to these recommendations is adding
the query_id
and op
to the beginning of the message:
op
identifies the operation to be performed, or the method of the smart contract to be invoked.query_id
used in all query-response internal messages to indicate that a response is related to a query (in case we want to send response).
This is how it looks:
() recv_internal (int balance, int msg_value, cell in_msg_full, slice in_msg_body) {
int op = in_msg_body~load_int(32);
int query_id = in_msg_body~load_uint(64);
if (op == 1) {
;;
} else {
if (op == 2) {
;;
} else {
;; there might be an exception here
}
}
}
The longer you travel through TON, the more you learn about interaction with the environment. The account manager on your team says he knows how to make your smart contracts more efficient.
The TON network has its own currency - Toncoin. Thanks to the Toncoin cryptocurrency, there is a market for computing. Such a marketplace provides an economic incentive for participants to validate and fulfill transactional requests and provide computing resources to the network. Any participant broadcasting a transaction request must pay fees in Toncoin. Smart contracts also use Toncoin, so you need to think about optimizing smart contracts in terms of fees.
In TON the transaction fee consists of:
storage_fees
- fees for a "place" in the blockchain.in_fwd_fees
- fees for importing messages (this is the case when we process external messages).computation_fees
- fees for executing TVM instructions.action_fees
- fees associated with processing a list of actions (for example, sending messages).out_fwd_fees
- fees for importing outgoing messages.
One simple technique to reduce fees is to use the inline
keyword for functions that are called once or twice, so that
when compiled they will simply be substituted into the function call site.
This is how it looks, if you need to load data, for example, only once:
(slice, slice) load_data () inline {
var ds = get_data().begin_parse();
return (ds~load_msg_addr(), ds~load_msg_addr());
}
Lesson about op
.
Testing Op
Being already an experienced TON traveler you know that everything has to be tested....
To test the work with op, we will need to understand what action the contract performed. Register c5 will help us with this.
c5 - Contains the output actions. This value is a Cell.
Outgoing messages from smart-contracts are written to register c5. Therefore, in the tests, we will send messages from address to address and parse the c5 register to make sure everything is correct.
Example of register c5 subtraction:
(int, cell) extract_single_message(cell actions) impure inline method_id {
;; ---------------- Parse actions list
;; prev:^(OutList n)
;; #0ec3c86d
;; mode:(## 8)
;; out_msg:^(MessageRelaxed Any)
;; = OutList (n + 1);
slice cs = actions.begin_parse();
throw_unless(1010, cs.slice_refs() == 2);
cell prev_actions = cs~load_ref();
throw_unless(1011, prev_actions.cell_empty?());
int action_type = cs~load_uint(32);
throw_unless(1013, action_type == 0x0ec3c86d);
int msg_mode = cs~load_uint(8);
throw_unless(1015, msg_mode == 64);
cell msg = cs~load_ref();
throw_unless(1017, cs.slice_empty?());
return (msg_mode, msg);
}
Lesson about testing op
.
HashMap
With the increase in the complexity of smart contracts, the question arises of how data can be stored and processed, for example, with respect to time. This opportunity is provided by hashmaps or as they are also called dictionaries.
Hashmap is a data structure represented by a tree. Hashmap - maps keys to values of arbitrary type, so that quick lookup and modification is possible. More details in clause 3.3. In FunC, hashmaps are represented by a cell.
For the convenience of working with hashmaps, the FunС standard library has many functions. For example, if you want to
add data to hashmap, you can use dict_set
, which sets the value associated with the key index key n bit depth in the
dict dictionary to a slice and returns the resulting dictionary.
dic~udict_set(256, key, in_msg_body);
To work with hashmaps, we need loops:
Loops are often used to work with hashmaps. FunC has
three loops: repeat
,until
,while
.
The most convenient hashmap loop is until
, since many functions for working with hashmaps return a flag by which it is
convenient to check the loop termination condition.
Lesson about hashmaps.
Testing hashmaps
When we test contracts that use some kind of time logic, you need to use the c7 register. Therefore, in tests for a smart contract, we will need to write a helper function that will allow us to work with time.
c7 — Contains the root of temporary data. It is a Tuple.
The helper function will allow us to put any time we need into the register.The helper function would look like this:
tuple get_c7_now(int now) inline method_id {
return unsafe_tuple([unsafe_tuple([
0x076ef1ea, ;; magic
0, ;; actions
0, ;; msgs_sent
now, ;; unixtime
1, ;; block_lt
1, ;; trans_lt
239, ;; randseed
unsafe_tuple([1000000000, null()]), ;; balance_remaining
null(), ;; myself
get_config() ;; global_config
])]);
}
Since the smart contract has the logic of storing values as well as receiving, we will have to test the data received
from previous tests, this cannot be done using the standard FunC library, BUT in toncli this moment is thought out: In
description of toncli tests, there are get_prev_c4
/ get_prev_c5
functions that allow you to get c4
/c5
cells
from previous tests.
To make it convenient for us to work with the tuple from the stack, the standard library has functions:
- first - returns the first element of a tuple.
- second - returns the second element of a tuple.
Lesson about testing hashmaps.
Further path
Congratulations, we have made significant progress on our journey. In the next part, we will talk about token standards and NFTs in TON.
Other parts: