Fees

Announcing transactions has an associated cost. This cost is necessary to provide an incentive for the harvesters who secure the network and run the infrastructure.

Network currency

By default, fees are paid in the underlying currency of the Symbol network.

Default network currency per network type
Network type Mosaic name
PRIVATE cat.currency
MAINNET, TESTNET symbol.xym

Private chains can edit the network configuration to eliminate fees or use another mosaic that better suits their needs.

Transaction fee

The fee associated with a transaction primarily depends on the size (in bytes) of the transaction. The effective fee deducted from the account sending the transaction is calculated as the product of the size of the transaction and a fee multiplier set by the node that harvests the block.

\[effectiveFee = transaction::size * block::feeMultiplier\]

A node owner can configure the fee multiplier to all positive values, including zero. The fee_multiplier is stored in the block header when the node harvests a new block, determining which was the effective fee paid for every transaction included.

Before announcing the transaction, the sender must specify during the transaction definition a max_fee, indicating the maximum fee the account allows to spend for this transaction.

If the effective_fee is smaller or equal to the max_fee, a harvester could opt to include the transaction in the block. The harvesting nodes can set their transaction inclusion strategy:

  • Prefer-oldest: Preferred for networks with high transaction throughput requirements. Include first the oldest transactions.
  • Minimize-fees: Philanthropic nodes. Include first the transactions that other nodes do not want to include.
  • Maximize-fees: Most common in public networks. Include first transactions with higher fees.

To ensure that the transaction will get included without setting a max_fee unnecessarily high, the sender of the transaction can ask the REST Gateway for the median, average, highest, or lowest multiplier of the network over the last 60 blocks (maxDifficultyBlocks).

Note

A quick way of retrieving this information is pointing your browser to:

<NODE_URL>:3000/network/fees/transaction

For example, the sender could set the transaction max_fee as follows:

\[maxFee = transaction::size ∗ network::medianFeeMultiplier\]
  const publicAccount1 = Account.generateNewAccount(NetworkType.TEST_NET)
    .publicAccount;
  const publicAccount2 = Account.generateNewAccount(NetworkType.TEST_NET)
    .publicAccount;
  // Get median fee multiplier
  const nodeUrl = 'http://ngl-dual-101.testnet.symboldev.network:3000';
  const repositoryHttp = new RepositoryFactoryHttp(nodeUrl);
  const networkHttp = repositoryHttp.createNetworkRepository();
  const medianFeeMultiplier = (
    await networkHttp.getTransactionFees().toPromise()
  ).medianFeeMultiplier;

  // Define transaction and set max fee
  const rawAddress = 'TB6Q5E-YACWBP-CXKGIL-I6XWCH-DRFLTB-KUK34I-YJQ';
  const recipientAddress = Address.createFromRawAddress(rawAddress);
  const transferTransaction = TransferTransaction.create(
    Deadline.create(epochAdjustment),
    recipientAddress,
    [],
    PlainMessage.create('This is a test message'),
    NetworkType.TEST_NET,
  ).setMaxFee(medianFeeMultiplier);
    const publicAccount1 = symbol_sdk_1.Account.generateNewAccount(symbol_sdk_1.NetworkType.TEST_NET)
        .publicAccount;
    const publicAccount2 = symbol_sdk_1.Account.generateNewAccount(symbol_sdk_1.NetworkType.TEST_NET)
        .publicAccount;
    // Get median fee multiplier
    const nodeUrl = 'http://ngl-dual-101.testnet.symboldev.network:3000';
    const repositoryHttp = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
    const networkHttp = repositoryHttp.createNetworkRepository();
    const medianFeeMultiplier = (await networkHttp.getTransactionFees().toPromise()).medianFeeMultiplier;
    // Define transaction and set max fee
    const rawAddress = 'TB6Q5E-YACWBP-CXKGIL-I6XWCH-DRFLTB-KUK34I-YJQ';
    const recipientAddress = symbol_sdk_1.Address.createFromRawAddress(rawAddress);
    const transferTransaction = symbol_sdk_1.TransferTransaction.create(symbol_sdk_1.Deadline.create(epochAdjustment), recipientAddress, [], symbol_sdk_1.PlainMessage.create('This is a test message'), symbol_sdk_1.NetworkType.TEST_NET).setMaxFee(medianFeeMultiplier);

Note

It is not guaranteed that the transaction will get confirmed if the multiplier used is too low. To have better chances, the sender of the transaction could opt to use any value between medianNetworkFeeMultiplier and highestFeeMultiplier instead.

To determine an aggregate bonded transaction size, it is required to know beforehand the number of participant accounts that will need to cosign the transaction.

  // Define transaction and set max fee
  const aggregateTransaction = AggregateTransaction.createBonded(
    Deadline.create(epochAdjustment),
    [
      transferTransaction.toAggregate(publicAccount1),
      transferTransaction.toAggregate(publicAccount2),
    ],
    NetworkType.TEST_NET,
    [],
  ).setMaxFeeForAggregate(medianFeeMultiplier, 1);
    // Define transaction and set max fee
    const aggregateTransaction = symbol_sdk_1.AggregateTransaction.createBonded(symbol_sdk_1.Deadline.create(epochAdjustment), [
        transferTransaction.toAggregate(publicAccount1),
        transferTransaction.toAggregate(publicAccount2),
    ], symbol_sdk_1.NetworkType.TEST_NET, []).setMaxFeeForAggregate(medianFeeMultiplier, 1);

Dynamic fee multiplier

Each block added to the blockchain has a different fee multiplier, set by the node that harvested it. The network also defines the dynamic fee multiplier as the median of the last maxDifficultyBlocks harvested blocks (60 by default).

This value approximates the most common fee multiplier that nodes and transaction creators have agreed upon in the most recent blocks, and is used in the calculation of namespace and mosaic rental fees.

If a block did not include any transaction, a value of defaultDynamicFeeMultiplier (100 by default) is used to avoid 0 multipliers.

Note

The current value of the dynamic fee multiplier can be found in the medianFeeMultiplier property returned by the REST Gateway:

<NODE_URL>:3000/network/fees/transaction

Continue: Receipt.


Did you find what you were looking for? Give us your feedback.