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
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.
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(
) * period / 2 ^ 16
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.
account.bitsare generally easy to estimate, the
account.cellsvalue 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
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(
) * 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.
Every message is subject to a forwarding fee, which is calculated according to the following formula:
msg_fwd_fee = (
lump_price + CEIL(
) / 2 ^ 16
msg.cells are calculated from message represented as a tree of cells. Root cell is not counted.
cell_price are contained in global config parameters
p25, and can and can only be changed by a vote of validators.
Note: Like in storage fees,
msg.bitsare generally easy to estimate, while the
msg.cellsvalue 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
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
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(
) / 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.
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 is a part of the standard forward fee for the outbound internal message.
out_int_msg.header.ihr_fee is currently disabled.
The forward fee for outbound internal message is split into
msg_forward_fee = int_msg_mine_fee + int_msg_remain_fee
int_msg_mine_fee = msg_forward_fee * first_frac / 2 ^ 16
first_frac — is contained in global config parameters
p25, and determines the fraction of the fee, that the current set of validators receive.
int_msg_mine_fee then becomes part of transaction action fees (see below).
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
p25, and determines the fraction of the remaining forward fee, that intermediary validators receive.
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 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
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(
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.
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.