Hedera is a public ledger. Nodes on the network collectively establish and maintain a consensus state.
Clients interact with that state through API calls sent to the nodes of the network.
To write to state, clients send transactions to a single or multiple nodes, for subsequent gossip to the rest of the network, assignment of a consensus timestamp and a place in consensus order, and then application to state in that order.
To read from state, clients send queries to a chosen node which, by looking at its local copy of state, can respond with the requested information. This interaction is a standard request/response model with no consensus implications (other than that the state was created based on consensus transactions).
For all transactions, and almost all queries, the client will pay a fee for nodes processing of a transaction into state or a node responding to a query about state.
The guiding principle of the fee model is that transactions and queries will have a fee that both reflects the amount of bandwidth, processing, and storage resources they consume, as well as the duration of that consumption.
A transaction fee is the sum of a node fee that compensates the node that processes the API call, a network fee that compensates the network for processing the transaction into a consensus order, and a service fee that compensates the network for performing the requested service, such as transfer some HBAR, store a file, etc.
Queries impact only the single node that received the query and responded and so will generally be less expensive.
Three particular queries are currently free:
- Asking for the balance of a crypto account
- Asking for a receipt associated with a transaction
- Asking for the projected cost of a query if a client were to send that query
A published fee schedule (with file identifier 0.0.111) designates the weights of different network resources (e.g. bandwidth, GPU, RAM, hard drive, etc.) towards fees – these weights are designed to ensure that more scarce resources are priced higher than abundant resources. Fees also reflect the duration of storage and not just bytes.
For a given transaction (or query) nodes determine what is the appropriate fee by:
- Analyzing the specifics of the transaction (or query)
- Calculating how much of each scarce resource that transaction (or query) will consume
- Multiplying by the specified weights from the fee schedule to determine the total fee
Generally, nodes will perform the above calculation and determine if:
- The client paying the fee has sufficient balance and
- The client authorizes (as manifested in an appropriate signature) the calculated fee to be paid
The coefficients are in US dollars (USD), but the transaction fee is in HBAR. So there needs to be a number that is used to convert between the two.
The official exchange rate between USD and HBAR is published to the network in another file (with file identifier 0.0.112). Hedera updates this exchange rate file frequently so that clients will be insulated from volatility of HBAR against USD in the fees they pay. As HBAR goes up / down against USD, the changing exchange rate will mean that a given transaction will cost fewer or more HBAR over time – resulting in a stable USD price.
This stability in USD fees means that the fee that nodes calculate (and deduct from the client’s account) in HBAR will go up and down as the exchange rate varies – even for the exact same transaction.
How do clients pay fees?
The mechanisms by which clients pay for transactions and queries are different.
When a client sends a transaction to a node, the transaction includes a transactionFee parameter that the client stipulates. By signing the transaction, the client authorizes that, at maximum, the amount of the transactionFee should be used to pay for the processing of the transaction by the network.
In practice, the actual fee charged to the client (as calculated by the nodes) will be less than the transactionFee that the client stipulated. Logically the client receives a refund of the excess between the calculated and maximum (though it was never actually charged the maximum fee).
After processing a transaction into consensus and applying it to state, all nodes will determine the appropriate fee and allocate it appropriately, so the client’s account will go down, while that of the node that submitted the transaction to the network, as well as the Hedera treasury, will go up.
Clients pay for transactions by stipulating a transactionFee on a transaction. Clients are protected against overpaying on transactions.
To guide them in determining what is an appropriate value for the transactionFee on different transactions, clients can:
- Use the Hedera Fee Estimator. The tool predicts the fee (in both USD and HBAR) for different transactions, based on the provided characteristics. The Fee Estimator simulates the same calculation the nodes perform when analyzing a transaction – even using the same fee schedule and exchange rate as used by the nodes.
Note: because they are protected against overpayment, clients should feel safe ‘rounding up’ the estimates from the above for transactions. Rounding up to a larger maximum transaction fee will insulate them from the risk of failed transactions due to any HBAR volatility, as described above.
Note: it could be tempting to set the MaxTransactionFee or TransactionFee extremely high. Doing so could result in INSUFFICIENT_BALANCE errors if the stipulated fee is higher than the payer’s balance, even if the eventual actual fee is less than the payer’s balance.
When a client sends a query to a node, by definition that query is not submitted to the rest of the network. A single node receives the query, looks at its local copy of state, and returns the appropriate data. Consequently, the above mechanism for paying a fee will not work as the rest of the network never sees the query (and so can’t assess its appropriate fee).
Instead, when constructing a query, a client includes within the query a distinct payment transaction by which the node gets paid for the above effort. This payment transaction will be submitted to the network, similar to any other cryptocurrency transfer, and with processing as for any other transaction as described above.
Critically, when the other nodes of the network are processing this payment transaction, they have no information about the query that motivated this payment. For these other nodes, this payment transaction appears like any other CryptoTransfer. As a result, the other nodes are unable to determine if this payment is greater than necessary for the original query.
Consequently, unlike for transactions, clients are not protected against overpayment on the payment transactions for queries. When processing a payment transaction, the network will fully reduce the client’s account by whatever value is stipulated in the payment transaction.
Clients pay for queries by including a distinct signed payment transaction in the query. It is this payment transaction that is submitted to, and processed by, the network in order to pay the node its fee for processing the query. Clients are NOT protected against overpaying on the value of these payment transactions for queries.
It is therefore important that a client be able to accurately predict the appropriate fee for a query (to be inserted in the payment transaction for that query).
To guide them in determining what is an appropriate value for the value of the payment transaction on different queries, clients can:
- Use the Hedera Fee Estimator. The tool predicts the fee (in both USD and HBAR) for different queries, based on the provided characteristics. The Fee Estimator simulates the same calculation the nodes perform when analyzing a query
Note: the current version of the Fee Estimator returns the total cost for a query, which is both the value of the payment transaction for the query AND the transactionFee for that payment transaction. Clients should NOT use this total value in the value of a payment transaction for a query, but rather the result of subtracting the cost of a simple HBAR transfer from this value.
Note: the Fee Estimator makes assumptions with respect to the characteristics of a query, for example, a query returning 20 records for an account will cost more than a query that returns 2 records. Likewise, a GetFileContents query will have an increasing cost as the size of the file itself increases. The accuracy of the estimated fee depends on how closely the inputs to the fee estimator match the actual physical characteristics of an actual query.
- Use the COST_ANSWER mechanism
Clients can send a query to a node but indicate that they don’t want the actual response, but rather the projected cost of the response (were they to send a real query). This COST_ANSWER query is free to the client. This mechanism gives to the client the most precise estimate of the appropriate value for the payment transaction for a query. As the node determining the cost will use the current exchange rate in that determination, clients can be certain that the returned cost will, when used in a payment transaction for a query, be neither an overpayment nor fail due to exchange rate volatility.
Note: the cost returned in response to a COST_ANSWER is based on the current exchange rate. If the exchange rate changes in the interim between the cost being returned and the client creating and sending a query using that projected cost value in the payment transaction, then the actual cost may change – causing either an error or an overpayment.
Note: the returned COST doesn’t include the transactionFee for the payment transaction for the query. As discussed above, this is different than the current implementation of the Fee Estimator. Of course, that transaction fee can be safely overestimated, and only the appropriate amount will be deducted.
The Java SDK provides a number of mechanisms that allow developers to control the risk of over and underpayment when constructing and sending transactions and queries.
As described above, transactions are paid for by the client providing a sufficiently large transactionFee on the transactions it creates and sends to the network. The Java SDK allows clients to control that value.
Client.setMaxTransactionFee() allows a dev to stipulate a maximum fee that will apply to all transactions created with that client. This value will be used in the transactionFee on transactions sent to the network.
This default can be overridden by .setTransactionFee() on a specific transaction.
Consequently, developers can use client.setMaxTransactionFee to set a default, but use .setTransactionFee() to override that default (higher or lower) .
Note: the record for a transaction will indicate the actual fee paid for that transaction. This may be informative for tuning the transaction fee for future transactions.
As described above, nodes will fully deduct the value of the payment transaction for a query from the payer’s account. Unlike for transactions, there is no network-level protection against overpayment for queries. Consequently, developers should protect themselves by using the constructs the SDK provides to both stipulate accurate payment values and to set maximum payments.
Developers can use client.setMaxQueryPayment() to set a maximum value for the payment transaction of any query run by this client. This will throw an exception if a query cost exceeds the specified value.
Additionally, developers can leverage the COST_ANSWER mechanism described above to ensure that they stipulate sufficient (but not excessive) payments for their queries. Before sending a query (with a payment), clients can first query for the cost of that actual query. This COST_ANSWER query is free to the client.
In the SDK, the above pattern could be implemented using query.setPaymentDefault(query.requestCost()). This code sends a COST_ANSWER query and then uses the returned cost in the creation of a signed payment transaction with that value.
It may be beneficial to add a small margin (5-10%) to the cost returned by the COST_ANSWER query when the query pertains to dynamic and changing data such as records, file contents and to account for exchange rate fluctuations. Indeed, the cost of a query may increase (or decrease) slightly between requesting for the cost and actually executing the query if new records are added to an account or a file is modified, or if the exchange rate changes in the interim.
Note: the node will return an INSUFFICIENT_TX_FEE error if either a) the value of the payment transaction, or b) the transactionFee on the payment transaction, is too low.
Note: a common pattern is that a client , after sending a transaction, will send a TransactionGetReceipt or TransactionGetRecord query to obtain confirmation of that transaction being processed into consensus.
TransactionGetReceipt is a free query, the payment transaction within can have zero value. The TransactionGetRecord query is not free and must be paid for with an appropriate payment transaction.
The following sample code shows the above SDK constructs in use.
A client will:
1. Send a CryptoTransfer transaction
2. Query for the receipt of the transaction of #1
3. Query for the cost of asking for a record for the transaction of #1
4. Query for the record of the transaction of #1 (with a payment informed by #3)