diff --git a/docs/v3/documentation/smart-contracts/func/docs/stdlib.mdx b/docs/v3/documentation/smart-contracts/func/docs/stdlib.mdx index e81ed1af30..977c52df6a 100644 --- a/docs/v3/documentation/smart-contracts/func/docs/stdlib.mdx +++ b/docs/v3/documentation/smart-contracts/func/docs/stdlib.mdx @@ -256,10 +256,10 @@ Returns the internal address of the current smart contract as a Slice with `MsgA [int, cell] get_balance() asm "BALANCE"; ``` -Returns the remaining balance of the smart contract as `tuple` consisting of `int` (the remaining balance in nanotoncoins) and `cell` (a dictionary with 32-bit keys representing the balance of “extra currencies”). Note that RAW primitives such as `send_raw_message` do not update this field. +Returns the remaining balance of the smart contract as `tuple` consisting of `int` (the remaining balance in nanotoncoins) and `cell` (a dictionary with 32-bit keys representing the balance of “extra currencies”). Since this will occur in compute phase, balance of the contract will have incoming message `value` included, `storage_fee` and `import_fee` deducted. -:::tip -Since this will occur in compute phase, balance of the contract will have incoming message `value` included, `storage_fee` and `import_fee` deducted +:::warning +RAW primitives such as `send_raw_message` do not update this field. ::: #### cur_lt diff --git a/docs/v3/documentation/smart-contracts/message-management/message-modes-cookbook.mdx b/docs/v3/documentation/smart-contracts/message-management/message-modes-cookbook.mdx index 87e57b1244..7b01e0bde6 100644 --- a/docs/v3/documentation/smart-contracts/message-management/message-modes-cookbook.mdx +++ b/docs/v3/documentation/smart-contracts/message-management/message-modes-cookbook.mdx @@ -1,62 +1,95 @@ # Message Modes Cookbook -:::danger Caution -Page is under active development -::: - -Understanding the different modes and flags available for sending messages is important to ensure that your smart contracts behave correctly. +Understanding the different modes and flags available for sending messages is important to ensure that your smart contracts behave correctly. While [message modes](/v3/documentation/smart-contracts/message-management/sending-messages#message-modes) section provided detailed descriptions of these modes and flags, in this section we will illustrate their practical application with specific examples. +:::info IMPORTANT +You can check [this example](https://testnet.tonviewer.com/transaction/42ed45726e4fe994b7fd6dbf953a2ac24ecd77753858abeda9d6755c664a537a) as a real-world confirmation. +::: + #### Message value and account balance -There are 2 ways to pay for blockchain action: -* from [message value](/v3/documentation/data-formats/tlb/msg-tlb#commonmsginfo). -* from [contract balance](https://github.com/ton-blockchain/ton/blob/7151ff26279fef6dcfa1f47fc0c5b63677ae2458/crypto/block/block.tlb#L263C1-L265C20) -Typically it's charged from contract `balance`, but in specific cases it will use part of the message `value`. The transaction fees used in these examples are hypothetical and are used for illustrative purposes only, any fees other than message forward are out of scope of this article. +Please check how [get_balance](/v3/documentation/smart-contracts/func/docs/stdlib/#get_balance) works to understand transaction state better. Balance in + +There are 2 ways to pay for blockchain action: + +- From the [message value](/v3/documentation/data-formats/tlb/msg-tlb#commonmsginfo) +- From the [contract balance](https://github.com/ton-blockchain/ton/blob/7151ff26279fef6dcfa1f47fc0c5b63677ae2458/crypto/block/block.tlb#L263C1-L265C20) + +Typically it's charged from contract `balance`, but in specific cases it will use part of the message `value`. + +#### Fees + +Actual transaction fees will vary depending on blockchain configuration, code of the smart contract and other factors. When message is received, part of the contract `balance` will be consumed by [storage fees](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#storage-fee) and gas_fees if message `value` [is above certain amount](/v3/documentation/tvm/tvm-overview#compute-phase-skipped). + +According to [transaction flow](/v3/documentation/tvm/tvm-overview#transactions-and-phases) there are 5 phases: + +- Storage, consisting of [account storage](https://github.com/ton-blockchain/ton/blob/7151ff26279fef6dcfa1f47fc0c5b63677ae2458/crypto/block/transaction.cpp#L651-L675) and [in_msg import](https://github.com/ton-blockchain/ton/blob/7151ff26279fef6dcfa1f47fc0c5b63677ae2458/crypto/block/transaction.cpp#L783-L816) +- [Credit](https://github.com/ton-blockchain/ton/blob/7151ff26279fef6dcfa1f47fc0c5b63677ae2458/crypto/block/transaction.cpp#L959-L981), where in_msg `value` is added to the `balance` +- Compute, where actual smart contract code is executed in TVM +- Action, where actions like `SENDRAWMSG` are performed +- Bounce, where everything about bouncing is handled + +The order is: storage_fee -> import_fee -> gas_fee -> action_fee + fwd_fee :::info IMPORTANT -__Note__: Actual transaction fees will vary depending on blockchain configuration, code of the smart contract and other factors. When message is received, part of it will be consumed for gas and storage fees. Please check how [get_balance](/v3/documentation/smart-contracts/func/docs/stdlib/#get_balance) works to understand transaction state better. You can check [this example](https://testnet.tonviewer.com/transaction/42ed45726e4fe994b7fd6dbf953a2ac24ecd77753858abeda9d6755c664a537a) as a real-world confirmation. +The table is populated from [this example](https://tonviewer.com/transaction/b5e14a9c4a4e982fda42d6079c3f84fa48e76497a8f3fca872f9a3737f1f6262). You can check [**live calculator**](/v3/documentation/smart-contracts/transaction-fees/fees#basic-fees-formula). +::: + +| Fee in Explorer | Value | How it's obtained | +| :------------------------------------------------------------------------------------------------ | :---------- | :------------------------------ | +| Total fee | 0,001982134 | gas + storage + action + import | +| total_fwd_fees | 0,001 | fwd_fee + action_fee + ihr_fee | +| gas_fees | 0,0011976 | compute phase, gas used | +| storage_fees | 0,000000003 | storage phase, account only | +| total_action_fees | 0,000133331 | action phase, cost per action | +| import_fee (hidden) | 0,0006512 | cost of import of ext_msg | +| [fwd_fee](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#formula-1) (each msg) | 0,000266669 | cost of fwd of in_msg | +| [ihr_fee](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#ihr) (each msg) | 0.0006 | cost of ihr fwd of in_msg | + +:::info +The transaction fees used in these examples are hypothetical and are used for illustrative purposes only, any fees other than message forward are out of scope of this article. ::: ## 1. Send a regular message State before transaction: Account A has 1 TON, Account B has 1 TON -__A__ sent 0.1 TON to __B__, [msg_fwd_fees](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#forward-fees) are 0.004 TON, actual received value will be 0.096 TON, `fwd_fee` and `action_fee` deducted from `value`. +**A** sent 0.1 TON to **B**, [msg_fwd_fees](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#forward-fees) are 0.004 TON, actual received value will be 0.096 TON, `fwd_fee` and `action_fee` deducted from `value`. State after transaction: Account A has 0.9 TON, Account B has 1.096 TON ![](/img/docs/message-modes-cookbook/send_regular_message.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :-------------------- | :------------------------- | | `mode` = 0, no `flag` | `send_raw_message(msg, 0)` | ## 2. Send a regular message, no bounce the message on error and ignore it State before transaction: Account A has 1 TON, Account B has 1 TON -__A__ sent 0.1 TON to __B__, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.096 TON, `fwd_fee` and `action_fee` deducted from `value`. +**A** sent 0.1 TON to **B**, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.096 TON, `fwd_fee` and `action_fee` deducted from `value`. In case of an error during transaction processing, the message will not bounce and will be ignored. State after transaction: Account A has 0.9 TON, Account B has 1.096 TON :::info tip -Funds included with an ignored message will still be [credited to the receiving address](https://testnet.tonviewer.com/transaction/8a388731812c80ab9b0ea9531108425488af5def854e4bd6f0ed47362b56d557). +Funds included with an ignored message will still be [credited to the receiving address](https://testnet.tonviewer.com/transaction/8a388731812c80ab9b0ea9531108425488af5def854e4bd6f0ed47362b56d557). If no errors the result is the same as [`mode = 0`](#1-send-a-regular-message). ::: ![](/img/docs/message-modes-cookbook/send_regular_message_and_ignore_errors.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :--------------------- | :------------------------- | | `mode` = 0, `flag` = 2 | `send_raw_message(msg, 2)` | ## 3. Send a regular message and bounce the message on action error State before transaction: Account A has 1 TON, Account B has 1 TON -__A__ sent 0.1 TON to __B__, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.096 TON, `fwd_fee` and `action_fee` deducted from `value`. +**A** sent 0.1 TON to **B**, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.096 TON, `fwd_fee` and `action_fee` deducted from `value`. In case of an error during [action phase](https://retracer.ton.org/?tx=e9dccba82badc0d742f14eff41c203280f380e87180b5314fcfd742856e598f7&testnet=true), the message will bounce and `total_fee` + `fwd_fee` will be deducted from `value`. State after transaction with error: Account A has 1 - ([total_fee](/v3/documentation/smart-contracts/transaction-fees/fees#basic-fees-formula) + `fwd_fee`) TON, Account B has 1 TON @@ -67,30 +100,30 @@ If no errors the result is the same as [`mode = 0`](#1-send-a-regular-message). ![](/img/docs/message-modes-cookbook/send_regular_message_and_bounce_if_error.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :---------------------- | :-------------------------- | | `mode` = 0, `flag` = 16 | `send_raw_message(msg, 16)` | ## 4. Send a regular message with separate fees State before transaction: Account A has 1 TON, Account B has 1 TON -__A__ sent 0.1 TON to __B__, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.1 TON, `fwd_fee` and `action_fee` deducted from `balance`. +**A** sent 0.1 TON to **B**, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.1 TON, `fwd_fee` and `action_fee` deducted from `balance`. State after transaction: Account A has 0.896 TON, Account B has 1.1 TON ![](/img/docs/message-modes-cookbook/send_regular_and_pay_fees_separately.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :--------------------- | :------------------------- | | `mode` = 0, `flag` = 1 | `send_raw_message(msg, 1)` | ## 5. Send a regular message with separate fees and bounce the message on error State before transaction: Account A has 1 TON, Account B has 1 TON -__A__ sent 0.1 TON to __B__, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.1 TON, `fwd_fee` and `action_fee` deducted from `balance`. -In case of an error during [action phase](https://retracer.ton.org/?tx=e9dccba82badc0d742f14eff41c203280f380e87180b5314fcfd742856e598f7&testnet=true), the message will bounce and `total_fee` + `fwd_fee` will be deducted from `value`. +**A** sent 0.1 TON to **B**, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.1 TON, `fwd_fee` and `action_fee` deducted from `balance`. +In case of an error [during action phase](https://retracer.ton.org/?tx=e9dccba82badc0d742f14eff41c203280f380e87180b5314fcfd742856e598f7&testnet=true), the message will bounce and `total_fee` + `fwd_fee` will be deducted from `value`. State after transaction with error: Account A has 1 - ([total_fee](/v3/documentation/smart-contracts/transaction-fees/fees#basic-fees-formula) + `fwd_fee`) TON, Account B has 1 TON @@ -100,33 +133,39 @@ If no errors the result is the same as [`mode = 1`](#4-send-a-regular-message-wi ![](/img/docs/message-modes-cookbook/send_regular_message_pay_fee_separately_bounce_if_error.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :------------------------------- | :-------------------------- | | `mode` = 0, `flag` = 1 + 16 = 17 | `send_raw_message(msg, 17)` | ## 6. Carry remaining value with new message State before transaction: Account A has 1 TON, Account B has 1 TON, Account C has 1 TON -__A__ sent 0.1 TON to __B__ after that __B__ sent 0.5 TON to __C__ with `mode` = 64, `msg_fwd_fees` are 0.004 TON, actual received `value` will be 0.6 TON, total_fee + `fwd_fee` deducted from `value`. +**A** sent 0.1 TON to **B** after that **B** sent 0.5 TON to **C** with `mode` = 64, `msg_fwd_fees` are 0.004 TON, actual received `value` will be 0.6 TON, total_fee + `fwd_fee` deducted from `value`. State after transaction: Account A has 0.896 TON, Account B has 0.5 TON, Account C has 1.6 - (total_fee + `fwd_fee`) TON :::info -You might check [this example](https://retracer.ton.org/?tx=4340b5ecbd83227cc64e10b1ca7628352133cda1d608081fb2ed58d299f00936&testnet=true) +You might check [this example](https://retracer.ton.org/?tx=4340b5ecbd83227cc64e10b1ca7628352133cda1d608081fb2ed58d299f00936&testnet=true). +::: + +:::warning +Please note that `SENDRAWMSG` doesn't update balance. + +If you try to send multiple messages (i.e. mode 0 **and** mode 64) you'll get exit code 37. ::: ![](/img/docs/message-modes-cookbook/carry_all_the_remaining_value.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :--------------------- | :-------------------------- | | `mode` = 64, no `flag` | `send_raw_message(msg, 64)` | ## 7. Carry remaining value with new message with separate fees State before transaction: Account A has 1 TON, Account B has 1 TON, Account C has 1 TON -__A__ sent 0.1 TON to __B__ after that __B__ sent 0.5 TON to __C__ with `mode` = 65, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.6 TON, total_fee + `fwd_fee` deducted from `balance`. +**A** sent 0.1 TON to **B** after that **B** sent 0.5 TON to **C** with `mode` = 65, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.6 TON, total_fee + `fwd_fee` deducted from `balance`. State after transaction: Account A has 0.896 TON, Account B has 0.5 - (total_fee + `fwd_fee`) TON, Account C has 1.6 TON @@ -136,94 +175,92 @@ You might check [this example](https://retracer.ton.org/?tx=5c2525feeb3b93db594b ![](/img/docs/message-modes-cookbook/carry_all_the_remaining_value_and_pay_fees_separately.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :---------------------- | :-------------------------- | | `mode` = 64, `flag` = 1 | `send_raw_message(msg, 65)` | ## 8. Carry remaining value and bounce the message on error State before transaction: Account A has 1 TON, Account B has 1 TON, Account C has 1 TON -__A__ sent 0.1 TON to __B__ after that __B__ sent 0.5 TON to __C__ with `mode` = 80, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.6 TON, total_fee + `fwd_fee` deducted from `value`. +**A** sent 0.1 TON to **B** after that **B** sent 0.5 TON to **C** with `mode` = 80, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.6 TON, total_fee + `fwd_fee` deducted from `value`. In case of an error during action phase, the message will bounce and `total_fee` + `fwd_fee` will be deducted from `value`. State after transaction with error: Account A has 1 - (total_fee + `fwd_fee`) TON, Account B has 1 TON, Account C has 1 TON - :::info tip -If no errors the result is the same as [`mode = 64`](#6-carry-remaining-value-with-new-message). +If no errors the result is the same as [`mode = 64`](#6-carry-remaining-value-with-new-message). This mode is actually used a lot in TON for jetton transfers, you can [check it in C5 action list](https://retracer.ton.org/?tx=6489d60d9197c0be7ee64b0812139d82221e8f94c6e378c356acc10f9067310c) of the jetton wallet. ::: ![](/img/docs/message-modes-cookbook/carry_all_the_remaining_value_and_if_error_bounce.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :----------------------- | :-------------------------- | | `mode` = 64, `flag` = 16 | `send_raw_message(msg, 80)` | ## 9. Carry remaining value with new message with separate fees and bounce the message on error State before transaction: Account A has 1 TON, Account B has 1 TON, Account C has 1 TON -__A__ sent 0.1 TON to __B__ after that __B__ sent 0.5 TON to __C__ with `mode` = 80, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.6 TON, total_fee + `fwd_fee` deducted from `balance`. +**A** sent 0.1 TON to **B** after that **B** sent 0.5 TON to **C** with `mode` = 80, `msg_fwd_fees` are 0.004 TON, actual received value will be 0.6 TON, total_fee + `fwd_fee` deducted from `balance`. In case of an error during action phase, the message will bounce and `total_fee` + `fwd_fee` will be deducted from `value`. State after transaction with error: Account A has 1 - (total_fee + `fwd_fee`) TON, Account B has 1 TON, Account C has 1 TON - :::info tip If no errors the result is the same as [`mode = 65`](#7-carry-remaining-value-with-new-message-with-separate-fees). ::: ![](/img/docs/message-modes-cookbook/carry_all_the_remaining_value_and_pay_fees_separately_and_if_error_bounce.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :--------------------------- | :-------------------------- | | `mode` = 64, `flag` = 16 + 1 | `send_raw_message(msg, 81)` | ## 10. Send all received tokens together with the contract balance State before transaction: Account A has 1 TON, Account B has 1 TON, Account C has 1 TON -__A__ sent 0.1 TON to __B__ after that __B__ sent 0.5 TON to __C__ with `mode` = 128, `msg_fwd_fees` are 0.004 TON, actual received value will be 1.1 - total_fee TON, total_fee deducted from `value`. +**A** sent 0.1 TON to **B** after that **B** sent 0.5 TON to **C** with `mode` = 128, `msg_fwd_fees` are 0.004 TON, actual received value will be 1.1 - total_fee TON, total_fee deducted from `value`. -State after transaction: Account A has 0.896 TON, Account B has 0 TON, Account C has ~2.09 TON +State after transaction: Account A has 0.896 TON, Account B has 0 TON, Account C has 2.1 - (total_fee + `fwd_fee`) TON ![](/img/docs/message-modes-cookbook/send_all_received_tokens_with_balance.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :---------------------- | :--------------------------- | | `mode` = 128, no `flag` | `send_raw_message(msg, 128)` | ## 11. Send all received tokens together with the contract balance and bounce the message on error State before transaction: Account A has 1 TON, Account B has 1 TON, Account C has 1 TON -__A__ sent 0.1 TON to __B__ after that __B__ sent 0.5 TON to __C__ with `mode` = 144, `msg_fwd_fees` are 0.004 TON, actual received value will be 1.1 - total_fee TON, total_fee deducted from `value`. +**A** sent 0.1 TON to **B** after that **B** sent 0.5 TON to **C** with `mode` = 144, `msg_fwd_fees` are 0.004 TON, actual received value will be 1.1 - total_fee TON, total_fee deducted from `value`. State after transaction with error: Account A has 1 - (total_fee + `fwd_fee`) TON, Account B has 1 TON, Account C has 1 TON - :::info tip -If no errors the result is the same as [`mode = 128`](#10-send-all-received-tokens-together-with-the-contract-balance). +If no errors the result is the same as [`mode = 128`](#10-send-all-received-tokens-together-with-the-contract-balance). This mode is actually used a lot in TON for jetton transfers, you can [check it in C5 action list](https://retracer.ton.org/?tx=e4f31e37eec74a8cfcecdad9246a6bbf3da20c4edb3635150cb0fa54b9def558) of the jetton wallet. ::: ![](/img/docs/message-modes-cookbook/send_all_received_tokens_with_balance_and_if_error_bounce.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :------------------------ | :--------------------------- | | `mode` = 128, `flag` = 16 | `send_raw_message(msg, 144)` | ## 12. Send all received tokens together with the contract balance and destroy smart-contract State before transaction: Account A has 1 TON, Account B has 1 TON, Account C has 1 TON -__A__ sent 0.1 TON to __B__ after that __B__ sent 0.5 TON to __C__ with `mode` = 160, `msg_fwd_fees` are 0.004 TON, actual received value will be 1.1 - total_fee TON, total_fee deducted from `value`. +**A** sent 0.1 TON to **B** after that **B** sent 0.5 TON to **C** with `mode` = 160, `msg_fwd_fees` are 0.004 TON, actual received value will be 1.1 - total_fee TON, total_fee deducted from `value`. + +State after transaction: Account A has 0.896 TON, Account B has 0 TON and `nonexist`, Account C has 2.1 - (total_fee + `fwd_fee`) TON -State after transaction: Account A has 0.896 TON, Account B has 0 TON and `nonexist`, Account C has ~2.09 TON When the balance reaches 0 TON destroy the contract. ![](/img/docs/message-modes-cookbook/send_all_received_tokens_with_balance_and_destroy_sc.svg) -| Mode and Flags | Code | -|:-|:-| +| Mode and Flags | Code | +| :------------------------ | :--------------------------- | | `mode` = 128, `flag` = 32 | `send_raw_message(msg, 160)` | diff --git a/docs/v3/documentation/smart-contracts/transaction-fees/fees-low-level.md b/docs/v3/documentation/smart-contracts/transaction-fees/fees-low-level.md index 765fe291f4..76190d4254 100644 --- a/docs/v3/documentation/smart-contracts/transaction-fees/fees-low-level.md +++ b/docs/v3/documentation/smart-contracts/transaction-fees/fees-low-level.md @@ -18,15 +18,15 @@ As was described in the [TVM overview](/v3/documentation/tvm/tvm-overview), tran TON validators collect storage fees from smart contracts. -Storage fees are collected from the smart contract balance at the **Storage phase** of any transaction due storage payments for the account state -(including smart-contract code and data, if present) up to the present time. The smart contract may be frozen as a result. +Storage fees are collected from the smart contract `balance` at the **Storage phase** of **any** transaction due to storage payments for the account state +(including smart-contract code and data, if present) up to the present time. Even if [contract received 1 nanoton](https://retracer.ton.org/?tx=1805820dccd7ffd70d6cee6cb581e60ee2f91f7f3eeb20ed00c08dc9fcd6a08b) it will pay all the debt since last payment. The smart contract may be frozen as a result. **Only unique hash cells are counted for storage and forward fees i.e. 3 identical hash cells are counted as one**. In particular, it [deduplicates](/v3/documentation/data-formats/tlb/library-cells) data: if there are several equivalent sub-cells referenced in different branches, their content is only stored once. -It’s important to keep in mind that on TON you pay for both the execution of a smart contract and for the **used storage** (check [@thedailyton article](https://telegra.ph/Commissions-on-TON-07-22)). `storage fee` depends on you contract size: number of cells and sum of number of bits from that cells. **Only unique hash cells are counted for storage and fwd fees i.e. 3 identical hash cells are counted as one**. It means you have to pay a storage fee for having TON Wallet (even if it's very-very small). +It’s important to keep in mind that on TON you pay for both the execution of a smart contract and for the **used storage** (check [@thedailyton article](https://telegra.ph/Commissions-on-TON-07-22)), `storage_fee` depends on your contract size: number of cells and sum of bits from that cells. It means you have to pay a storage fee for having TON Wallet (even if it's very-very small). If you have not used your TON Wallet for a significant period of time (1 year), _you will have to pay a significantly larger commission than usual because the wallet pays commission on sending and receiving transactions_. -:::info -__Note__: When message is bounced from the contract, the contract will pay current debt for storage aka `storage_fee` +:::info **Note**: +When message is bounced from the contract, the contract will pay it's current `storage_fee` ::: ### Formula @@ -34,64 +34,63 @@ __Note__: When message is bounced from the contract, the contract will pay curre You can approximately calculate storage fees for smart contracts using this formula: ```cpp - storage_fee = (cells_count * cell_price + bits_count * bit_price) - * time_delta / 2^16 +storage_fee = ceil( + (account.bits * bit_price + + account.cells * cell_price) + * time_delta / 2 ^ 16) + ``` Let's examine each value more closely: -* `storage_fee`—price for storage for `time_delta` seconds -* `cells_count`—count of cells used by smart contract -* `bits_count`—count of bits used by smart contract -* `cell_price`—price of single cell -* `bit_price`—price of single bit +- `storage_fee` — price for storage for `time_delta` seconds +- `account.cells` — count of cells used by smart contract +- `account.bits` — count of bits used by smart contract +- `cell_price` — price of single cell +- `bit_price` — price of single bit Both `cell_price` and `bit_price` could be obtained from Network Config [param 18](/v3/documentation/network/configs/blockchain-configs#param-18). Current values are: -* Workchain. - ```cpp - bit_price_ps:1 - cell_price_ps:500 - ``` -* Masterchain. - ```cpp - mc_bit_price_ps:1000 - mc_cell_price_ps:500000 - ``` +- Workchain. + ```cpp + bit_price_ps:1 + cell_price_ps:500 + ``` +- Masterchain. + ```cpp + mc_bit_price_ps:1000 + mc_cell_price_ps:500000 + ``` ### Calculator Example You can use this JS script to calculate storage price for 1 MB in the workchain for 1 year ```js live - // Welcome to LIVE editor! // feel free to change any variables // Source code uses RoundUp for the fee amount, so does the calculator function storageFeeCalculator() { - - const size = 1024 * 1024 * 8 // 1MB in bits - const duration = 60 * 60 * 24 * 365 // 1 Year in secs - - const bit_price_ps = 1 - const cell_price_ps = 500 - - const pricePerSec = size * bit_price_ps + - + Math.ceil(size / 1023) * cell_price_ps - - let fee = Math.ceil(pricePerSec * duration / 2**16) * 10**-9 - let mb = (size / 1024 / 1024 / 8).toFixed(2) - let days = Math.floor(duration / (3600 * 24)) - - let str = `Storage Fee: ${fee} TON (${mb} MB for ${days} days)` - - return str -} + const size = 1024 * 1024 * 8; // 1MB in bits + const duration = 60 * 60 * 24 * 365; // 1 Year in secs + const bit_price_ps = 1; + const cell_price_ps = 500; + const pricePerSec = + size * bit_price_ps + +Math.ceil(size / 1023) * cell_price_ps; + + let fee = Math.ceil((pricePerSec * duration) / 2 ** 16) * 10 ** -9; + let mb = (size / 1024 / 1024 / 8).toFixed(2); + let days = Math.floor(duration / (3600 * 24)); + + let str = `Storage Fee: ${fee} TON (${mb} MB for ${days} days)`; + + return str; +} ``` ## Computation fees @@ -111,17 +110,16 @@ number of cell references included in the instruction. Apart from those basic fees, the following fees appear: -| Instruction | GAS price | Description | -|-------------------------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Creation of cell | **500** | Operation of transforming builder to cell. | -| Parsing cell firstly | **100** | Operation of transforming cells into slices first time during current transaction. | -| Parsing cell repeatedly | **25** | Operation of transforming cells into slices, which already has parsed during same transaction. | -| Throwing exception | **50** | | -| Operation with tuple | **1** | This price will multiply by the quantity of tuple's elements. | -| Implicit Jump | **10** | It is paid when all instructions in the current continuation cell are executed. However, there are references in that continuation cell, and the execution flow jumps to the first reference. | -| Implicit Back Jump | **5** | It is paid when all instructions in the current continuation are executed and execution flow jumps back to the continuation from which the just finished continuation was called. | -| Moving stack elements | **1** | Price for moving stack elements between continuations. It will charge correspond gas price for every element. However, the first 32 elements moving is free. | - +| Instruction | GAS price | Description | +| ----------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Creation of cell | **500** | Operation of transforming builder to cell. | +| Parsing cell firstly | **100** | Operation of transforming cells into slices first time during current transaction. | +| Parsing cell repeatedly | **25** | Operation of transforming cells into slices, which already has parsed during same transaction. | +| Throwing exception | **50** | | +| Operation with tuple | **1** | This price will multiply by the quantity of tuple's elements. | +| Implicit Jump | **10** | It is paid when all instructions in the current continuation cell are executed. However, there are references in that continuation cell, and the execution flow jumps to the first reference. | +| Implicit Back Jump | **5** | It is paid when all instructions in the current continuation are executed and execution flow jumps back to the continuation from which the just finished continuation was called. | +| Moving stack elements | **1** | Price for moving stack elements between continuations. It will charge correspond gas price for every element. However, the first 32 elements moving is free. | ### FunC constructions gas fees @@ -198,9 +196,10 @@ By default, when you have a FunC function, it gets its own `id`, stored in a sep ### Dictionaries Dictionaries on TON are introduced as trees (DAGs to be precise) of cells. That means that if you search, read, or write to the dictionary, you need to parse all cells of the corresponding branch of the tree. That means that - * a) dicts operations are not fixed in gas costs (since the size and number of nodes in the branch depend on the given dictionary and key) - * b) it is expedient to optimize dict usage by using special instructions like `replace` instead of `delete` and `add` - * c) developer should be aware of iteration operations (like next and prev) as well `min_key`/`max_key` operations to avoid unnecessary iteration through the whole dict + +- a) dicts operations are not fixed in gas costs (since the size and number of nodes in the branch depend on the given dictionary and key) +- b) it is expedient to optimize dict usage by using special instructions like `replace` instead of `delete` and `add` +- c) developer should be aware of iteration operations (like next and prev) as well `min_key`/`max_key` operations to avoid unnecessary iteration through the whole dict ### Stack operations @@ -215,18 +214,24 @@ will be translated into a few instructions which changes the order of elements o When the number of stack entries is substantial (10+), and they are actively used in different orders, stack operations fees may become non-negligible. - ## Forward fees Internal messages define an `ihr_fee` in Toncoins, which is subtracted from the value attached to the message and awarded to the validators of the destination shardchain if they include the message through the IHR mechanism. The `fwd_fee` is the original total forwarding fee paid for using the HR mechanism; it is automatically computed from the [24 and 25 configuration parameters](/v3/documentation/network/configs/blockchain-configs#param-24-and-25) and the size of the message at the time the message is generated. Note that the total value carried by a newly created internal outbound message equals the sum of the value, `ihr_fee`, and `fwd_fee`. This sum is deducted from the balance of the source account. Of these components, only the `ihr_fee` value is credited to the destination account upon message delivery. The `fwd_fee` is collected by the validators on the HR path from the source to the destination, and the `ihr_fee` is either collected by the validators of the destination shardchain (if the message is delivered via IHR) or credited to the destination account. -:::tip +### IHR +:::tip At this moment (November 2024), [IHR](/v3/documentation/smart-contracts/shards/infinity-sharding-paradigm#messages-and-instant-hypercube-routing-instant-hypercube-routing) is not implemented, and if you set the `ihr_fee` to a non-zero value, it will always be added to the message value upon receipt. For now, there are no practical reasons to do this. - ::: +### Formula + ```cpp +// In_msg and Ext_msg are using the same method of calculation +// It is called import_fee or in_fwd_fee for the Ext_msg +// https://github.com/ton-blockchain/ton/blob/7151ff26279fef6dcfa1f47fc0c5b63677ae2458/crypto/block/transaction.cpp#L2071-L2090 + +// bits in the root cell of a message are not included in msg.bits (lump_price pays for them) msg_fwd_fees = (lump_price + ceil( (bit_price * msg.bits + cell_price * msg.cells) / 2^16) @@ -238,9 +243,9 @@ total_fwd_fees = msg_fwd_fees + ihr_fwd_fees; // ihr_fwd_fees - is 0 for externa ``` :::info IMPORTANT -Please note that `msg_fwd_fees` above includes `action_fee` below. For a basic message this fee = lump_price = 400000 gram, action_fee = (400000 * 21845) / 65536 = 133331. Or approximately a third of the `msg_fwd_fees`. +Please note that `msg_fwd_fees` above includes `action_fee` below. For a basic message this fee = lump_price = 400000 nanotons, action_fee = (400000 \* 21845) / 65536 = 133331. Or approximately a third of the `msg_fwd_fees`. -Hence `fwd_fee` = `msg_fwd_fees` - `action_fee` = 266669 gram = 0,000266669 TON +`fwd_fee` = `msg_fwd_fees` - `action_fee` = 266669 nanotons = 0,000266669 TON ::: ## Action fee @@ -261,6 +266,7 @@ Remember that an action register can contain up to 255 actions, which means that ```cpp total_fees = sum(action_fee) + sum(total_fwd_fees); ``` + ::: Starting from the fourth [global version](https://github.com/ton-blockchain/ton/blob/master/doc/GlobalVersions.md) of TON, if a "send message" action fails, the account is required to pay for processing the cells of the message, referred to as the `action_fine`. @@ -273,36 +279,15 @@ max_cells = floor(remaining_balance / fine_per_cell) action_fine = fine_per_cell * min(max_cells, cells_in_msg); ``` -## Fee's calculation formulas - -### storage_fees - -```cpp -storage_fees = ceil( - (account.bits * bit_price - + account.cells * cell_price) - * period / 2 ^ 16) -``` - -:::info -Only unique hash cells are counted for storage and fwd fees i.e. 3 identical hash cells are counted as one. - -In particular, it deduplicates data: if there are several equivalent sub-cells referenced in different branches, their content is only stored once. - -Read more about [deduplication](/v3/documentation/data-formats/tlb/library-cells). -::: - -// bits in the root cell of a message are not included in msg.bits (lump_price pays for them) - ## Fee's config file -All fees are nominated in nanotons or nanotons multiplied by 2^16 to [maintain accuracy while using integer](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#forward-fees) and may be changed. The config file represents the current fee cost. +All fees are nominated in nanotons or nanotons multiplied by 2^16 to [maintain accuracy while using integer](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#forward-fees) and may be changed. The config file represents the current fee cost. -* storage_fees = [p18](https://tonviewer.com/config#18) -* in_fwd_fees = [p24](https://tonviewer.com/config#24), [p25](https://tonviewer.com/config#25) -* computation_fees = [p20](https://tonviewer.com/config#20), [p21](https://tonviewer.com/config#21) -* action_fees = [p24](https://tonviewer.com/config#24), [p25](https://tonviewer.com/config#25) -* out_fwd_fees = [p24](https://tonviewer.com/config#24), [p25](https://tonviewer.com/config#25) +- storage_fees = [p18](https://tonviewer.com/config#18) +- in_fwd_fees = [p24](https://tonviewer.com/config#24), [p25](https://tonviewer.com/config#25) +- computation_fees = [p20](https://tonviewer.com/config#20), [p21](https://tonviewer.com/config#21) +- action_fees = [p24](https://tonviewer.com/config#24), [p25](https://tonviewer.com/config#25) +- out_fwd_fees = [p24](https://tonviewer.com/config#24), [p25](https://tonviewer.com/config#25) :::info [A direct link to the mainnet live config file](https://tonviewer.com/config) @@ -312,10 +297,10 @@ For educational purposes [example of the old one](https://explorer.toncoin.org/c ## References -* Based on @thedailyton [article](https://telegra.ph/Fees-calculation-on-the-TON-Blockchain-07-24) from 24.07* +- Based on @thedailyton [article](https://telegra.ph/Fees-calculation-on-the-TON-Blockchain-07-24) from 24.07\* ## See Also -* [TON Fees overview](/v3/documentation/smart-contracts/transaction-fees/fees) -* [Transactions and Phases](/v3/documentation/tvm/tvm-overview#transactions-and-phases) -* [Fees calculation](/v3/guidelines/smart-contracts/fee-calculation) \ No newline at end of file +- [TON Fees overview](/v3/documentation/smart-contracts/transaction-fees/fees) +- [Transactions and Phases](/v3/documentation/tvm/tvm-overview#transactions-and-phases) +- [Fees calculation](/v3/guidelines/smart-contracts/fee-calculation) diff --git a/docs/v3/documentation/smart-contracts/transaction-fees/fees.md b/docs/v3/documentation/smart-contracts/transaction-fees/fees.md index 3480496f7c..4b19173fcc 100644 --- a/docs/v3/documentation/smart-contracts/transaction-fees/fees.md +++ b/docs/v3/documentation/smart-contracts/transaction-fees/fees.md @@ -22,9 +22,9 @@ Current settings in masterchain are as follows: 1 unit of gas costs 10000 nanoto ### Average transaction cost -> **TLDR:** Today, every transaction costs around **~0.005 TON** +> **TLDR:** Today, basic transaction costs around **~0.0025 TON** -Even if TON price increases 100 times, transactions will remain ultra-cheap; less than $0.01. Moreover, validators may lower this value if they see commissions have become expensive [read why they're interested](#gas-changing-voting-process). +Even if TON price increases 100 times, transactions will remain ultra-cheap; about $0.01. Moreover, validators may lower this value if they see commissions have become expensive [read why they're interested](#gas-changing-voting-process). :::info The current gas amount is written in the Network Config [param 20](https://tonviewer.com/config#20) and [param 21](https://tonviewer.com/config#21) for masterchain and basechain respectively. @@ -38,7 +38,7 @@ Changing any parameter requires getting 66% of the validator votes. #### Could gas cost more? -> *Does it mean that one day gas prices could rise by 1,000 times or even more?* +> _Does it mean that one day gas prices could rise by 1,000 times or even more?_ Technically, yes; but in fact, no. @@ -64,15 +64,13 @@ Fees on TON are calculated by this formula: ```cpp transaction_fee = storage_fees - + in_fwd_fees + + in_fwd_fees // also named import_fee + computation_fees + action_fees + out_fwd_fees ``` - ```jsx live - // Welcome to LIVE editor! // feel free to change any variables // Check https://retracer.ton.org/?tx=b5e14a9c4a4e982fda42d6079c3f84fa48e76497a8f3fca872f9a3737f1f6262 @@ -90,12 +88,12 @@ function FeeCalculator() { const ihr_disabled = 0; // First of all define is ihr gonna be counted let fwd_fee = - (lump_price + Math.ceil((bit_price * 0 + cell_price * 0) / bit16)) * nano; + lump_price + Math.ceil((bit_price * 0 + cell_price * 0) / bit16); if (ihr_disabled) { var ihr_fee = 0; } else { - var ihr_fee = Math.ceil((fwd_fee * ihr_price_factor) / bit16) * nano; + var ihr_fee = Math.ceil((fwd_fee * ihr_price_factor) / bit16); } let total_fwd_fees = fwd_fee + ihr_fee; @@ -103,36 +101,32 @@ function FeeCalculator() { let storage_fees = 0.000000003; // And storage fees as well let total_action_fees = +((fwd_fee * first_frac) / bit16).toFixed(9); let import_fee = - (lump_price + Math.ceil((bit_price * 528 + cell_price * 1) / bit16)) * nano; - let total_fee = +( - gas_fees + - storage_fees + - total_action_fees + - import_fee - ).toFixed(9); + lump_price + Math.ceil((bit_price * 528 + cell_price * 1) / bit16); + let total_fee = + gas_fees + storage_fees + total_action_fees * nano + import_fee * nano; return (
-

