Skip to content

Transaction Fees

Blockchains like Algorand are decentralized networks with finite computational resources. Transaction fees serve as an essential economic mechanism to protect the network by requiring users to pay for the computational resources their transactions use. This fee structure protects Algorand against potential spam attacks that could overwhelm the system and prevent the blockchain from being trapped in infinite computational loops that compromise performance and security.

The base fee for each transaction on Algorand is 1,000 microAlgo (0.001 Algo), and it is fixed to this amount when the network is not congested.

The signature scheme that authorizes a transaction can add a contribution on top of the base fee. Post-quantum signatures are much larger than Ed25519 signatures, and a Falcon-1024 signature adds a contribution of 2× the base fee:

Authorized byMinimum fee
Ed25519 signature, multisignature, or logic signature1,000 microAlgo (0.001 Algo)
Falcon-1024 signature (including Falcon-delegated logic signatures)3,000 microAlgo (0.003 Algo)

The total fee of a transaction is calculated using the following formula where:

  • txn_in_bytes is the size of the transaction in bytes
  • fee_per_byte is the current network’s congestion-based fee per byte
  • min_fee is the minimum fee for a transaction
fee = max(current_fee_per_byte*len(txn_in_bytes), min_fee)

If the network is not congested, the current_fee_per_byte will be zero, and the minimum transaction fee will be used.

If the network is congested, the current_fee_per_byte will be non-zero. For a given transaction, if the product of the transaction’s size in bytes and the current fee per byte is greater than the minimum fee, the product is used as the fee. Otherwise, the minimum fee is used.

Transaction fees in Algorand apply uniformly across all transaction types—payments, Asset transfers, application calls, and others all use the same fee structure. Application call transaction fees also don’t vary based on the complexity of the smart contract code being executed. Only the size of the serialized transaction determines the fee.

The algod REST server provides suggestedParams that include the fee parameter which is the suggested current fee per byte. This suggested fee is used to determines transaction costs through this simple formula:

fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)

When the network isn’t congested, current_fee_per_byte equals zero, simplifying the formula to:

fee = max(0, min_fee) = min_fee

This is why standard Algorand transactions cost 0.001 ALGO or 1000 microAlgo during normal network conditions.

Here is an example of how to get suggested parameters using the Algorand Client.

/**
* Get the suggested parameters for a transaction
*
* Returns an object with the following attributes:
* - consensusVersion: The consensus version of the network
* - fee: The fee per byte for the transaction
* - firstValid: The first valid block round
* - flatFee: Whether the fee is a flat fee
* - genesisID: The genesis name of the network
* - genesisHash: The genesis hash of the network
* - lastValid: The last valid block round
* - minFee: The minimum fee for the transaction
*/
const sp = await algorand.getSuggestedParams()
console.log('Minimum Fee: ', sp.minFee)
console.log('Fee: ', sp.fee)
console.log('Flat Fee: ', sp.flatFee)

Algorand Client Suggested Params Configuration

Section titled “Algorand Client Suggested Params Configuration”

The Algorand Client caches suggested parameters provided by the network automatically to reduce network traffic. It has a set of default configurations that control this behavior, but the default configuration can be overridden and changed:

  • algorand.setDefaultValidityWindow(validityWindow) - Set the default validity window (number of rounds from the current known round that the transaction will be valid to be accepted for). Having a smallish value for this is usually ideal to avoid transactions that are valid for a long future period and may be submitted even after you think it failed to submit if waiting for a particular number of rounds for the transaction to be successfully submitted. The validity window defaults to 10, except in automated testing where it’s set to 1000 when targeting LocalNet.
  • algorand.setSuggestedParams(suggestedParams, until?) - Set the suggested network parameters to use (optionally until the given time)
  • algorand.setSuggestedParamsTimeout(timeout) - Set the timeout that is used to cache the suggested network parameters (by default 3 seconds)
  • algorand.getSuggestedParams() - Get the current suggested network parameters object, either the cached value, or if the cache has expired a fresh value

