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.
By default, fees are paid in the underlying currency of the Symbol network.
Network type |
Mosaic name |
---|---|
PRIVATE |
|
MAINNET, TESTNET |
|
Private chains can edit the network configuration to eliminate fees or use another mosaic that better suits their needs.
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.
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 /network/fees/transaction
For example, the sender could set the transaction max_fee as follows:
const publicAccount1 = Account.generateNewAccount(NetworkType.TEST_NET)
.publicAccount;
const publicAccount2 = Account.generateNewAccount(NetworkType.TEST_NET)
.publicAccount;
// Get median fee multiplier
const nodeUrl = 'NODE_URL';
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 = 'NODE_URL';
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);
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 /network/fees/transaction