Total fee: {total_fee} TON

-

Action fee: {total_action_fees} TON

-

Fwd fee: {fwd_fee} TON

-

Import fee: {import_fee} TON

+

Total fee: {+total_fee.toFixed(9)} TON

+

Action fee: {+(total_action_fees * nano).toFixed(9)} TON

+

Fwd fee: {+(total_fwd_fees * nano).toFixed(9)} TON

+

Import fee: {+(import_fee * nano).toFixed(9)} TON

+

IHR fee: {+(ihr_fee * nano).toFixed(9)} TON

); } - ``` ## Elements of transaction fee -* `storage_fees` is the amount you pay for storing a smart contract in the blockchain. In fact, you pay for every second the smart contract is stored on the blockchain. - * _Example_: your TON wallet is also a smart contract, and it pays a storage fee every time you receive or send a transaction. Read more about [how storage fees are calculated](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#storage-fee). -* `in_fwd_fees` is a charge for importing messages only from outside the blockchain, e.g. `external` messages. Every time you make a transaction, it must be delivered to the validators who will process it. For ordinary messages from contract to contract this fee is not applicable. Read [the TON Blockchain paper](https://docs.ton.org/tblkch.pdf) to learn more about inbound messages. - * _Example_: each transaction you make with your wallet app (like Tonkeeper) requires first to be distributed among validation nodes. -* `computation_fees` is the amount you pay for executing code in the virtual machine. The larger the code, the more fees must be paid. - * _Example_: each time you send a transaction with your wallet (which is a smart contract), you execute the code of your wallet contract and pay for it. -* `action_fees` is a charge for sending outgoing messages made by a smart contract, updating the smart contract code, updating the libraries, etc. -* `out_fwd_fees` stands for a charge for sending messages outside the TON Blockchain to interact with off-chain services (e.g., logs) and external blockchains. +- `storage_fees` is the amount you pay for storing a smart contract in the blockchain. In fact, you pay for every second the smart contract is stored on the blockchain. + - _Example_: your TON wallet is also a smart contract, and it pays a storage fee every time you receive or send a transaction. Read more about [how storage fees are calculated](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#storage-fee). +- `in_fwd_fees` is a charge for importing messages only from outside the blockchain, e.g. `external` messages. Every time you make a transaction, it must be delivered to the validators who will process it. For ordinary messages from contract to contract this fee is not applicable. Read [the TON Blockchain paper](https://docs.ton.org/tblkch.pdf) to learn more about inbound messages. + - _Example_: each transaction you make with your wallet app (like Tonkeeper) requires first to be distributed among validation nodes. +- `computation_fees` is the amount you pay for executing code in the virtual machine. The larger the code, the more fees must be paid. + - _Example_: each time you send a transaction with your wallet (which is a smart contract), you execute the code of your wallet contract and pay for it. +- `action_fees` is a charge for sending outgoing messages made by a smart contract, updating the smart contract code, updating the libraries, etc. +- `out_fwd_fees` stands for a charge for sending messages outside the TON Blockchain to interact with off-chain services (e.g., logs) and external blockchains. ## FAQ @@ -156,7 +150,7 @@ Saving 1 MB of data for one year on TON will cost 6.01 TON. Note that you usuall ### Is it possible to send a gasless transaction? -In TON, gasless transactions are possible using [wallet v5](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts#preparing-for-gasless-transactions) a relayer that pays the gas fee for transaction. +In TON, gasless transactions are possible using [wallet v5](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts#preparing-for-gasless-transactions) a relayer that pays the gas fee for transaction. ### How to calculation? @@ -164,9 +158,9 @@ There is an article about [fee calculation](/v3/guidelines/smart-contracts/fee-c ## References -* Based on the [@thedailyton article](https://telegra.ph/Commissions-on-TON-07-22) originally written by [menschee](https://github.com/menschee)* +- Based on the [@thedailyton article](https://telegra.ph/Commissions-on-TON-07-22) originally written by [menschee](https://github.com/menschee)\* ## See Also -* ["Low-level fees overview"](/v3/documentation/smart-contracts/transaction-fees/fees-low-level)—read about the formulas for calculating commissions. -* [Smart contract function to calculate forward fees in FunC](https://github.com/ton-blockchain/token-contract/blob/main/misc/forward-fee-calc.fc) +- ["Low-level fees overview"](/v3/documentation/smart-contracts/transaction-fees/fees-low-level)—read about the formulas for calculating commissions. +- [Smart contract function to calculate forward fees in FunC](https://github.com/ton-blockchain/token-contract/blob/main/misc/forward-fee-calc.fc)