Runtime Fees
Runtime fees are measured in Gas. Gas price will be discussed separately.
When a transaction is converted into a receipt, the signer account is charged for the full cost of the transaction. This cost consists of extra attached gas, attached deposits and the transaction fee.
The total transaction fee is the sum of the following:
- A fee for creation of the receipt
- A fee for every action
Every Fee consists of 3 values measured in gas:
send_sirandsend_not_sir- the gas burned when the action is being created to be sent to a receiver.send_siris used whencurrent_account_id == receiver_id(current_account_idis asigner_idfor a signed transaction).send_not_siris used whencurrent_account_id != receiver_id
execution- the gas burned when the action is being executed on the receiver's account.
Receipt creation cost
Fees are charged for 2 types of receipts:
- Action receipts ActionReceipt
- Data receipts DataReceipt
A transaction is converted into an ActionReceipt. Data receipts are used for data dependencies and will be discussed separately.
The Fee for an action receipt creation is described in the config action_receipt_creation_config.
Example: when a signed transaction is being converted into a receipt, the gas for action_receipt_creation_config.send is being burned immediately,
while the gas for action_receipt_creation_config.execution is only charged, but not burned. It'll be burned when
the newly created receipt is executed on the receiver's account.
Fees for actions
Every Action has a corresponding Fee(s).
The fees of most actions are described in the config action_creation_config.
The fees of the global contract, state init and gas key actions are not part of that config.
This page names those fees by their runtime parameter names, such as action_gas_key_transfer.
Similar to a receipt creation costs, the send gas is burned when an action is added to a receipt to be sent, and the execution gas is only charged, but not burned.
Fees are either a base fee or a fee per unit of some data within the action, such as a byte or a storage entry.
Here is the list of actions and their corresponding fees:
-
CreateAccount uses
- the base fee
create_account_cost
- the base fee
-
DeployContract uses the sum of the following fees:
- the base fee
deploy_contract_cost - the fee per byte of the contract code to be deployed with the fee
deploy_contract_cost_per_byteTo compute the number of bytes for a deploy contract actiondeploy_contract_actionusedeploy_contract_action.code.len()
- the base fee
-
FunctionCall uses the sum of the following fees:
- the base fee
function_call_cost - the fee per byte of method name string and per byte of arguments with the fee
function_call_cost_per_byte. To compute the number of bytes for a function call actionfunction_call_actionusefunction_call_action.method_name.as_bytes().len() + function_call_action.args.len()
- the base fee
-
Transfer uses one of the following fees:
- if the
receiver_idis a NEAR-implicit account ID, then a sum of base fees is used:- the create account base fee
create_account_cost - the transfer base fee
transfer_cost - the add full access key base fee
add_key_cost.full_access_cost
- the create account base fee
- if the
receiver_idis an ETH-implicit account ID, a deterministic account ID, or from protocol version 88 (nearcore 2.14.0) a universal (0u) account ID, then a sum of base fees is used:- the create account base fee
create_account_cost - the transfer base fee
transfer_cost
- the create account base fee
- for any other
receiver_id, only the base fee is used:- the transfer base fee
transfer_cost
- the transfer base fee
- if the
-
Stake uses
- the base fee
stake_cost
- the base fee
-
AddKey uses one of the following fees:
- if the access key is
AccessKeyPermission::FullAccessthe base fee is used- the add full access key base fee
add_key_cost.full_access_cost
- the add full access key base fee
- if the access key is
AccessKeyPermission::FunctionCallthe sum of the fees is used- the add function call permission access key base fee
add_key_cost.function_call_cost - the fee per byte of method names with extra byte for every method with the fee
add_key_cost.function_call_cost_per_byteTo compute the number of bytes forfunction_call_permissionusefunction_call_permission.method_names.iter().map(|name| name.as_bytes().len() as u64 + 1).sum::<u64>()
- the add function call permission access key base fee
- if the access key is a gas key, with
AccessKeyPermission::GasKeyFullAccessorAccessKeyPermission::GasKeyFunctionCall, the fees of the matchingFullAccessorFunctionCallcase above are used, plus the following fees:- the
sendpart of the fee per byteaction_gas_key_byte, for the bytes of the gas key information. To compute the number of bytes useGasKeyInfo::borsh_len() - the
executionpart of the base feeaction_gas_key_nonce_write_base, once for each nonce of the gas key - the
executionpart of the fee per byteaction_gas_key_byte, for the trie key and value of each nonce. To compute the number of bytes usegas_key_info.num_nonces * (receiver_id.len() + add_key_action.public_key.trie_id_len() + 2 + size_of::<NonceIndex>() + size_of::<Nonce>()). The2counts the column byte and the separator byte of the trie key.
- the
- if the access key is
-
DeleteKey uses
- the base fee
delete_key_cost
- the base fee
-
DeleteAccount uses the base fee
delete_account_cost. The remaining balance goes tobeneficiary_idin a balance refund receipt, which has no fee. -
Delegate uses the sum of the following fees.
DelegateV2uses the same fees:- the base fee
delegate_cost - the
sendfees of the inner actions, which are burned when the transaction is converted into a receipt - the
sendandexecutionfees of the inner actions and theexecutionfee of an action receipt. These fees are prepaid for the receipt that the delegate action creates. The prepaidsendfees are burned when that receipt is created.
- the base fee
-
DeployGlobalContract uses the sum of the following fees:
- the base fee
action_deploy_global_contract - the fee per byte of the contract code
action_deploy_global_contract_per_byte. To compute the number of bytes usedeploy_global_contract_action.code.len()
The action also burns tokens for the code storage, see DeployGlobalContractAction.
- the base fee
-
UseGlobalContract uses the sum of the following fees:
- the base fee
action_use_global_contract - the fee per byte of the contract identifier
action_use_global_contract_per_identifier_byte. To compute the number of bytes useuse_global_contract_action.contract_identifier.len(). It is 32 for a code hash and the length of the account ID for an account ID.
- the base fee
-
DeterministicStateInit uses the sum of the following fees:
- the base fee
action_deterministic_state_init - the fee per byte of the storage keys and values
action_deterministic_state_init_per_byte. To compute the number of bytes usestate_init_action.state_init.len_bytes(), which is the total length of all keys and values instate_init_action.state_init.data() - the fee per storage entry
action_deterministic_state_init_per_entry. To compute the number of entries usestate_init_action.state_init.data().len()
- the base fee
-
TransferToGasKeyandWithdrawFromGasKeyuse the sum of the following fees:- the base fee
action_gas_key_transfer - the
sendpart of the fee per byteaction_gas_key_byte, for the bytes of the public key. To compute the number of bytes useaction.public_key.trie_id_len(). This is the length of the key as stored in the trie: 33 bytes for ED25519 and ML-DSA-65 keys, and 65 bytes for SECP256K1 keys. - the
executionpart of the fee per byteaction_gas_key_byte, for the trie key and value of the gas key. To compute the number of bytes usereceiver_id.len() + action.public_key.trie_id_len() + 2 + AccessKey::min_gas_key_borsh_len().AccessKey::min_gas_key_borsh_len()is the Borsh length of the smallest gas key record. - For an action that a contract creates, the host function uses the encoded key length (1953 bytes for ML-DSA-65)
instead of
trie_id_len()for thesendpart, and also for theexecutionpart unlessfix_ml_dsa_cost_chargingis enabled.
- the base fee
-
From protocol version 88 (nearcore 2.14.0),
UniversalStateInituses the sum of the following fees:- the base fee
action_universal_state_init - the fee per byte of the serialized state init
action_universal_state_init_per_byte. To compute the number of bytes useuniversal_state_init_action.state_init.0.len() - the fee per storage entry
action_universal_state_init_per_entry, for each storage entry in the decoded state init - the add full access key base fee
add_key_cost.full_access_cost, for each access key in the decoded state init
- the base fee
Account creation charge
From protocol version 85 (nearcore 2.13.0), the runtime charges extra when a receipt creates an account.
The aim is that creating an account costs account_creation_charge, which is 0.007 NEAR.
The charge applies when a receipt succeeds and its receiver account exists after the receipt but not before it.
This applies to CreateAccount and DeterministicStateInit. It also applies to a Transfer that is the only action of its receipt and goes to a new NEAR-implicit, ETH-implicit or deterministic account.
From protocol version 88 (nearcore 2.14.0), it also happens with a Transfer to a new universal (0u) account and with UniversalStateInit.
The receipt burns gas at burn_price, which is the lower of the receipt gas price and the gas price of the current block.
The refund of the receipt includes the price difference for all the gas that the receipt burned, see Refunds.
The runtime takes the charge from this price difference refund and burns it:
price_difference_refund = gas_burnt * (receipt_gas_price - burn_price)
create_account_execution_cost = config.action_creation_config.create_account_cost.execution * burn_price
charge = min(max(0, account_creation_charge - create_account_execution_cost), price_difference_refund)
At a burn price of 10^8 yoctoNEAR per gas, the execution fee of create_account_cost is 0.00072 NEAR, and the charge is 0.00628 NEAR.
Together they cost 0.007 NEAR.
If the burn price is so high that the execution fee alone reaches 0.007 NEAR, there is no extra charge.
Gas tracking
In Runtime, gas is tracked in the following fields of ActionResult struct:
gas_burnt- irreversible amount of gas spent on computations.gas_used- includes burnt gas and gas attached to the newActionReceipts created during the method execution.gas_burnt_for_function_call- stores gas burnt during function call execution. Up to protocol version 86, the contract account gets 30% of it as a reward for a possibility to invoke the function. From protocol version 87 (nearcore 2.14.0), the reward parameterburnt_gas_rewardis 0, so the contract account gets no reward.
Initially runtime charges gas_used from the account. Some gas may be refunded later, see Refunds.
At first, we charge fees related to conversion from SignedTransaction to ActionReceipt and future execution of this receipt:
- costs of all
SignedTransactions passed toRuntime::applyare computed intx_costfunction during validation; total_costis deducted from signer, which is a sum of:gas_burnt * gas_pricewheregas_burntis action receipt send fee +total_send_fees(transaction.actions)+ the signature verification cost, andgas_priceis the gas price of the current block;gas_remaining * receipt_gas_pricewheregas_remainingistotal_prepaid_gas(transaction.actions)+total_prepaid_send_fees(transaction.actions)+ action receipt exec fee +total_prepaid_exec_fees(transaction.actions)to pay the attached gas and all remaining fees caused by transaction;total_deposit(transaction.actions);
- each transaction is converted to receipt and passed to
Runtime::process_receipt.
total_prepaid_gas is the gas attached to function calls, including the function calls inside delegate actions.
total_prepaid_send_fees is the sum of the send fees of the actions inside delegate actions.
The signature verification cost is charged for the key that signed the transaction and for the key of each delegate action.
It is ml_dsa_65_verification_cost (100 Ggas) for an ML-DSA-65 key and 0 for ED25519 and SECP256K1 keys.
ML-DSA-65 keys are supported from protocol version 85 (nearcore 2.13.0).
receipt_gas_price is max(gas_price, min_gas_purchase_price), and it becomes the gas_price of the new receipt.
From protocol version 85 (nearcore 2.13.0), min_gas_purchase_price is 10^9 yoctoNEAR per gas. Before that, it was 0.
The receipt later burns gas at the lower of receipt_gas_price and the gas price of the block where it is executed.
Unused gas and the price difference for burned gas are refunded, see Refunds.
Then each ActionReceipt is passed to Runtime::apply_action_receipt where gas is tracked as follows:
ActionResultis created withActionReceiptexecution fee;- all actions inside
ActionReceiptare passed toRuntime::apply_action; ActionResultwith charged base execution fees is created there;- if action execution creates new
ActionReceipts, their fees are added to theActionResult. For example, when a function call creates a promise, it pays:gas_burnt: the send fees for the newActionReceiptand its actions, plus the base send and exec fees of oneDataReceiptfor each promise that the newActionReceiptwaits on. The per-byteDataReceiptfees are burnt later, by thevalue_returncall of the function that produces the datagas_used:gas_burnt+ the exec fees for the newActionReceiptand its actions + the gas attached to the function calls in the newActionReceipt
- all computed
ActionResults are merged into one, where all gas values are summed up; - unused gas is refunded in
refund_unspent_gas_and_deposits, after subtracting the gas refund fee, see Refunds.
Inside VMLogic, the fees are tracked in the GasCounter struct.
The VM itself is called in the action_function_call inside Runtime. When all actions are processed, the result is returned as a VMOutcome, which is later merged with ActionResult.
Gas key transactions
From protocol version 85 (nearcore 2.13.0), a transaction can be signed with a gas key. Its fees are the same as for a transaction signed with a regular access key, but its cost is paid from two balances:
- The gas cost is paid from the gas key balance. It is
gas_burntconverted at the current gas price, plusgas_remainingconverted at the receipt gas price. The receipt gas price is the larger of the current gas price andmin_gas_purchase_price. - The deposit cost,
total_deposit(transaction.actions), is paid from the account balance. It is the sum of the deposits of all actions, such asTransfer,FunctionCallandTransferToGasKey.
If the gas key balance does not cover the gas cost, the transaction fails with NotEnoughGasKeyBalance and nothing is charged.
If the account balance does not cover the deposits, or does not cover the storage of the account after them, the transaction fails with NotEnoughBalanceForDeposit.
In that case the gas key is charged only for gas_burnt, at the current gas price. See Gas key transactions.
Gas refunds of a gas key transaction go back to the gas key balance, see Refunds.
Example
Let's say we have the following transaction:
#![allow(unused)] fn main() { Transaction { signer_id: "alice.near", public_key: "2onVGYTFwyaGetWckywk92ngBiZeNpBeEjuzSznEdhRE", nonce: 23, receiver_id: "lockup.alice.near", block_hash: "3CwEMonK6MmKgjKePiFYgydbAvxhhqCPHKuDMnUcGGTK", actions: [ Action::CreateAccount(CreateAccountAction {}), Action::Transfer(TransferAction { deposit: 100000000000000000000000000, }), Action::DeployContract(DeployContractAction { code: vec![/*<...128000 bytes...>*/], }), Action::FunctionCall(FunctionCallAction { method_name: "new", args: b"{\"owner_id\": \"alice.near\"}".to_vec(), gas: 25000000000000, deposit: 0, }), ], } }
It has signer_id != receiver_id so it will use send_not_sir for send fees.
It contains 4 actions with 2 actions that requires to compute number of bytes.
We assume code in DeployContractAction contains 128000 bytes. And FunctionCallAction has
method_name with length of 3 and args length of 26, so total of 29.
First let's compute the amount that will be burned immediately for sending a receipt.
burnt_gas = \
config.action_receipt_creation_config.send_not_sir + \
config.action_creation_config.create_account_cost.send_not_sir + \
config.action_creation_config.transfer_cost.send_not_sir + \
config.action_creation_config.deploy_contract_cost.send_not_sir + \
128000 * config.action_creation_config.deploy_contract_cost_per_byte.send_not_sir + \
config.action_creation_config.function_call_cost.send_not_sir + \
29 * config.action_creation_config.function_call_cost_per_byte.send_not_sir
Now, by using burnt_gas, we can calculate the total transaction fee
total_transaction_fee = burnt_gas + \
config.action_receipt_creation_config.execution + \
config.action_creation_config.create_account_cost.execution + \
config.action_creation_config.transfer_cost.execution + \
config.action_creation_config.deploy_contract_cost.execution + \
128000 * config.action_creation_config.deploy_contract_cost_per_byte.execution + \
config.action_creation_config.function_call_cost.execution + \
29 * config.action_creation_config.function_call_cost_per_byte.execution
This total_transaction_fee is the amount of gas required to create a new receipt from the transaction.
NOTE: There are extra amounts required to prepay for deposit in TransferAction and gas in FunctionCallAction, but this is not part of the total transaction fee.