Fee calculation
Introduction
Transaction fees consist of a few types of different fees connected to the execution of a single transaction. Transactions itself are complex processes, and fees are paid relative to different stages of executing them.
In this document, we explain how the fees are calculated.
We shall define transaction_fee as a sum of all fees for a single transaction.
transaction_fee = inbound_external_message_fee
+ storage_fees
+ gas_fees
+ total_action_fees
+ outbound_internal_messages_fee
Where:
inbound_external_message_fee
— is deducted, if an inbound external message is imported in the transaction.
storage_fees
— storage costs since the moment of the last transaction.
gas_fees
— include all gas fees associated with the transaction. You can find more info in the Gas calculation basics section.
total_action_fees
— fees for performing send message actions.
outbound_internal_messages_fee
— is calculated as a sum of fees for all outbound internal messages generated by the transaction.
Depending on the nature of the transaction, all of these except storage fees may not be applicable.
Below we examine these types of fees in detail.
Note: Block creation fee is not to be confused with the fees discussed in this document. Block creation fee is the new coins minted by the elector contract and distributed among validators as reward for creating blocks. It is not part of transaction fees.
Storage fees
Every transaction in Everscale has a storage phase that implies a certain storage fee charged on an account balance. This fee is charged for the period between transactions and is calculated according to the following formula:
storage_fees = CEIL(
(
account.bits
* global_bit_price
+ account.cells
* global_cell_price
) * period / 2 ^ 16
)
Where:
account.bits
and account.cells
— stand for a number of bits and cells in the Account structure represented as tree of cells (including code and data).
global_bit_price
— is a global configuration parameter (p18
for both masterchain and workchains), price for storing one bit.
global_cell_price
— another global configuration parameter (p18
for both masterchain and workchains), price for storing one cell.
period
— number of seconds since previous storage fee payment.
Note: While
account.bits
are generally easy to estimate, theaccount.cells
value can vary greatly for different types of data. A cell can contain no more than 1023 bits and 4 references to other cells. Contract code and numerical variables tend to be packed into cells effectively, resulting in mostly full cells, and thus a minimal number of cells needed to store the data. More complex data structures can be packed into cells less efficiently, taking up more cells to store the same amount of data.
Example: Let's calculate a minimal fee for storing 1KB of data for the duration of one day on a workchain:
global_bit_price
= 1
global_cell_price
= 500
period
= 86400 seconds
account.bits
= 8192
The minimal account.cells
value for 8192 bits of data is 9 (rounding 8192/1023 up to the nearest integer).
Thus the minimum storage fee would be calculated as follows:
storage_fees = CEIL(
(
8192
* 1
+ 9
* 500
) * 86400 / 65536
) = 16733 nanotokens = 0.000016733 tokens
Real storage fees for 1KB account can be higher, depending on the specific features of the contract.
If the account balance is less than the due storage fee, the account is frozen and its balance is subtracted from storage fee and reduced to zero. Remaining storage fee is stored in account as debt.
Note: Current global configuration can be always reviewed on [ever.live]( https:/ /ever.live/) in the master config section of the latest key block details (example)
FIXME broken link
. It can only be changed by a vote of validators.
Message fees
Every message is subject to a forwarding fee, which is calculated according to the following formula:
msg_fwd_fee = (
lump_price + CEIL(
(
bit_price
* msg.bits
+ cell_price
* msg.cells
) / 2 ^ 16
)
)
msg.bits
and msg.cells
are calculated from message represented as a tree of cells. Root cell is not counted. lump_price
, bit_price
, cell_price
are contained in global config parameters p24
and p25
, and can and can only be changed by a vote of validators.
Note: Like in storage fees,
msg.bits
are generally easy to estimate, while themsg.cells
value can vary for different types of messages.
Example: Let's calculate a minimal forward fee for sending a 1KB message on a workchain:
lump_price
= 10000000
bit_price
= 655360000
cell_price
= 65536000000
To calculate msg.bits
we subtract the root cell bits from the total message bits. For this example we'll assume that the root cell is filled completely (usually this is not the case, and the subtracted value is smaller, which results in a higher fee):
msg.bits = 8192 - 1023 = 7169
To calculate msg.cells
we subtract the root cell from the total umber of cells. The minimal number of cells in a 1 KB message is 9 (rounding 8192/1023 up to the nearest integer). Thus msg.cells
is calculated as follows:
msg.bits = 9 - 1 = 8
The minimum forward fee for a 1KB message would be calculated as follows:
msg_fwd_fee = (
10000000 + CEIL(
(
655360000
* 7169
+ 65536000000
* 8
) / 65536
)
) = 89690000 nanotokens = 0.08969 tokens
Real forward fees for 1 KB messages may be higher, depending on the type and contents of the message.
Note: Current global configuration can be always reviewed on ever.live in the master config section of the latest key block details (example)
FIXME broken link
.
Outbound messages
outbound_internal_messages_fee
is calculated as a sum of outbound internal message fees for every message generated as result of transaction execution:
outbound_internal_messages_fee = SUM(
out_int_msg.header.fwd_fee
+ out_int_msg.header.ihr_fee
)
Where
out_int_msg.header.fwd_fee
is a part of the standard forward fee for the outbound internal message.
out_int_msg.header.ihr_fee
is currently disabled.
Routing
The forward fee for outbound internal message is split into int_msg_mine_fee
and int_msg_remain_fee
:
msg_forward_fee = int_msg_mine_fee + int_msg_remain_fee
Where:
int_msg_mine_fee = msg_forward_fee * first_frac / 2 ^ 16
first_frac
— is contained in global config parameters p24
and p25
, and determines the fraction of the fee, that the current set of validators receive.
Note: Current global configuration can be always reviewed on ever.live in the master config section of the latest key block details (example)
FIXME broken link
.
int_msg_mine_fee
then becomes part of transaction action fees (see below).
The remaining int_msg_remain_fee
is placed in the header of outbound internal message (becoming out_int_msg.header.fwd_fee
) and will go to validators who will process the message.
If, while being forwarded to the destination address, the message passes through additional validator sets (i.e. if the validator set changes more than once while the message is being forwarded), a part of out_int_msg.header.fwd_fee
is payed to the relevant validator set every time and the remaining fee in the message header is reduced by this amount:
intermediate_fee = out_int_msg.header.fwd_fee * next_frac / 2 ^ 16
next_frac
— is contained in global config parameters p24
and p25
, and determines the fraction of the remaining forward fee, that intermediary validators receive.
Note: Current global configuration can be always reviewed on ever.live in the master config section of the latest key block details (example)
FIXME broken link
.
Note: Length of route does not affect the initial calculation of the forward fee. The fee is simply split between all involved validators according to global config parameters.
Note: If an exception is thrown, and a bounce message is generated, it is subject to fees, just like a single regular outbound message.
Inbound external messages
Whenever an inbound external message needs to be imported for transaction execution, the for this action fee is calculated according to the standard forwarding fee formula, and paid to the current validators.
Action fees
Action fees pay for performing 'send message' actions. They consist of all fees for external outbound messages, and the first fraction of internal outbound message fees. They are calculated as follows:
total_action_fees = total_out_ext_msg_fwd_fee + total_int_msg_mine_fee
where:
total_out_ext_msg_fwd_fee
— sum of implicit forward fee for all generated outbound external messages.
total_int_msg_mine_fee
— sum of 'mine' parts of message forward fees for outbound internal messages.
total_fwd_fees
— is a separate way to calculate total forwarding fees.
total_fwd_fees = total_action_fees + SUM(
int_msg_remain_fee
+ out_int_msg.header.ihr_fee
)
out_int_msg.header.ihr_fee
— this fee is currently zero.
The action fee might be absent if no actions are performed during the transaction.
Gas fees
trans.gas_fees
includes all gas fees associated with the transaction.
As with Action fees, Gas fees are not always applicable. They can be skipped if the TVM compute phase is not initialized for a transaction.
In order to study the calculation of Gas fees in detail please consult this page.