The suggested parameters include a flat_fee field that enables manual fee configuration. When set to true, you can specify exactly how much you want to pay for a transaction.

If you choose this method, ensure your specified fee meets at least the minimum transaction fee (min-fee) available in the suggested parameters through the Algorand Client.

  • Covering extra fees in transaction groups or app calls with inner transactions
  • Displaying specific rounded fees to users in applications
  • Preparing future transactions when network conditions are unknown
  • Handling non-urgent transactions that can be retried if rejected
/**
* Set a static fee to the transaction.
* This transaction will use 2000 microAlgo no matter the network conditions
* and may fail if the network is congested.
*/
await algorand.createTransaction.payment({
sender: randomAccountA,
receiver: randomAccountB,
amount: algos(1),
staticFee: microAlgos(2000), // set the fee to 2000 microAlgo
})
/**
* Or get the suggested parameters and directly configure the flat fee to true
* and set the fee to 2000 microAlgo.
*/
const sp2 = await algorand.getSuggestedParams()
sp2.flatFee = true
sp2.fee = 2000
// Cache the configured suggested parameters and use it for future transactions
algorand.setSuggestedParamsCache(sp2)

The Algorand protocol supports pooled fees, where one transaction can pay the fees of other transactions within the same atomic group.

For atomic transactions, the fees set on all transactions in the group are summed. This sum is compared against the protocol determined expected fee for the group and may proceed as long as the sum of the fees is greater than or equal to the required fee for the group.

Below is an example of setting a pooled fee on an atomic group of two transactions. Here transaction B’s fee is directly set to be 2x the minimum fee and transaction A’s fee is set to zero. This atomic group will successfully execute because the sum of the fees is greater than or equal to the required fee for the group.

// Transaction A will not pay any fees
const transactionA = await algorand.createTransaction.payment({
sender: randomAccountA,
receiver: randomAccountB,
amount: algos(2),
staticFee: algos(0), // fee is set to 0
})
// Transaction B has the extra fee field set to cover the fee of transactionA
const transactionB = await algorand.createTransaction.payment({
sender: randomAccountA,
receiver: randomAccountB,
amount: algos(1),
extraFee: microAlgos(
Math.max(
Number(sp.fee) * 1000, // estimating size as 1000 bytes
Number(sp.minFee),
),
), // extra fee covers the fee of transactionA
})
/**
* transactionA and transactionB are added to the same atomic group.
* The extra fee of transactionB covers the fee of transactionA.
* Therefore, the total fee sum for the group is enough to cover the fee of both transactions.
*/
const group = algorand.newGroup().addTransaction(transactionA).addTransaction(transactionB)
const result = await group.send()
console.log(result.txIds)

Certain transaction fields can exceed their standard size limits when the transaction pays an additional fee. Each byte over the standard limit adds a surcharge of 0.0001 of the minimum fee (0.1 microAlgo, consensus parameter PerByteTxnSurcharge) to the required fee for the transaction, so 1,000 extra bytes add 100 microAlgo, a tenth of the minimum fee. Like any other fee, the surcharge can be covered by another transaction in the same atomic group through fee pooling.

The following limits can be exceeded by paying the surcharge:

FieldStandard LimitAbsolute MaximumAbsolute Maximum Parameter
Note1,024 bytes4,096 bytesMaxAbsoluteTxnNoteBytes
Application arguments (combined)2,048 bytes16,384 bytesMaxAbsoluteTotalArgLen
Extra program pages3 pages7 pagesMaxAbsoluteExtraProgramPages
Logic signature program1,000 bytes per transaction, pooled across the group16,000 bytes in a single transactionMaxAbsoluteLogicSigProgramSize

Individual application arguments are limited to 4,096 bytes, and the argument count remains capped at 16.

Logic signature arguments are never charged. Arguments can be modified after signing by anyone holding the signed transaction, so the required fee cannot depend on them. Each logic signature can carry up to 1,000 bytes of arguments without charge; larger arguments must fit within the group’s ordinary logic signature size pool.

