Fees

Announcing transactions have 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
MIJIN_TEST cat.currency
TEST_NET 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 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 N blocks. 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://api-01.us-east-1.096x.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(),
        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://api-01.us-east-1.096x.symboldev.network:3000';
    const repositoryHttp = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
    const networkHttp = repositoryHttp.createNetworkRepository();
    const medianFeeMultiplier = (yield 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(), 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 to 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 requiredCosignatures = 1;
    const aggregateTransaction = AggregateTransaction
        .createBonded(
        Deadline.create(),
        [transferTransaction.toAggregate(publicAccount1),
            transferTransaction.toAggregate(publicAccount2)],
        NetworkType.TEST_NET,
        [])
    .setMaxFeeForAggregate(medianFeeMultiplier, 1);
    // Define transaction and set max fee
    const requiredCosignatures = 1;
    const aggregateTransaction = symbol_sdk_1.AggregateTransaction
        .createBonded(symbol_sdk_1.Deadline.create(), [transferTransaction.toAggregate(publicAccount1),
        transferTransaction.toAggregate(publicAccount2)], symbol_sdk_1.NetworkType.TEST_NET, [])
        .setMaxFeeForAggregate(medianFeeMultiplier, 1);

Rental fee

Accounts willing to register a namespace or a mosaic have to pay a rental fee in addition to the transaction fee. The effective rental fee is adjusted dynamically based on the median network multiplier over last maxDifficultyBlocks.

For more information, see how the network calculates the effective rental fee for mosaics and namespaces.

Continue: Receipt.