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
There are 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) described in the config action_creation_config.
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 byte of some data within the action.
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 an 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 NOT an Implicit Account ID, then 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
- DeleteKey uses
- the base fee
delete_key_cost
- the base fee
- DeleteAccount uses
- the base fee
delete_account_cost - action receipt creation fee for creating Transfer to send remaining funds to
beneficiary_id - full transfer fee described in the corresponding item
- the base fee
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. Later, contract account gets 30% of it as a reward for a possibility to invoke the function.
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_to_balance(gas_burnt)wheregas_burntis action receipt send fee +total_send_fees(transaction.actions));gas_to_balance(gas_remaining)wheregas_remainingis action receipt exec fee +total_prepaid_exec_fees(transaction.actions)to pay all remaining fees caused by transaction;total_deposit(transaction.actions);
- each transaction is converted to receipt and passed to
Runtime::process_receipt.
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 leads to new
ActionReceipts creation, correspondingaction_[action_name]function adds new fees to theActionResult. E.g.action_delete_accountalso charges the following fees:gas_burnt: send fee for newActionReceiptcreation + complex send fee forTransferto beneficiary accountgas_used:gas_burnt+ exec fee for createdActionReceipt+ complex exec fee forTransfer
- all computed
ActionResults are merged into one, where all gas values are summed up; - unused gas is refunded in
generate_refund_receipts, 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.
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.