The surcharge buys size only. It does not increase the opcode budget or grant additional resource references. Large application programs also count against the group’s box read and write budgets; when a call fails for this reason, the algod simulate endpoint reports the additional empty box references required.

Inner transactions are transactions sent by an application account. When an account makes an application call transaction to a contract method with one inner transaction, there are two ways to cover the associated inner transaction fee.

  • App caller pays fees (recommended): The account calling the contract method pays for both the application call and the inner transaction fees through fee pooling. This approach is recommended because the inner transaction execution doesn’t depend on the application account’s ALGO balance.
  • App account pays fees (not recommended): The application account itself pays the inner transaction fee using its own ALGO balance. This approach is discouraged because repeated calls to the method could deplete the application’s ALGO balance, eventually resulting in “insufficient balance” errors that prevent the application from functioning.

Smart Contract Inner transactions may have their fees covered by the outer transactions, but they cannot cover outer transaction fees. This limitation that only outer transactions may cover the inner transactions is true in the case of nested smart contract inner transactions as well. For example, if Smart Contract A is called, which then calls Smart Contract B, which then calls Smart Contract C, then C’s fee can not cover the call for B, which can not cover the call to A.

Refer to the Inner Transactions page for code examples.

Inner transaction fees are fixed at 1,000 microAlgo per transaction, regardless of network congestion. To properly cover fees:

  • For one inner transaction: Add 1,000 microALGO to the outer transaction fee
  • For two inner transactions: Add 2,000 microALGO to the outer transaction fee
  • And so on for additional inner transactions

Here is an example of calling a smart contract with an inner transaction and covering the inner transaction fee with the outer transaction.

This is the payment contract method that will be called and it has one inner payment transaction.

const factory = algorand.client.getTypedAppFactory(InnerTransactionsFactory, {
defaultSender: randomAccountA.addr,
defaultSigner: randomAccountA.signer,
})
const { appClient } = await factory.deploy({
onUpdate: OnUpdate.ReplaceApp,
onSchemaBreak: OnSchemaBreak.Fail,
})
await algorand.account.ensureFunded(appClient.appAddress, dispenser, algos(1))
/**
* By setting `coverAppCallInnerTransactionFees` to true, this transaction will
* pay extra fees to cover the fees for any inner transactions in the contract method.
*/
await appClient.send.payment({ args: {}, coverAppCallInnerTransactionFees: true })
/**
* You can also set the `extraFee` in the method call parameters to cover the fees for
* one inner transaction in the contract method. This would fail if the contract method
* has more than one inner transaction.
*/
await appClient.send.payment({ args: {}, extraFee: microAlgos(1000) })

The algod simulate endpoint reports how much a transaction group is required to pay in fees. The response includes the following fields:

  • group-usage: the fee usage for the transaction group, including all of its inner transactions, expressed in millionths of the minimum fee
  • fees-paid: the total fees actually set on a transaction and its inner transactions, in microAlgo
  • group-fees-paid: the total fees actually set across the transaction group, in microAlgo

To determine the required fee for a group, simulate it first, then convert the reported usage using the min-fee value from the suggested parameters:

required_group_fee = group_usage * min_fee / 1000000

The result is rounded up to the next microAlgo, once for the whole group rather than per transaction. For example, a group-usage of 2,500,000 with a 1,000 microAlgo minimum fee means the group must pay 2,500 microAlgo in total fees.

Because fees are pooled, the full amount can be set on a single transaction in the group. Comparing group-fees-paid against the required fee shows whether the fees currently set on the group are sufficient. The reported usage includes any per-byte surcharge paid by larger transactions, the case where this kind of estimation matters most.

A simulation still fails when the fees on the group are too low, and a run that stops early underreports usage because inner transactions that never execute are not counted. Simulate with fees set generously high so the group runs to completion, then use the reported group-usage to set the actual fees before submitting.