From 5f0dbbd6ca01ae824d2e779383884768b62e3cf6 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Tue, 2 Sep 2025 16:32:45 +0100 Subject: [PATCH 01/21] Create ELIP 202 for issued asset fees Add controller descritpion Add publication descritpion Add test vectors fix typos --- elip-0202.mediawiki | 226 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 elip-0202.mediawiki diff --git a/elip-0202.mediawiki b/elip-0202.mediawiki new file mode 100644 index 0000000..c236371 --- /dev/null +++ b/elip-0202.mediawiki @@ -0,0 +1,226 @@ +
+  ELIP: 202
+  Layer: Applications
+  Title: Issued Asset Fees
+  Author: Tom Trevethan 
+  Comments-Summary: No comments yet.
+  Comments-URI: https://github.com/ElementsProject/elips/wiki/Comments:ELIP-0202
+  Status: Draft
+  Type: Standards Track
+  Created: 2025-09-02
+  License: BSD-3-Clause
+
+ +==Introduction== + +===Abstract=== + +This document proposes a modification to Elements to enable transaction fees to be paid in issued assets in addition to the policyAsset. To enable this, the specific issued assets that will be accepted and the corresponding fee rate, relative to the applied policyAsset rate must be agreed and configured by block creators and communicated to users and implemented in wallets. To enable the use of issued asset fees requires changes to policy rules for transaction relay and block creator mempool acceptance, and changes to block creation to enable issued asset fees to be paid to specified destinations in coinbase outputs. Issued asset fee acceptance and payout in the coinbase transaction is valid within the current consensus rules and so none of the features described here require any consensus fork to deploy. In addition to the core changes to policy and block creation, this document also proposes features to enable both publication of accepted assets and fee rates on-chain for use by wallets, and a mechanism to update the assets and rates accepted by nodes via an authenticated on-chain transaction. + +===Copyright=== + +This document is licensed under the 3-clause BSD license. + +===Motivation=== + +In Elements, transaction fees are required in order to prevent denial of service attacks and to prioritise transactions for limited blockspace. Unlike in Bitcoin, where transaction fees are determined from the difference between the total input and output values, in Elements transaction fees are specified by an explicit unblinded output (signified by an empty scriptPubKey) and input and output totals for all assets must equal. In the current Elements implementaion, policy rules for relay and mempool acceptance require that the assetID of this fee output be equal to the policyAsset. The current consensus rules however allow for any assetID for the fee asset (so long as the transaction is otherwise valid) as well as no fee output (however, a specified fee output with zero fee is not consensus valid). The current conseus rules also allow for coinbase outputs for any asset with values equal to or less than the total fee outputs for that asset for all transactions in a block, however the current Elements code will only create a block with a single spendable coinbase output for the total of policyAsset fees. + +These current restrictions limit the usefulness of the Elements platform for transacting in issued assets, as policyAsset is always then required for the payment of transaction fees. For example, for a Bitcoin sidechain (e.q. Liquid) there are many issued assets for tokensied currencies (e.g. USDT) which many users will use for making payments, however they must obtain an amount of the policyAsset (e.g. LBTC) before they can transact, and the fees will be priced in BTC instead of USD. This leads to a poor user experience when transacting in tokenised stablecoins, as a new user will be required to purchase LBTC before they are able to spend USDT that they have received in their wallet. In addition, the value of transaction fee required will not be explicit in terms of USDT and depend on the current USD value of BTC. By enabling fees to be paid in specified issued assets and at specified rates, user wallets can transact in an issued asset like USDT seamlessly, with predictable and transparent fees. + +In addition to enabling the issued asset transaction fees to be accepted, and payment of these fees in the coinbase, there needs to be a method for wallets and users to know which assets are accepted by block creators and at what rates. The simplest and most robust way of achieving this is with a publication of the accepted assets and rates by block creators on chain, in the coinbase - this removes the requirement for separate servers to publish and relay this data. + +Finally, a method is required to set the assets and rates that will be applied and enforced on relay/bridge and block creation nodes. These can be set with direct access to the node, either via the node configuration or the RPC interface. However, these assets and rates may need to be updated frequently, and it may be impractical and have security implications to have direct access to functionary and bridge nodes to perform these updates via the RPC interface. Therefore, a method to perform this update remotely via an on-chain transaction is also required. Individual nodes can then be configured with a 'controller' script that authenticates changes to the asset fee policy without requiring direct access to any individual nodes. + +==Design== + +===Overview=== + +We propose a modification to Elements to enable to use of transaction fees paid in specified issued assets in addition to the policyAsset at specified rates relative to the base policyAsset rate applied. This requires no changes to the consensus rules, and these changes can be deployed without requiring a network fork. The specified assets and rates will be applied to relay and mempool policy, and the fees will be paid to specified destinations in the coinbase transaction of created blocks. + +Each node has complete control over which assets and rates to accept as policy, however in practice all block creators and bridge nodes *should* agree to apply the same asset fee policy in order to provide a reliable service for users. Each node will be able to directly specify what assets and at what rate they will apply to policy. This can be set as part of the node configuration, specifying the assetID, the relative rate (and optionally the coinbase destination for the fee collection by the block creator) before initialisation, or set dynamically at runtime via a new RPC option. Note that if the consesus parameter c_mandatory_coinbase_destination is set to a non-empty script, then this cannot be overridden and all asset fee coinbase outputs must be paid to the c_mandatory_coinbase_destination script. In addition, a new RPC will be added to query the current assets, rates (and destinations) currently being applied by any node. + +The asset fee rate will be applied relative to whatever the current policyAsset rate (in sat/vbyte) is applied for a given transaction. The total transaction fee calculated for mempool/relay policy and block creation is: + + +vsize * policy_asset_fee_rate * issued_asset_fee_rate +. + +E.g. An issued asset is USDT. The current conversion between USDT and LBTC (policyAsset) is 100:1 (i.e. 100 USDT to one LBTC) the rate applied (issued_asset_fee_rate) would then be 100. Therefore, if the USDT asset is used for the fee in a transaction, they should pay 100 times the fee amount in LBTC for the equivalent value. If the current minimum fee rate is 1 vsat/byte in LBTC (i.e. the policy_asset_fee_rate), then the minimum fee output value required for an e.g. vsize = 257 transaction would be: 257 x 1 x 100 = 25700 base units of the USDT asset. + +A new configuration option will be added to enable any node that is creating blocks to publish the assets, rates (and optionally destination) they are applying to their own policy. This information will be encoded into an OP_RETURN output for each accepted asset and included in the coinbase transaction for blocks that they create every N blocks (where N is specified in the configuration). Wallets and users will then be able to read and decode the latest assets and rates being applied by block creators and use this information for determining fees when creating transactions. For ease in retrieving this, all nodes will cache the latest rates which can be accessed via a new RPC. This publication method assumes that all block creators are applying the same assets and rates to their policy, which they would need to agree upon. + +In order for the acceptance of issued asset and rates for fees to be reliable and predictable, all block creators and bridging nodes (and all relaying nodes) should be applying the same policy with the same list of accepted assets and rates. To coordinate this and apply changes to a large number of nodes is impractical, which may be required regularly as exchange rates change. To provide an alternative method for remotely modifying the accepted assets and rates, individual nodes can be configured to accept updates via an on-chain transaction - this way all block creator and bridge nodes can have their applied policy updated simultaneously without requiring direct access to the RPC interface. + +To enable this, each node can be configured with a specified scriptPubKey that authorises the policy update. This is called the 'controller' scriptPubKey (but it can be a multisig representing several entities that must agree to any update). The controller(s) create a transaction with the scriptPubKey as the first output (as well as spending from the same scriptPubKey), and this transaction contains an additional OP_RETURN output for each fee asset to be accepted, encoded with the assetID, rate (and optional destination). + +Each configured node detects the configured controller scriptPubKey in a confirmed transaction output. It then verifies that this transaction spends from an output with the same scriptPubKey (this authenticates the transaction by verifying input scriptPubKey witness) and decodes each of the OP_RETURN outputs in the transaction to update the node assetID and rate applied to policy (and destination if creating blocks and c_mandatory_coinbase_destination not used.) + +===Specification=== + +====Issued asset fees==== + +Accepted assets, rates (and optional destination) are stored in a vector assetFeeRates of CAssetFeeRate objects. +This object contains the CAsset asset ID, the int64_t fee rate and CScript fee destination. + +For each supported issued asset, there is specified fee rate (issued_asset_fee_rate). This is specified as a fixed precision number (the number of decimal places supported is specified by int64_t ASSET_RATE_SCALE_FACTOR = 100000000). The maximum possible rate is then std::numeric_limits::max() / ASSET_RATE_SCALE_FACTOR (9223372036854775807 / 100000000 = 92233720368). + +The fee asset and rate can be configured for an individual node with the option: + +-setfeeassetrate=: set the assetID:rate-script applied to relay and mempool policy for this node. Optional script to set destination for coinbase fees for each asset. If mandatory_coinbase_destination is set as a consensus rule, then this is not currently possible and will return an error. + +This can be repeated for additional fee assets and rates. The maximum number of fee assets allowed is set by MAX_ISSUED_ASSET_FEE_SIZE. + +A new accepted fee asset can be added on demand at runtime with the RPC: + +updatefeeassetrate add new assetID, rate, (optional) destination script to apply to node policy or update the rates of existing configured assets dynamically. + +getassetfeerate get all assetID, rate, script applied by this client. + +Note: In the current Elements, consensus enforces that any individual fee output, or the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could exceed this, as individual assets might have much larger issuances. Therefore, currently in BlockAssember the total value of fee outputs will not exceed MAX_MONEY when generating a new block. However, this may limit transaction capacity and fee revenue on the future. This limitation can be removed along with any hardfork enabling: https://github.com/ElementsProject/elements/pull/1476 + +====Fee rate publication==== + +Asset fee rates (and optional destination) encoded in coinbase OP_RETURN as follows: + +* 4 bytes: AFEE +* 32 bytes: asset ID +* 16 bytes: fee rate (uint_64 encoded) +* (up to) 35 bytes: fee destination script + +The maximum number of additional outputs is limited to MAX_ISSUED_ASSET_FEE_SIZE. The max size for transactions must be reduced by the additional space taken by the additional coinbase outputs. + +Config option: + +-writefeeassetrate=n Write issued asset fee rates accepted as policy to the coinbase of created blocks with interval n (default: 0). + +If n is 0 then fee assets and rates are not written. + +If n > 0 then any accepted assets and fee rates set via RPC or config are published every n blocks. +Any fee rates updated by setassetfeerate or controller script are not applied to policy until the end of an epoch. + +A new RPC getsignerfeerates retrieves the latest published accepted assets and rates (cached). +getassetfeerate has a single boolean argument to either retrieve the latest updated fee rates, or the rates applied in the current epoch. + +On node restart, the cached rates are updated from the latest publication. + +====Controller fee asset rate updates==== + +Feature for issued asset fees that enables an external controller to set issued asset fee policy via an onchain transaction, with asset fee rates encoded in OP_RETURN outputs. +Any node can be configured to apply issued asset fee policy that is set by a controller (this does not need to be a single entity, but can be defined by any multisig script). This is set with a config option: + +-confeeassetrate=scriptPubKey + +where scriptPubKey is the script that is used by the controller(s) to set the setting rate in a transaction. +-confeeassetrate cannot be set at the same time as -setfeeassetrate (will generate error). +A valid controller transaction must spend from an output where the scriptPubKey is also the same controller script (this is used as an authentication mechanism). + +The format for the OP_RETURN encoding is that same as that used for the fee rate publication. + +Python function to encode to OP_RETURN hex string: + + +def encode_asset_fee_rate(rate, asset_id, dest_script): + ASSET_RATE_SCALE_FACTOR = 10**8 # 8 decimal places + scaled_rate = round(rate * ASSET_RATE_SCALE_FACTOR) + # Convert to 8-byte hex string (16 characters) + encoded_rate = format(scaled_rate, '016x') + # Encode asset with rate and destination + return "afee" + asset_id + encoded_rate + dest_script + + +==Backwards Compatibility== + +No issues for backwards compatibility - all existing transactions with policy asset fees are unaffected by these features. No changes to consesus rules are required. + +==Test Vectors== + +* Configure issued asset with fee rate of issued_asset_fee_rate = 2 + +
-setfeeassetrate=e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0:2
+ +* Get configured fee asset rates + +
getassetfeerate
+ +Result: + +
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('2'), 'script': ''}]
+ +* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee at rate 2: + +** vsize: 257 + +** Fee: 514 + +
0200000001016f90831e415a738df732b62dd0b76e87bbe489096e43c892bf3dcbbfd173778d0000000000feffffff0301a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000002faf08000160014c5439a67eaa99a308719b544f327a942ae02e6a701a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000002f967de0016001467c8c91e8357e1e24d1721c4535a99608e47beb701a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee801000000000000020200006c0000000000024730440220031c1aa256e40a89cf622374e1ef281870a6abb6f588411800fe885f5195a49e02204f3c37cfe2318db87ac7ce4d7da70682e3477653f6222383ab72ea259a3b1fae0121029896a2fe9c710b8b71488b90038d3974144dc21c9c8750459162dc37d141459300000000000000
+ +* Update issued asset fee rate to 5 with RPC + +
updatefeeassetrate e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0 5
+ +* Get configured fee asset rates + +
getassetfeerate
+ +Result: + +
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('5'), 'script': ''}]
+ +* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee at rate 5: + +** vsize: 257 + +** Fee: 2570 + +
0200000001015ce5df8217dfa572d4539d0ae05178be26c27dbc43af35f6877f0160c8d329360100000000feffffff0301a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000001312d0000160014fc060cafa93c7f4d5e0963ba08c55540c28361bf01a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000001c830d4001600141f79c72bcdad5c20c17337c1f22c3b41f156c53d01a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000000000a0a00004100000000000247304402205bc1f97638e5b1932ba6df702e938bd1cfdc6da46a4db8511fe172f821f102c502206a6fbe66d81e89bd78b4157d58af15e8c940e0c0d797cbe91535fb31683e013601210291d9c10cf976e932db8ffe0f2bf1d9c1574ea3affe2e0f98ff15e1b328eb860300000000000000
+ +* Configuration for fee asset rate publication: + +
-writefeeassetrate=100
+ +
updatefeeassetrate b92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22 1.86
+
updatefeeassetrate af6e22da7b20de2da56c6726c4a036c73f777c8fb5bfc356892c01a9a3dd58d3 3.781
+ +Coinbase transaction: + +
0200000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401640101ffffffff0401230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000000002c6a2aafeeaf6e22da7b20de2da56c6726c4a036c73f777c8fb5bfc356892c01a9a3dd58d3000000008d1ba84001230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000000002c6a2aafeeb92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22000000001689592001230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b201000000012a05f200001976a9149d8419680d03a0ac4133beb9e097d5c9ff7babbc88ac01230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b201000000000000000000266a24aa21a9ed5aa9aac025ce73291c124e2ccfd44ae60837d1f20a8573318427075444287de800000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ +Output 1: + +
afeeaf6e22da7b20de2da56c6726c4a036c73f777c8fb5bfc356892c01a9a3dd58d3000000008d1ba840
+ +Output 2: + +
afeeb92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a220000000016895920
+ +* Controller transaction + +
+controler_script = "00145956bc071791295bfd5161ddb770aa536289b3e3"
+controller_addr = "ert1qt9ttcpchjy54hl23v8wmwu922d3gnvlrz7sqx7"
+private_key = "cP3N3Z3rMTo4L2gNRDWmzo8nGNyVNnWwFX9DRVPqwcPnMWE8Rn33"
+
+ +Configuration + +
-confeeassetrate=00145956bc071791295bfd5161ddb770aa536289b3e3
+ +Fee Asset setting: + +
asset_id = b92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22
+ +rate = 1.0 + +Encoded fee asset hex string: + +
afeeb92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a220000000005f5e100
+ +Controller transaction: + +
020000000001500d712c870c96c0bec05f41d765f4f5f203bd8d5b7ea6ef333b53b8c2c2d164000000006a47304402207f1b2c580ee052b8d0da06a139cd1e910ec93c8b11585d692646e6267b8354c4022045caab622e3adbae204263dfdc86e341fd9f3c013cbf2234fdea2c8ee2ba0d81012103ba4a2b1f401eb59e1e6b104f8043ce41b38b65bd24c10edb3df8863b0241e5afffffffff0301230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b201000000012a046b60001600145956bc071791295bfd5161ddb770aa536289b3e301230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000000002c6a2aafeeb92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a220000000005f5e10001230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b20100000000000186a0000000000000
+ +
getassetfeerate
+ +
[{'asset': 'b92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22', 'rate': 1, 'script': ''}]
+ + From 4936ec2312726d3f976df4ac8ee500f4c845ea48 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Tue, 2 Sep 2025 16:34:32 +0100 Subject: [PATCH 02/21] Edit README for elip 0202 Fix path --- README.mediawiki | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.mediawiki b/README.mediawiki index ca4c53d..c0c2fd1 100644 --- a/README.mediawiki +++ b/README.mediawiki @@ -74,6 +74,13 @@ Having an ELIP here does not make it a formally accepted standard until its stat | Kilian Rausch | Standards Track | Active +|- style="background-color: #ffcfcf" +| [[elip-0202.mediawiki|202]] +| Applications +| Issued asset fees +| Tom Trevethan +| Standards Track +| Draft |} From 9641693b88a3bb0c36e8a602c6ba84a8b625594a Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Tue, 16 Sep 2025 12:33:34 +0100 Subject: [PATCH 03/21] fixed errors and renamed rate to multiplier --- elip-0202.mediawiki | 78 ++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/elip-0202.mediawiki b/elip-0202.mediawiki index c236371..9099917 100644 --- a/elip-0202.mediawiki +++ b/elip-0202.mediawiki @@ -15,7 +15,7 @@ ===Abstract=== -This document proposes a modification to Elements to enable transaction fees to be paid in issued assets in addition to the policyAsset. To enable this, the specific issued assets that will be accepted and the corresponding fee rate, relative to the applied policyAsset rate must be agreed and configured by block creators and communicated to users and implemented in wallets. To enable the use of issued asset fees requires changes to policy rules for transaction relay and block creator mempool acceptance, and changes to block creation to enable issued asset fees to be paid to specified destinations in coinbase outputs. Issued asset fee acceptance and payout in the coinbase transaction is valid within the current consensus rules and so none of the features described here require any consensus fork to deploy. In addition to the core changes to policy and block creation, this document also proposes features to enable both publication of accepted assets and fee rates on-chain for use by wallets, and a mechanism to update the assets and rates accepted by nodes via an authenticated on-chain transaction. +This document proposes a modification to Elements to enable transaction fees to be paid in issued assets in addition to the policyAsset. To enable this, the specific issued assets that will be accepted and the corresponding fee rate, relative to the applied policyAsset rate must be agreed and configured by block creators and communicated to users and implemented in wallets. To enable the use of issued asset fees requires changes to policy rules for transaction relay and block creator mempool acceptance, and changes to block creation to enable issued asset fees to be paid to specified destinations in coinbase outputs. Issued asset fee acceptance and payout in the coinbase transaction is valid within the current consensus rules and so none of the features described here require any consensus fork to deploy. In addition to the core changes to policy and block creation, this document also proposes features to enable both publication of accepted assets and fee rates on-chain for use by wallets, and a mechanism to update the assets and rates accepted by nodes via an authenticated on-chain transaction. ===Copyright=== @@ -23,35 +23,37 @@ This document is licensed under the 3-clause BSD license. ===Motivation=== -In Elements, transaction fees are required in order to prevent denial of service attacks and to prioritise transactions for limited blockspace. Unlike in Bitcoin, where transaction fees are determined from the difference between the total input and output values, in Elements transaction fees are specified by an explicit unblinded output (signified by an empty scriptPubKey) and input and output totals for all assets must equal. In the current Elements implementaion, policy rules for relay and mempool acceptance require that the assetID of this fee output be equal to the policyAsset. The current consensus rules however allow for any assetID for the fee asset (so long as the transaction is otherwise valid) as well as no fee output (however, a specified fee output with zero fee is not consensus valid). The current conseus rules also allow for coinbase outputs for any asset with values equal to or less than the total fee outputs for that asset for all transactions in a block, however the current Elements code will only create a block with a single spendable coinbase output for the total of policyAsset fees. +In Elements, transaction fees are required in order to prevent denial of service attacks and to prioritise transactions for limited blockspace. Unlike in Bitcoin, where transaction fees are determined from the difference between the total input and output values, in Elements transaction fees are specified by an explicit unblinded output (signified by an empty scriptPubKey) and input and output totals for all assets must equal. In the current Elements implementaion, policy rules for relay and mempool acceptance require that the assetID of this fee output be equal to the policyAsset. The current consensus rules however allow for any assetID for the fee asset (so long as the transaction is otherwise valid) as well as no fee output (however, a specified fee output with zero fee is not consensus valid). The current consensus rules also allow for coinbase outputs for any asset with values equal to or less than the total fee outputs for that asset for all transactions in a block. However the current Elements code will only create a block with a single spendable coinbase output for the total of policyAsset fees. -These current restrictions limit the usefulness of the Elements platform for transacting in issued assets, as policyAsset is always then required for the payment of transaction fees. For example, for a Bitcoin sidechain (e.q. Liquid) there are many issued assets for tokensied currencies (e.g. USDT) which many users will use for making payments, however they must obtain an amount of the policyAsset (e.g. LBTC) before they can transact, and the fees will be priced in BTC instead of USD. This leads to a poor user experience when transacting in tokenised stablecoins, as a new user will be required to purchase LBTC before they are able to spend USDT that they have received in their wallet. In addition, the value of transaction fee required will not be explicit in terms of USDT and depend on the current USD value of BTC. By enabling fees to be paid in specified issued assets and at specified rates, user wallets can transact in an issued asset like USDT seamlessly, with predictable and transparent fees. +These current restrictions limit the usefulness of the Elements platform for transacting in issued assets, as policyAsset is always then required for the payment of transaction fees. For example, for a Bitcoin sidechain (e.q. Liquid) there are many issued assets for tokenized currencies (e.g. USDT) which many users will use for making payments. However they must obtain an amount of the policyAsset (e.g. LBTC) before they can transact, and the fees will be priced in BTC instead of USD. This leads to a poor user experience when transacting in tokenized stablecoins, as a new user will be required to purchase LBTC before they are able to spend USDT that they have received in their wallet. In addition, the value of transaction fee required will not be explicit in terms of USDT and depend on the current USD value of BTC. By enabling fees to be paid in specified issued assets and at specified rates, user wallets can transact in an issued asset like USDT seamlessly, with predictable and transparent fees. -In addition to enabling the issued asset transaction fees to be accepted, and payment of these fees in the coinbase, there needs to be a method for wallets and users to know which assets are accepted by block creators and at what rates. The simplest and most robust way of achieving this is with a publication of the accepted assets and rates by block creators on chain, in the coinbase - this removes the requirement for separate servers to publish and relay this data. +In addition to enabling the issued asset transaction fees to be accepted, and payment of these fees in the coinbase, there needs to be a method for wallets and users to know which assets are accepted by block creators and at what rates. The simplest and most robust way of achieving this is with a publication of the accepted assets and rates by block creators on chain, in the coinbase - this removes the requirement for separate servers to publish and relay this data. -Finally, a method is required to set the assets and rates that will be applied and enforced on relay/bridge and block creation nodes. These can be set with direct access to the node, either via the node configuration or the RPC interface. However, these assets and rates may need to be updated frequently, and it may be impractical and have security implications to have direct access to functionary and bridge nodes to perform these updates via the RPC interface. Therefore, a method to perform this update remotely via an on-chain transaction is also required. Individual nodes can then be configured with a 'controller' script that authenticates changes to the asset fee policy without requiring direct access to any individual nodes. +Finally, a method is required to set the assets and rates that will be applied and enforced on relay/bridge and block creation nodes. These can be set with direct access to the node, either via the node configuration or the RPC interface. However, these assets and rates may need to be updated frequently, and it may be impractical and have security implications to have direct access to functionary and bridge nodes to perform these updates via the RPC interface. Therefore, a method to perform this update remotely via an on-chain transaction is also required. Individual nodes can then be configured with a 'controller' script that authenticates changes to the asset fee policy without requiring direct access to any individual nodes. ==Design== ===Overview=== -We propose a modification to Elements to enable to use of transaction fees paid in specified issued assets in addition to the policyAsset at specified rates relative to the base policyAsset rate applied. This requires no changes to the consensus rules, and these changes can be deployed without requiring a network fork. The specified assets and rates will be applied to relay and mempool policy, and the fees will be paid to specified destinations in the coinbase transaction of created blocks. +We propose a modification to Elements to enable to use of transaction fees paid in specified issued assets in addition to the policyAsset at specified rates relative to the current policyAsset rate applied. This requires no changes to the consensus rules, and these changes can be deployed without requiring a network fork. The specified assets and rates will be applied to relay and mempool policy, and the fees will be paid to specified destinations in the coinbase transaction of created blocks. -Each node has complete control over which assets and rates to accept as policy, however in practice all block creators and bridge nodes *should* agree to apply the same asset fee policy in order to provide a reliable service for users. Each node will be able to directly specify what assets and at what rate they will apply to policy. This can be set as part of the node configuration, specifying the assetID, the relative rate (and optionally the coinbase destination for the fee collection by the block creator) before initialisation, or set dynamically at runtime via a new RPC option. Note that if the consesus parameter c_mandatory_coinbase_destination is set to a non-empty script, then this cannot be overridden and all asset fee coinbase outputs must be paid to the c_mandatory_coinbase_destination script. In addition, a new RPC will be added to query the current assets, rates (and destinations) currently being applied by any node. +Each node has complete control over which assets and rates to accept as policy, however in practice all block creators and relaying nodes *should* agree to apply the same asset fee policy in order to provide a reliable service for users. Each node will be able to directly specify what assets and at what rate they will apply to policy. This can be set as part of the node configuration, specifying the assetID, the relative rate (and optionally the coinbase destination for the fee collection by the block creator) before initialisation, or set dynamically at runtime via a new RPC option. Note that if the consesus parameter c_mandatory_coinbase_destination is set to a non-empty script, then this cannot be overridden and all asset fee coinbase outputs must be paid to the c_mandatory_coinbase_destination script. In addition, a new RPC will be added to query the current assets, rates (and destinations) currently being applied by any node. -The asset fee rate will be applied relative to whatever the current policyAsset rate (in sat/vbyte) is applied for a given transaction. The total transaction fee calculated for mempool/relay policy and block creation is: +The asset fee rate will be applied relative the current policyAsset minimum rate (in sat/vbyte) that is applied for a given transaction (or package) for mempool acceptance and relay. The package fee rate calculated for minimum mempool and relay policy is: -vsize * policy_asset_fee_rate * issued_asset_fee_rate -. +policy_asset_fee_rate * issued_asset_fee_rate_multiplier / ASSET_RATE_SCALE_FACTOR +
+ +The fee rate multiplier is specified as a fixed precision number (the number of decimal places supported is specified by the int64_t parameter ASSET_RATE_SCALE_FACTOR (default 100000000) which is applied to all asset rate multipliers. The maximum possible rate is then std::numeric_limits::max() / ASSET_RATE_SCALE_FACTOR (9223372036854775807 / 100000000 = 92233720368). -E.g. An issued asset is USDT. The current conversion between USDT and LBTC (policyAsset) is 100:1 (i.e. 100 USDT to one LBTC) the rate applied (issued_asset_fee_rate) would then be 100. Therefore, if the USDT asset is used for the fee in a transaction, they should pay 100 times the fee amount in LBTC for the equivalent value. If the current minimum fee rate is 1 vsat/byte in LBTC (i.e. the policy_asset_fee_rate), then the minimum fee output value required for an e.g. vsize = 257 transaction would be: 257 x 1 x 100 = 25700 base units of the USDT asset. +E.g. An issued asset is USDT. The current conversion between USDT and LBTC (policyAsset) is 100:1 (i.e. 100 USDT to one LBTC) the rate multiplier applied (issued_asset_fee_rate_multiplier) would then be set to 100*ASSET_RATE_SCALE_FACTOR. If the USDT asset is used for the fee in any transaction, they should pay 100 times in USDT the fee rate that would have applied in LBTC. If the current minimum fee rate for mempool and relay is 1 vsat/byte in LBTC (i.e. the policy_asset_fee_rate), then the minimum fee output value required for a transaction of size e.g. vsize = 257 transaction would be: 257 x 1 x 100 = 25700 base units of the USDT asset. -A new configuration option will be added to enable any node that is creating blocks to publish the assets, rates (and optionally destination) they are applying to their own policy. This information will be encoded into an OP_RETURN output for each accepted asset and included in the coinbase transaction for blocks that they create every N blocks (where N is specified in the configuration). Wallets and users will then be able to read and decode the latest assets and rates being applied by block creators and use this information for determining fees when creating transactions. For ease in retrieving this, all nodes will cache the latest rates which can be accessed via a new RPC. This publication method assumes that all block creators are applying the same assets and rates to their policy, which they would need to agree upon. +A new configuration option will be added to enable any node that is creating blocks to publish the assets, rates (and optionally destination) they are applying to their own policy. This information will be encoded into an OP_RETURN output for each accepted asset and included in the coinbase transaction for blocks that they create every N blocks (where N is specified in the configuration). Wallets and users will then be able to read and decode the latest assets and rates being applied by block creators and use this information for determining fees when creating transactions. For ease in retrieving this, all nodes will cache the latest rates which can be accessed via a new RPC. This publication method assumes that all block creators are applying the same assets and rates to their policy, which they would need to agree upon. -In order for the acceptance of issued asset and rates for fees to be reliable and predictable, all block creators and bridging nodes (and all relaying nodes) should be applying the same policy with the same list of accepted assets and rates. To coordinate this and apply changes to a large number of nodes is impractical, which may be required regularly as exchange rates change. To provide an alternative method for remotely modifying the accepted assets and rates, individual nodes can be configured to accept updates via an on-chain transaction - this way all block creator and bridge nodes can have their applied policy updated simultaneously without requiring direct access to the RPC interface. +In order for the acceptance of issued asset and rates for fees to be reliable and predictable, all block creators and bridging nodes (and all relaying nodes) should be applying the same policy with the same list of accepted assets and rates. To coordinate this and apply changes to a large number of nodes is impractical, which may be required regularly as exchange rates change. To provide an alternative method for remotely modifying the accepted assets and rates, individual nodes can be configured to accept updates via an on-chain transaction - this way all block creator and bridge nodes can have their applied policy updated simultaneously without requiring direct access to the RPC interface. -To enable this, each node can be configured with a specified scriptPubKey that authorises the policy update. This is called the 'controller' scriptPubKey (but it can be a multisig representing several entities that must agree to any update). The controller(s) create a transaction with the scriptPubKey as the first output (as well as spending from the same scriptPubKey), and this transaction contains an additional OP_RETURN output for each fee asset to be accepted, encoded with the assetID, rate (and optional destination). +To enable this, each node can be configured with a specified scriptPubKey that authorises the policy update. This is called the 'controller' scriptPubKey (but it can be a multisig representing several entities that must agree to any update). The controller(s) create a transaction with the scriptPubKey as the first output (as well as spending from the same scriptPubKey), and this transaction contains an additional OP_RETURN output for each fee asset to be accepted, encoded with the assetID, rate (and optional destination). Each configured node detects the configured controller scriptPubKey in a confirmed transaction output. It then verifies that this transaction spends from an output with the same scriptPubKey (this authenticates the transaction by verifying input scriptPubKey witness) and decodes each of the OP_RETURN outputs in the transaction to update the node assetID and rate applied to policy (and destination if creating blocks and c_mandatory_coinbase_destination not used.) @@ -59,16 +61,16 @@ Each configured node detects the configured controller scriptPubKey ====Issued asset fees==== -Accepted assets, rates (and optional destination) are stored in a vector assetFeeRates of CAssetFeeRate objects. -This object contains the CAsset asset ID, the int64_t fee rate and CScript fee destination. +Accepted assets, rate multipliers (and optional destination) are stored in a vector assetFeeRates of CAssetFeeRate objects. +This object contains the CAsset asset ID, the int64_t fee rate multiplier and CScript fee destination. -For each supported issued asset, there is specified fee rate (issued_asset_fee_rate). This is specified as a fixed precision number (the number of decimal places supported is specified by int64_t ASSET_RATE_SCALE_FACTOR = 100000000). The maximum possible rate is then std::numeric_limits::max() / ASSET_RATE_SCALE_FACTOR (9223372036854775807 / 100000000 = 92233720368). +For each supported issued asset, there is specified fee rate multiplier (issued_asset_fee_rate_multiplier). The fee asset and rate can be configured for an individual node with the option: --setfeeassetrate=: set the assetID:rate-script applied to relay and mempool policy for this node. Optional script to set destination for coinbase fees for each asset. If mandatory_coinbase_destination is set as a consensus rule, then this is not currently possible and will return an error. +-setfeeassetrate=: set the assetID:rate-script applied to relay and mempool policy for this node. Optional script to set destination for coinbase fees for each asset. If mandatory_coinbase_destination is set as a consensus rule, then this is not currently possible and will return an error. -This can be repeated for additional fee assets and rates. The maximum number of fee assets allowed is set by MAX_ISSUED_ASSET_FEE_SIZE. +This can be repeated for additional fee assets and rates. The maximum number of issued assets fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE. A new accepted fee asset can be added on demand at runtime with the RPC: @@ -76,6 +78,8 @@ A new accepted fee asset can be added on demand at runtime with the RPC: getassetfeerate get all assetID, rate, script applied by this client. +All fee rate multipliers are input and output as floating point numbers, scalled with ASSET_RATE_SCALE_FACTOR to/from the internal int64_t issued_asset_fee_rate_multiplier. + Note: In the current Elements, consensus enforces that any individual fee output, or the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could exceed this, as individual assets might have much larger issuances. Therefore, currently in BlockAssember the total value of fee outputs will not exceed MAX_MONEY when generating a new block. However, this may limit transaction capacity and fee revenue on the future. This limitation can be removed along with any hardfork enabling: https://github.com/ElementsProject/elements/pull/1476 ====Fee rate publication==== @@ -84,44 +88,44 @@ Asset fee rates (and optional destination) encoded in coinbase OP_RETURNAFEE * 32 bytes: asset ID -* 16 bytes: fee rate (uint_64 encoded) +* 16 bytes: fee rate multiplier (uint_64 encoded) * (up to) 35 bytes: fee destination script -The maximum number of additional outputs is limited to MAX_ISSUED_ASSET_FEE_SIZE. The max size for transactions must be reduced by the additional space taken by the additional coinbase outputs. +The maximum number of additional outputs is limited to MAX_ISSUED_ASSET_FEE_SIZE. The maximum size for transactions included in a generated block must be reduced by the additional space taken by the additional coinbase outputs. Config option: --writefeeassetrate=n Write issued asset fee rates accepted as policy to the coinbase of created blocks with interval n (default: 0). +-writefeeassetrate=n Write issued asset fee rate multipliers accepted as policy to the coinbase of created blocks with interval n (default: 0). -If n is 0 then fee assets and rates are not written. +If n is 0 then fee assets and rate multipliers are not written. If n > 0 then any accepted assets and fee rates set via RPC or config are published every n blocks. Any fee rates updated by setassetfeerate or controller script are not applied to policy until the end of an epoch. -A new RPC getsignerfeerates retrieves the latest published accepted assets and rates (cached). -getassetfeerate has a single boolean argument to either retrieve the latest updated fee rates, or the rates applied in the current epoch. +A new RPC getsignerfeerates retrieves the latest published accepted assets and rate multipliers (cached). +getassetfeerate has a single boolean argument to either retrieve the latest updated fee rate multipliers, or the rates applied in the current epoch. -On node restart, the cached rates are updated from the latest publication. +On node restart, the cached rates are updated from the latest publication. ====Controller fee asset rate updates==== -Feature for issued asset fees that enables an external controller to set issued asset fee policy via an onchain transaction, with asset fee rates encoded in OP_RETURN outputs. +Feature for issued asset fees that enables an external controller to set issued asset fee policy via an onchain transaction, with asset fee rate multipliers encoded in OP_RETURN outputs. Any node can be configured to apply issued asset fee policy that is set by a controller (this does not need to be a single entity, but can be defined by any multisig script). This is set with a config option: -confeeassetrate=scriptPubKey -where scriptPubKey is the script that is used by the controller(s) to set the setting rate in a transaction. --confeeassetrate cannot be set at the same time as -setfeeassetrate (will generate error). +where scriptPubKey is the script that is used by the controller(s) to set the asset and rate multipliers in a transaction. +-confeeassetrate cannot be set at the same time as -setfeeassetrate (this will generate an error). A valid controller transaction must spend from an output where the scriptPubKey is also the same controller script (this is used as an authentication mechanism). -The format for the OP_RETURN encoding is that same as that used for the fee rate publication. +The format for the OP_RETURN encoding is that same as that used for the fee rate publication. Python function to encode to OP_RETURN hex string: -def encode_asset_fee_rate(rate, asset_id, dest_script): +def encode_asset_fee_rate(rate_multiplier, asset_id, dest_script): ASSET_RATE_SCALE_FACTOR = 10**8 # 8 decimal places - scaled_rate = round(rate * ASSET_RATE_SCALE_FACTOR) + scaled_rate = round(rate_multiplier * ASSET_RATE_SCALE_FACTOR) # Convert to 8-byte hex string (16 characters) encoded_rate = format(scaled_rate, '016x') # Encode asset with rate and destination @@ -130,11 +134,11 @@ def encode_asset_fee_rate(rate, asset_id, dest_script): ==Backwards Compatibility== -No issues for backwards compatibility - all existing transactions with policy asset fees are unaffected by these features. No changes to consesus rules are required. +No issues for backwards compatibility - all existing transactions with policy asset fees are unaffected by these features. No changes to consesus rules are required. ==Test Vectors== -* Configure issued asset with fee rate of issued_asset_fee_rate = 2 +* Configure issued asset with fee rate of issued_asset_fee_rate_multiplier = 2
-setfeeassetrate=e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0:2
@@ -146,7 +150,7 @@ Result:
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('2'), 'script': ''}]
-* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee at rate 2: +* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee multiplier at rate 2: ** vsize: 257 @@ -154,7 +158,7 @@ Result:
0200000001016f90831e415a738df732b62dd0b76e87bbe489096e43c892bf3dcbbfd173778d0000000000feffffff0301a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000002faf08000160014c5439a67eaa99a308719b544f327a942ae02e6a701a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000002f967de0016001467c8c91e8357e1e24d1721c4535a99608e47beb701a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee801000000000000020200006c0000000000024730440220031c1aa256e40a89cf622374e1ef281870a6abb6f588411800fe885f5195a49e02204f3c37cfe2318db87ac7ce4d7da70682e3477653f6222383ab72ea259a3b1fae0121029896a2fe9c710b8b71488b90038d3974144dc21c9c8750459162dc37d141459300000000000000
-* Update issued asset fee rate to 5 with RPC +* Update issued asset fee rate multiplier to 5 with RPC
updatefeeassetrate e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0 5
@@ -166,7 +170,7 @@ Result:
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('5'), 'script': ''}]
-* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee at rate 5: +* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee at rate multiplier 5: ** vsize: 257 From 4b5eeb206931975787cb5d67114ecd4ed8d1557c Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Sat, 20 Sep 2025 17:25:15 +0100 Subject: [PATCH 04/21] updated fee rate multiplier logic and limits --- elip-0202.mediawiki | 92 +++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/elip-0202.mediawiki b/elip-0202.mediawiki index 9099917..5110350 100644 --- a/elip-0202.mediawiki +++ b/elip-0202.mediawiki @@ -15,7 +15,7 @@ ===Abstract=== -This document proposes a modification to Elements to enable transaction fees to be paid in issued assets in addition to the policyAsset. To enable this, the specific issued assets that will be accepted and the corresponding fee rate, relative to the applied policyAsset rate must be agreed and configured by block creators and communicated to users and implemented in wallets. To enable the use of issued asset fees requires changes to policy rules for transaction relay and block creator mempool acceptance, and changes to block creation to enable issued asset fees to be paid to specified destinations in coinbase outputs. Issued asset fee acceptance and payout in the coinbase transaction is valid within the current consensus rules and so none of the features described here require any consensus fork to deploy. In addition to the core changes to policy and block creation, this document also proposes features to enable both publication of accepted assets and fee rates on-chain for use by wallets, and a mechanism to update the assets and rates accepted by nodes via an authenticated on-chain transaction. +This document proposes a modification to Elements to enable transaction fees to be paid in issued assets in addition to the policyAsset. To enable this, the specific issued assets that will be accepted and the corresponding fee rate, relative to the applied policyAsset rate must be agreed and configured by block creators and communicated to users and implemented in wallets. To enable the use of issued asset fees requires changes to policy rules for transaction relay mempool acceptance and ordering, and changes to block creation to enable issued asset fees to be paid to specified destinations in coinbase outputs. Issued asset fee acceptance and payout in the coinbase transaction is valid within the current consensus rules and so none of the features described here require any consensus fork to deploy. In addition to the core changes to policy and block creation, this document also proposes features to enable both publication of accepted assets and fee rates on-chain for use by wallets, and a mechanism to update the assets and rates accepted by nodes via an authenticated on-chain transaction. ===Copyright=== @@ -23,7 +23,7 @@ This document is licensed under the 3-clause BSD license. ===Motivation=== -In Elements, transaction fees are required in order to prevent denial of service attacks and to prioritise transactions for limited blockspace. Unlike in Bitcoin, where transaction fees are determined from the difference between the total input and output values, in Elements transaction fees are specified by an explicit unblinded output (signified by an empty scriptPubKey) and input and output totals for all assets must equal. In the current Elements implementaion, policy rules for relay and mempool acceptance require that the assetID of this fee output be equal to the policyAsset. The current consensus rules however allow for any assetID for the fee asset (so long as the transaction is otherwise valid) as well as no fee output (however, a specified fee output with zero fee is not consensus valid). The current consensus rules also allow for coinbase outputs for any asset with values equal to or less than the total fee outputs for that asset for all transactions in a block. However the current Elements code will only create a block with a single spendable coinbase output for the total of policyAsset fees. +In Elements, transaction fees are required in order to prevent denial of service attacks and to prioritize transactions for limited block space. Unlike in Bitcoin, where transaction fees are determined from the difference between the total input and output values, in Elements transaction fees are specified by an explicit unblinded output (signified by an empty scriptPubKey) and input and output totals for all assets must equal. In the current Elements implementation, policy rules for relay and mempool acceptance require that the assetID of this fee output be equal to the policyAsset. The current consensus rules however allow for any assetID for the fee asset (so long as the transaction is otherwise valid) as well as no fee output (however, a specified fee output with zero fee is not consensus valid). The current consensus rules also allow for coinbase outputs for any asset with values equal to or less than the total fee outputs for that asset for all transactions in a block. However the current Elements code will only create a block with a single spendable coinbase output for the total of policyAsset fees. These current restrictions limit the usefulness of the Elements platform for transacting in issued assets, as policyAsset is always then required for the payment of transaction fees. For example, for a Bitcoin sidechain (e.q. Liquid) there are many issued assets for tokenized currencies (e.g. USDT) which many users will use for making payments. However they must obtain an amount of the policyAsset (e.g. LBTC) before they can transact, and the fees will be priced in BTC instead of USD. This leads to a poor user experience when transacting in tokenized stablecoins, as a new user will be required to purchase LBTC before they are able to spend USDT that they have received in their wallet. In addition, the value of transaction fee required will not be explicit in terms of USDT and depend on the current USD value of BTC. By enabling fees to be paid in specified issued assets and at specified rates, user wallets can transact in an issued asset like USDT seamlessly, with predictable and transparent fees. @@ -35,25 +35,19 @@ Finally, a method is required to set the assets and rates that will be applied a ===Overview=== -We propose a modification to Elements to enable to use of transaction fees paid in specified issued assets in addition to the policyAsset at specified rates relative to the current policyAsset rate applied. This requires no changes to the consensus rules, and these changes can be deployed without requiring a network fork. The specified assets and rates will be applied to relay and mempool policy, and the fees will be paid to specified destinations in the coinbase transaction of created blocks. +We propose a modification to Elements to enable to use of transaction fees paid in specified issued assets in addition to the policyAsset at specified rates relative to the current policyAsset rate applied. This requires no changes to the consensus rules, and these changes can be deployed without requiring a network fork. The specified assets and rates will be applied to relay and mempool policy, mempool ordering, and these fees will be paid to specified destinations in the coinbase transaction of created blocks. -Each node has complete control over which assets and rates to accept as policy, however in practice all block creators and relaying nodes *should* agree to apply the same asset fee policy in order to provide a reliable service for users. Each node will be able to directly specify what assets and at what rate they will apply to policy. This can be set as part of the node configuration, specifying the assetID, the relative rate (and optionally the coinbase destination for the fee collection by the block creator) before initialisation, or set dynamically at runtime via a new RPC option. Note that if the consesus parameter c_mandatory_coinbase_destination is set to a non-empty script, then this cannot be overridden and all asset fee coinbase outputs must be paid to the c_mandatory_coinbase_destination script. In addition, a new RPC will be added to query the current assets, rates (and destinations) currently being applied by any node. +Each node has complete control over which assets and at what rates to accept as policy, however in practice all block creators and relaying nodes *should* agree to apply the same asset fee policy in order to provide a reliable service for users. Each node will be able to directly specify what assets and at what rate they will apply to policy. This can be set as part of the node configuration, specifying the assetID, the relative rate multiplier (and optionally the coinbase destination for the fee collection by the block creator) before initialization, or set dynamically at runtime via a new RPC option. Note that if the consensus parameter c_mandatory_coinbase_destination is set to a non-empty script, then this cannot be overridden and all asset fee coinbase outputs must be paid to the c_mandatory_coinbase_destination script. In addition, a new RPC will be added to query the current assets, rate multiplier (and destinations) currently being applied by any node. -The asset fee rate will be applied relative the current policyAsset minimum rate (in sat/vbyte) that is applied for a given transaction (or package) for mempool acceptance and relay. The package fee rate calculated for minimum mempool and relay policy is: +For fees outputs in an issued asset, the fee rate (in units/vbyte) will be scaled by a fee rate multiplier to determine an 'effective rate'. This effective rate is then used in place of the policyAsset fee rate for the purpose of meeting relay and mempool minimums and for mempool ordering. - -policy_asset_fee_rate * issued_asset_fee_rate_multiplier / ASSET_RATE_SCALE_FACTOR - - -The fee rate multiplier is specified as a fixed precision number (the number of decimal places supported is specified by the int64_t parameter ASSET_RATE_SCALE_FACTOR (default 100000000) which is applied to all asset rate multipliers. The maximum possible rate is then std::numeric_limits::max() / ASSET_RATE_SCALE_FACTOR (9223372036854775807 / 100000000 = 92233720368). - -E.g. An issued asset is USDT. The current conversion between USDT and LBTC (policyAsset) is 100:1 (i.e. 100 USDT to one LBTC) the rate multiplier applied (issued_asset_fee_rate_multiplier) would then be set to 100*ASSET_RATE_SCALE_FACTOR. If the USDT asset is used for the fee in any transaction, they should pay 100 times in USDT the fee rate that would have applied in LBTC. If the current minimum fee rate for mempool and relay is 1 vsat/byte in LBTC (i.e. the policy_asset_fee_rate), then the minimum fee output value required for a transaction of size e.g. vsize = 257 transaction would be: 257 x 1 x 100 = 25700 base units of the USDT asset. +E.g. An issued asset is USDT. The current conversion rate between USDT and LBTC (policyAsset) is 100:1 (i.e. 100 USDT to one LBTC) the issued asset fee rate multiplier would then be set to 0.01 (i.e. the fee paid in USDT will be multiplied by 0.01 for comparison with LBTC fees). If the USDT asset is used for the fee in any transaction, it should pay 100 times in USDT base units the fee rate that would have applied in LBTC (the policyAsset). If the current minimum fee rate for mempool and relay is 1 sat/vbyte in LBTC (i.e. the policy asset fee rate), then the effective minimum fee rate for this asset would be 100 units/byte. The minimum fee output value required for a transaction of size e.g. vsize = 257 transaction to be relayed would be: 257 x 1 x 100 = 25700 base units of the USDT asset. A new configuration option will be added to enable any node that is creating blocks to publish the assets, rates (and optionally destination) they are applying to their own policy. This information will be encoded into an OP_RETURN output for each accepted asset and included in the coinbase transaction for blocks that they create every N blocks (where N is specified in the configuration). Wallets and users will then be able to read and decode the latest assets and rates being applied by block creators and use this information for determining fees when creating transactions. For ease in retrieving this, all nodes will cache the latest rates which can be accessed via a new RPC. This publication method assumes that all block creators are applying the same assets and rates to their policy, which they would need to agree upon. In order for the acceptance of issued asset and rates for fees to be reliable and predictable, all block creators and bridging nodes (and all relaying nodes) should be applying the same policy with the same list of accepted assets and rates. To coordinate this and apply changes to a large number of nodes is impractical, which may be required regularly as exchange rates change. To provide an alternative method for remotely modifying the accepted assets and rates, individual nodes can be configured to accept updates via an on-chain transaction - this way all block creator and bridge nodes can have their applied policy updated simultaneously without requiring direct access to the RPC interface. -To enable this, each node can be configured with a specified scriptPubKey that authorises the policy update. This is called the 'controller' scriptPubKey (but it can be a multisig representing several entities that must agree to any update). The controller(s) create a transaction with the scriptPubKey as the first output (as well as spending from the same scriptPubKey), and this transaction contains an additional OP_RETURN output for each fee asset to be accepted, encoded with the assetID, rate (and optional destination). +To enable this, each node can be configured with a specified scriptPubKey that authorizes the policy update. This is called the 'controller' scriptPubKey (but it can be a multisig representing several entities that must agree to any update). The controller(s) create a transaction with the scriptPubKey as the first output (as well as spending from the same scriptPubKey), and this transaction contains an additional OP_RETURN output for each fee asset to be accepted, encoded with the assetID, rate (and optional destination). Each configured node detects the configured controller scriptPubKey in a confirmed transaction output. It then verifies that this transaction spends from an output with the same scriptPubKey (this authenticates the transaction by verifying input scriptPubKey witness) and decodes each of the OP_RETURN outputs in the transaction to update the node assetID and rate applied to policy (and destination if creating blocks and c_mandatory_coinbase_destination not used.) @@ -64,23 +58,43 @@ Each configured node detects the configured controller scriptPubKey Accepted assets, rate multipliers (and optional destination) are stored in a vector assetFeeRates of CAssetFeeRate objects. This object contains the CAsset asset ID, the int64_t fee rate multiplier and CScript fee destination. -For each supported issued asset, there is specified fee rate multiplier (issued_asset_fee_rate_multiplier). +For each supported issued asset, there is supplied fee rate multiplier: issued_asset_fee_rate_multiplier. This value scales the calculated the fee rate of a transaction that has an issued asset fee to give an effective_fee_rate which is then used to determine if this is equal to or greater than the relay and mempool minimums set with policyAsset fee rates. For mempool ordering, the effective fee rate is used for comparison with transactions with policyAsset fee rates. + +The fee rate multiplier is specified as a fixed precision int64_t number issued_asset_fee_rate_multiplier_int64 (the number of decimal places supported is specified by the int64_t parameter ASSET_MULTIPLIER_SCALE_FACTOR (default 100000000) which is applied to all asset multipliers. -The fee asset and rate can be configured for an individual node with the option: + +issued_asset_fee_rate_multiplier_int64 = static_cast(issued_asset_fee_rate_multiplier * ASSET_RATE_SCALE_FACTOR) + + +The effective_fee_rate is calculated as follows for any transaction where the fee output is paid in an amount of accepted issued asset: + + +effective_fee_rate = (asset_fee_rate * issued_asset_fee_rate_multiplier_int64) / ASSET_MULTIPLIER_SCALE_FACTOR + --setfeeassetrate=: set the assetID:rate-script applied to relay and mempool policy for this node. Optional script to set destination for coinbase fees for each asset. If mandatory_coinbase_destination is set as a consensus rule, then this is not currently possible and will return an error. +Where asset_fee_rate is the fee rate calculated using the transaction vsize the amount of the issued asset fee output. For every transaction in the mempool that has an issued asset fee, the effective_fee_rate is used in place of the policyAsset fee rate for the purposes of mempool ordering and minimum mempool fee rate calculation. -This can be repeated for additional fee assets and rates. The maximum number of issued assets fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE. +The value of issued_asset_fee_rate_multiplier_int64 must be between 1 and MAX_ASSET_MULTIPLIER -A new accepted fee asset can be added on demand at runtime with the RPC: +By default MAX_ASSET_MULTIPLIER = 10^15. Scaled as a fixed precision number, this enables values of issued_asset_fee_rate_multiplier between 0.00000001 and 10,000,000. -updatefeeassetrate add new assetID, rate, (optional) destination script to apply to node policy or update the rates of existing configured assets dynamically. +The value of (asset_fee_rate * issued_asset_fee_rate_multiplier_int64) cannot exceed the maximum allowed value of int64_t. If it does, then the effective_fee_rate is set to maximum allowable value of std::numeric_limits::max() / ASSET_RATE_SCALE_FACTOR = 92233720368 (which is 922.3372 LBTC/vbyte). -getassetfeerate get all assetID, rate, script applied by this client. +The fee asset and rate multiplier can be configured for an individual node with the option: -All fee rate multipliers are input and output as floating point numbers, scalled with ASSET_RATE_SCALE_FACTOR to/from the internal int64_t issued_asset_fee_rate_multiplier. +-setfeeassetrate=: set the assetID:multiplier-script applied to relay and mempool policy for this node. -Note: In the current Elements, consensus enforces that any individual fee output, or the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could exceed this, as individual assets might have much larger issuances. Therefore, currently in BlockAssember the total value of fee outputs will not exceed MAX_MONEY when generating a new block. However, this may limit transaction capacity and fee revenue on the future. This limitation can be removed along with any hardfork enabling: https://github.com/ElementsProject/elements/pull/1476 +The multiplier is supplied as a floating point number (issued_asset_fee_rate_multiplier) which is converted to fixed point integer by multiplication with ASSET_MULTIPLIER_SCALE_FACTOR. Optional script to set destination for coinbase fees for each asset. If mandatory_coinbase_destination is set as a consensus rule, then this is not currently possible and will return an error. + +This can be repeated for additional fee assets and rates. The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE (default 10). + +A new accepted fee asset can be added on demand at runtime with the RPC, or an existing asset updated with: + +updatefeeassetrate add new assetID, multiplier, (optional) destination script to apply to node policy or update the rates of existing configured assets dynamically. + +getassetfeerate get all assetID, multiplier, script applied by this client. + +Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently in BlockAssember the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block. ====Fee rate publication==== @@ -115,32 +129,20 @@ Any node can be configured to apply issued asset fee policy that is set by a con -confeeassetrate=scriptPubKey where scriptPubKey is the script that is used by the controller(s) to set the asset and rate multipliers in a transaction. --confeeassetrate cannot be set at the same time as -setfeeassetrate (this will generate an error). +-conassetfeerate cannot be set at the same time as -setassetfeerate (this will generate an error). A valid controller transaction must spend from an output where the scriptPubKey is also the same controller script (this is used as an authentication mechanism). The format for the OP_RETURN encoding is that same as that used for the fee rate publication. -Python function to encode to OP_RETURN hex string: - - -def encode_asset_fee_rate(rate_multiplier, asset_id, dest_script): - ASSET_RATE_SCALE_FACTOR = 10**8 # 8 decimal places - scaled_rate = round(rate_multiplier * ASSET_RATE_SCALE_FACTOR) - # Convert to 8-byte hex string (16 characters) - encoded_rate = format(scaled_rate, '016x') - # Encode asset with rate and destination - return "afee" + asset_id + encoded_rate + dest_script - - ==Backwards Compatibility== -No issues for backwards compatibility - all existing transactions with policy asset fees are unaffected by these features. No changes to consesus rules are required. +No issues for backwards compatibility - all existing transactions with policy asset fees are unaffected by these features. No changes to consensus rules are required. ==Test Vectors== -* Configure issued asset with fee rate of issued_asset_fee_rate_multiplier = 2 +* Configure issued asset with fee rate of issued_asset_fee_rate_multiplier = 0.5 -
-setfeeassetrate=e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0:2
+
-setfeeassetrate=e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0:0.5
* Get configured fee asset rates @@ -148,9 +150,9 @@ No issues for backwards compatibility - all existing transactions with policy as Result: -
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('2'), 'script': ''}]
+
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('0.5'), 'script': ''}]
-* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee multiplier at rate 2: +* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee multiplier of 0.5: ** vsize: 257 @@ -158,19 +160,19 @@ Result:
0200000001016f90831e415a738df732b62dd0b76e87bbe489096e43c892bf3dcbbfd173778d0000000000feffffff0301a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000002faf08000160014c5439a67eaa99a308719b544f327a942ae02e6a701a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000002f967de0016001467c8c91e8357e1e24d1721c4535a99608e47beb701a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee801000000000000020200006c0000000000024730440220031c1aa256e40a89cf622374e1ef281870a6abb6f588411800fe885f5195a49e02204f3c37cfe2318db87ac7ce4d7da70682e3477653f6222383ab72ea259a3b1fae0121029896a2fe9c710b8b71488b90038d3974144dc21c9c8750459162dc37d141459300000000000000
-* Update issued asset fee rate multiplier to 5 with RPC +* Update issued asset fee rate multiplier to 0.1 with RPC -
updatefeeassetrate e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0 5
+
updatefeeassetrate e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0 0.1
* Get configured fee asset rates -
getassetfeerate
+
getassetfeerates
Result: -
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('5'), 'script': ''}]
+
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('0.1'), 'script': ''}]
-* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee at rate multiplier 5: +* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee at rate multiplier 0.1: ** vsize: 257 From 845e8ab74f3e8ed6fc1f2aec67819e3befa27c55 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Tue, 30 Sep 2025 16:30:26 +0100 Subject: [PATCH 05/21] updated example, removed repetitions, removed elip number --- README.mediawiki | 7 ------ elip-0202.mediawiki | 59 +++++++++++++++++++++------------------------ 2 files changed, 27 insertions(+), 39 deletions(-) diff --git a/README.mediawiki b/README.mediawiki index c0c2fd1..ca4c53d 100644 --- a/README.mediawiki +++ b/README.mediawiki @@ -74,13 +74,6 @@ Having an ELIP here does not make it a formally accepted standard until its stat | Kilian Rausch | Standards Track | Active -|- style="background-color: #ffcfcf" -| [[elip-0202.mediawiki|202]] -| Applications -| Issued asset fees -| Tom Trevethan -| Standards Track -| Draft |} diff --git a/elip-0202.mediawiki b/elip-0202.mediawiki index 5110350..141530d 100644 --- a/elip-0202.mediawiki +++ b/elip-0202.mediawiki @@ -1,10 +1,10 @@
-  ELIP: 202
+  ELIP: ???
   Layer: Applications
   Title: Issued Asset Fees
   Author: Tom Trevethan 
   Comments-Summary: No comments yet.
-  Comments-URI: https://github.com/ElementsProject/elips/wiki/Comments:ELIP-0202
+  Comments-URI: https://github.com/ElementsProject/elips/wiki/Comments:ELIP-0???
   Status: Draft
   Type: Standards Track
   Created: 2025-09-02
@@ -15,7 +15,7 @@
 
 ===Abstract===
 
-This document proposes a modification to Elements to enable transaction fees to be paid in issued assets in addition to the policyAsset. To enable this, the specific issued assets that will be accepted and the corresponding fee rate, relative to the applied policyAsset rate must be agreed and configured by block creators and communicated to users and implemented in wallets. To enable the use of issued asset fees requires changes to policy rules for transaction relay mempool acceptance and ordering, and changes to block creation to enable issued asset fees to be paid to specified destinations in coinbase outputs. Issued asset fee acceptance and payout in the coinbase transaction is valid within the current consensus rules and so none of the features described here require any consensus fork to deploy. In addition to the core changes to policy and block creation, this document also proposes features to enable both publication of accepted assets and fee rates on-chain for use by wallets, and a mechanism to update the assets and rates accepted by nodes via an authenticated on-chain transaction.
+This document proposes a modification to Elements to enable transaction fees to be paid in issued assets in addition to the policyAsset (or pegged asset). To enable this, the specific issued assets that will be accepted for fees and the corresponding fee rate (relative to the enforced policyAsset rate) must be agreed and configured by all block creators and also communicated to users and implemented in wallets. To enable the use of issued asset fees requires changes to policy rules for transaction relay mempool acceptance and ordering, and changes to block creation to enable issued asset fees to be paid to specified destinations in coinbase outputs. Issued asset fee acceptance and payout in the coinbase transaction is valid within the current consensus rules and so none of the features described here require any consensus fork to deploy. In addition to these changes to policy and block creation, this document also proposes features to enable both publication of accepted assets and fee rates on-chain for use by wallets, and a method to update the fee asset policy applied by nodes remotely via an authenticated on-chain transaction.
 
 ===Copyright===
 
@@ -23,56 +23,45 @@ This document is licensed under the 3-clause BSD license.
 
 ===Motivation===
 
-In Elements, transaction fees are required in order to prevent denial of service attacks and to prioritize transactions for limited block space. Unlike in Bitcoin, where transaction fees are determined from the difference between the total input and output values, in Elements transaction fees are specified by an explicit unblinded output (signified by an empty scriptPubKey) and input and output totals for all assets must equal. In the current Elements implementation, policy rules for relay and mempool acceptance require that the assetID of this fee output be equal to the policyAsset. The current consensus rules however allow for any assetID for the fee asset (so long as the transaction is otherwise valid) as well as no fee output (however, a specified fee output with zero fee is not consensus valid). The current consensus rules also allow for coinbase outputs for any asset with values equal to or less than the total fee outputs for that asset for all transactions in a block. However the current Elements code will only create a block with a single spendable coinbase output for the total of policyAsset fees.
+In Elements, transaction fees are required in order to prevent denial of service attacks and to prioritize transactions for limited block space and are specified by an explicit unblinded output (signified by an empty scriptPubKey). In the current Elements implementation, policy rules for transaction relay and mempool acceptance require that the assetID of this fee output be equal to the policyAsset. The current consensus rules however allow for any assetID for the fee asset (so long as the transaction is otherwise valid). The current consensus rules also allow for coinbase outputs for any asset with values equal to or less than the total fee outputs for that asset for all transactions in a block. However the current Elements code will only create a block with a single spendable coinbase output for the total of policyAsset fees. 
 
 These current restrictions limit the usefulness of the Elements platform for transacting in issued assets, as policyAsset is always then required for the payment of transaction fees. For example, for a Bitcoin sidechain (e.q. Liquid) there are many issued assets for tokenized currencies (e.g. USDT) which many users will use for making payments. However they must obtain an amount of the policyAsset (e.g. LBTC) before they can transact, and the fees will be priced in BTC instead of USD. This leads to a poor user experience when transacting in tokenized stablecoins, as a new user will be required to purchase LBTC before they are able to spend USDT that they have received in their wallet. In addition, the value of transaction fee required will not be explicit in terms of USDT and depend on the current USD value of BTC. By enabling fees to be paid in specified issued assets and at specified rates, user wallets can transact in an issued asset like USDT seamlessly, with predictable and transparent fees.
 
-In addition to enabling the issued asset transaction fees to be accepted, and payment of these fees in the coinbase, there needs to be a method for wallets and users to know which assets are accepted by block creators and at what rates. The simplest and most robust way of achieving this is with a publication of the accepted assets and rates by block creators on chain, in the coinbase - this removes the requirement for separate servers to publish and relay this data.
+In addition to enabling the issued asset transaction fees to be accepted, and payment of these fees in the coinbase of new blocks, there needs to be a method for wallets and users to know which assets are accepted by block creators and at what rates. The simplest and most robust way of achieving this is with a publication of the accepted assets and rates by block creators on chain, in the coinbase transaction - this removes the requirement for separate servers to publish and relay this data. 
 
-Finally, a method is required to set the assets and rates that will be applied and enforced on relay/bridge and block creation nodes. These can be set with direct access to the node, either via the node configuration or the RPC interface. However, these assets and rates may need to be updated frequently, and it may be impractical and have security implications to have direct access to functionary and bridge nodes to perform these updates via the RPC interface. Therefore, a method to perform this update remotely via an on-chain transaction is also required. Individual nodes can then be configured with a 'controller' script that authenticates changes to the asset fee policy without requiring direct access to any individual nodes.
+Finally, a method is required to configure which assets and corresponding fee rates will be applied to policy on relaying and block creation nodes. These can be set with direct access to the node, either via the node configuration or the RPC interface. However, the accepted assets and fee rates may need to be updated frequently, and it may be impractical and have security implications to have direct access to functionary and bridge nodes to perform these updates via the RPC interface. Therefore, a method to perform this update remotely via an on-chain transaction is also required. Individual nodes can then be configured with a 'controller' script that authenticates changes to the asset fee policy without requiring direct access to any individual nodes. 
 
 ==Design==
 
 ===Overview===
 
-We propose a modification to Elements to enable to use of transaction fees paid in specified issued assets in addition to the policyAsset at specified rates relative to the current policyAsset rate applied. This requires no changes to the consensus rules, and these changes can be deployed without requiring a network fork. The specified assets and rates will be applied to relay and mempool policy, mempool ordering, and these fees will be paid to specified destinations in the coinbase transaction of created blocks.
+Each node has complete control over which assets and at what fee rates to accept as policy. However, in practice all block creators and relaying nodes should agree to apply the same asset fee policy in order to provide a reliable service for users. The assets and fee rates accepted by any node can be set as part of the node configuration, specifying the assetID, the relative rate multiplier (and optionally the coinbase destination for the fee collection by the block creator) before initialization, or this can be set dynamically at runtime via a new RPC. In addition, a new RPC will be added to query the current assets, rate multiplier (and destinations) currently being applied to policy by any node.
 
-Each node has complete control over which assets and at what rates to accept as policy, however in practice all block creators and relaying nodes *should* agree to apply the same asset fee policy in order to provide a reliable service for users. Each node will be able to directly specify what assets and at what rate they will apply to policy. This can be set as part of the node configuration, specifying the assetID, the relative rate multiplier (and optionally the coinbase destination for the fee collection by the block creator) before initialization, or set dynamically at runtime via a new RPC option. Note that if the consensus parameter c_mandatory_coinbase_destination is set to a non-empty script, then this cannot be overridden and all asset fee coinbase outputs must be paid to the c_mandatory_coinbase_destination script. In addition, a new RPC will be added to query the current assets, rate multiplier (and destinations) currently being applied by any node.
+For fee outputs in an issued asset, the fee rate (in units/vbyte) will be scaled to determine an 'effective rate'. This effective rate is then used in place of the policyAsset fee rate for the purpose of meeting relay and mempool minimums and for mempool ordering. 
 
-For fees outputs in an issued asset, the fee rate (in units/vbyte) will be scaled by a fee rate multiplier to determine an 'effective rate'. This effective rate is then used in place of the policyAsset fee rate for the purpose of meeting relay and mempool minimums and for mempool ordering. 
-
-E.g. An issued asset is USDT. The current conversion rate between USDT and LBTC (policyAsset) is 100:1 (i.e. 100 USDT to one LBTC) the issued asset fee rate multiplier would then be set to 0.01 (i.e. the fee paid in USDT will be multiplied by 0.01 for comparison with LBTC fees). If the USDT asset is used for the fee in any transaction, it should pay 100 times in USDT base units the fee rate that would have applied in LBTC (the policyAsset). If the current minimum fee rate for mempool and relay is 1 sat/vbyte in LBTC (i.e. the policy asset fee rate), then the effective minimum fee rate for this asset would be 100 units/byte. The minimum fee output value required for a transaction of size e.g. vsize = 257 transaction to be relayed would be: 257 x 1 x 100 = 25700 base units of the USDT asset. 
-
-A new configuration option will be added to enable any node that is creating blocks to publish the assets, rates (and optionally destination) they are applying to their own policy. This information will be encoded into an OP_RETURN output for each accepted asset and included in the coinbase transaction for blocks that they create every N blocks (where N is specified in the configuration). Wallets and users will then be able to read and decode the latest assets and rates being applied by block creators and use this information for determining fees when creating transactions. For ease in retrieving this, all nodes will cache the latest rates which can be accessed via a new RPC. This publication method assumes that all block creators are applying the same assets and rates to their policy, which they would need to agree upon.
-
-In order for the acceptance of issued asset and rates for fees to be reliable and predictable, all block creators and bridging nodes (and all relaying nodes) should be applying the same policy with the same list of accepted assets and rates. To coordinate this and apply changes to a large number of nodes is impractical, which may be required regularly as exchange rates change. To provide an alternative method for remotely modifying the accepted assets and rates, individual nodes can be configured to accept updates via an on-chain transaction - this way all block creator and bridge nodes can have their applied policy updated simultaneously without requiring direct access to the RPC interface.
-
-To enable this, each node can be configured with a specified scriptPubKey that authorizes the policy update. This is called the 'controller' scriptPubKey (but it can be a multisig representing several entities that must agree to any update). The controller(s) create a transaction with the scriptPubKey as the first output (as well as spending from the same scriptPubKey), and this transaction contains an additional OP_RETURN output for each fee asset to be accepted, encoded with the assetID, rate (and optional destination).
-
-Each configured node detects the configured controller scriptPubKey in a confirmed transaction output. It then verifies that this transaction spends from an output with the same scriptPubKey (this authenticates the transaction by verifying input scriptPubKey witness) and decodes each of the OP_RETURN outputs in the transaction to update the node assetID and rate applied to policy (and destination if creating blocks and c_mandatory_coinbase_destination not used.)
+E.g. An issued asset is USDT. The current accepted exchange rate between USDT and LBTC (the policyAsset) is $114,171.23 to 1 LBTC. The issued asset fee rate multiplier for this ratio is then set to 1/114,171.23 = 0.00000876 (rounded to a precision of 1e-8). For a transaction that uses the USDT asset for the fee, the fee rate (in units/vbyte) multiplied by this number must be greater than or equal to the minimum fee rate (in sat/vbyte) in LBTC applied to policy.
+If the current minimum relay fee rate is 1 sat/vbyte in LBTC, then the minimum fee rate required in USDT would be 1/0.00000876 = 114156 units/vbyte (rounded up to the nearest integer). To relay this transaction, a client will determine the fee rate in the USDT asset, multiply it by 0.00000876 to calculate the effective rate and then verify it is greater than or equal to 1 sat/vbyte. 
 
 ===Specification===
 
 ====Issued asset fees====
 
 Accepted assets, rate multipliers (and optional destination) are stored in a vector assetFeeRates of CAssetFeeRate objects.
-This object contains the CAsset asset ID, the int64_t fee rate multiplier and CScript fee destination.
+This object contains the CAsset asset ID, the int64_t fee rate multiplier and optional CScript fee destination.
 
-For each supported issued asset, there is supplied fee rate multiplier: issued_asset_fee_rate_multiplier. This value scales the calculated the fee rate of a transaction that has an issued asset fee to give an effective_fee_rate which is then used to determine if this is equal to or greater than the relay and mempool minimums set with policyAsset fee rates. For mempool ordering, the effective fee rate is used for comparison with transactions with policyAsset fee rates. 
-
-The fee rate multiplier is specified as a fixed precision int64_t number issued_asset_fee_rate_multiplier_int64 (the number of decimal places supported is specified by the int64_t parameter ASSET_MULTIPLIER_SCALE_FACTOR (default 100000000) which is applied to all asset multipliers. 
+For each supported issued asset, there is a specified fee rate multiplier: issued_asset_fee_rate_multiplier (a float). This is represented internally as a fixed precision int64_t number issued_asset_fee_rate_multiplier_int64 (the number of decimal places supported is specified by the int64_t parameter ASSET_MULTIPLIER_SCALE_FACTOR (default 100000000) which is applied to all asset multipliers. 
 
 
-issued_asset_fee_rate_multiplier_int64 = static_cast(issued_asset_fee_rate_multiplier * ASSET_RATE_SCALE_FACTOR)
+issued_asset_fee_rate_multiplier_int64 = static_cast(std::ceil(issued_asset_fee_rate_multiplier * ASSET_RATE_SCALE_FACTOR))
 
 
-The effective_fee_rate is calculated as follows for any transaction where the fee output is paid in an amount of accepted issued asset:
+This value scales the calculated the fee rate of a transaction that has an issued asset fee to give an effective_fee_rate. 
 
 
 effective_fee_rate = (asset_fee_rate * issued_asset_fee_rate_multiplier_int64) / ASSET_MULTIPLIER_SCALE_FACTOR
 
 
-Where asset_fee_rate is the fee rate calculated using the transaction vsize the amount of the issued asset fee output. For every transaction in the mempool that has an issued asset fee, the effective_fee_rate is used in place of the policyAsset fee rate for the purposes of mempool ordering and minimum mempool fee rate calculation. 
+Where asset_fee_rate is the fee rate calculated using the transaction vsize and the amount of the issued asset fee output. For every transaction in the mempool that has an issued asset fee, the effective_fee_rate is used in place of the policyAsset fee rate for the purposes of mempool ordering and minimum mempool fee rate calculation. 
 
 The value of issued_asset_fee_rate_multiplier_int64 must be between 1 and MAX_ASSET_MULTIPLIER
 
@@ -80,11 +69,17 @@ By default MAX_ASSET_MULTIPLIER = 10^15. Scaled as a fixed precisio
 
 The value of (asset_fee_rate * issued_asset_fee_rate_multiplier_int64) cannot exceed the maximum allowed value of int64_t. If it does, then the effective_fee_rate is set to maximum allowable value of std::numeric_limits::max() / ASSET_RATE_SCALE_FACTOR = 92233720368 (which is 922.3372 LBTC/vbyte). 
 
+To calculate the value of an issued asset fee output required for a transaction of size vsize where the current minimum policyAsset fee rate is policy_asset_rate:
+
+
+fee_amount = vsize * policy_asset_rate * (1 + ((ASSET_RATE_SCALE_FACTOR - 1) / issued_asset_fee_rate_multiplier_int64))
+
+
 The fee asset and rate multiplier can be configured for an individual node with the option:
 
 -setfeeassetrate=: set the assetID:multiplier-script applied to relay and mempool policy for this node. 
 
-The multiplier is supplied as a floating point number (issued_asset_fee_rate_multiplier) which is converted to fixed point integer by multiplication with ASSET_MULTIPLIER_SCALE_FACTOR. Optional script to set destination for coinbase fees for each asset. If mandatory_coinbase_destination is set as a consensus rule, then this is not currently possible and will return an error.
+The multiplier is supplied as a floating point number (issued_asset_fee_rate_multiplier) which is converted to fixed point integer by ceiling multiplication with ASSET_MULTIPLIER_SCALE_FACTOR. 
 
 This can be repeated for additional fee assets and rates. The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE (default 10).
 
@@ -94,11 +89,11 @@ A new accepted fee asset can be added on demand at runtime with the RPC, or an e
 
 getassetfeerate get all assetID, multiplier, script applied by this client.
 
-Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently in BlockAssember the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block. 
+Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently in BlockAssembler the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block. 
 
 ====Fee rate publication====
 
-Asset fee rates (and optional destination) encoded in coinbase OP_RETURN as follows:
+Asset fee rates multipliers (and optional destination) encoded in additional coinbase OP_RETURN outputs as follows:
 
 * 4 bytes: AFEE
 * 32 bytes: asset ID
@@ -114,10 +109,10 @@ Config option:
 If n is 0 then fee assets and rate multipliers are not written.
 
 If n > 0 then any accepted assets and fee rates set via RPC or config are published every n blocks.
-Any fee rates updated by setassetfeerate or controller script are not applied to policy until the end of an epoch.
+Any fee rates updated by setassetfeerate or controller script are not applied to policy until the end of an epoch of n blocks.
 
-A new RPC getsignerfeerates retrieves the latest published accepted assets and rate multipliers (cached).
-getassetfeerate has a single boolean argument to either retrieve the latest updated fee rate multipliers, or the rates applied in the current epoch.
+A new RPC getsignerfeerates retrieves the latest published accepted assets and rate multipliers.
+getassetfeerate has a single boolean argument to either retrieve the latest updated list of assets and fee rate multipliers, or the list applied in the current epoch.
 
 On node restart, the cached rates are updated from the latest publication.
 

From 3d917cdd4b480b8b247a79778de8daec460b1825 Mon Sep 17 00:00:00 2001
From: Tom Trevethan 
Date: Thu, 2 Oct 2025 12:47:48 +0100
Subject: [PATCH 06/21] change filename temporarily

---
 elip-0202.mediawiki => elip-0000.mediawiki | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename elip-0202.mediawiki => elip-0000.mediawiki (100%)

diff --git a/elip-0202.mediawiki b/elip-0000.mediawiki
similarity index 100%
rename from elip-0202.mediawiki
rename to elip-0000.mediawiki

From eb97c3804724c36f06724b9c7dfe4346adccf41c Mon Sep 17 00:00:00 2001
From: Tom Trevethan 
Date: Mon, 1 Dec 2025 16:35:43 +0000
Subject: [PATCH 07/21] Add mempool ordering logic for changing rate
 multipliers.

---
 elip-0000.mediawiki | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki
index 141530d..210b3de 100644
--- a/elip-0000.mediawiki
+++ b/elip-0000.mediawiki
@@ -87,6 +87,8 @@ A new accepted fee asset can be added on demand at runtime with the RPC, or an e
 
 updatefeeassetrate add new assetID, multiplier, (optional) destination script to apply to node policy or update the rates of existing configured assets dynamically.
 
+Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block by a block creator) by the policyAsset fee rate (if the fee asset is `policyAsset`) or the effective_fee_rate if the fee output is an issued asset. The issued_asset_fee_rate_multiplier_int64 for a specific asset may be changed (e.g. via the updatefeeassetrate RPC) while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. 
+
 getassetfeerate get all assetID, multiplier, script applied by this client.
 
 Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently in BlockAssembler the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block. 

From 18a74aff2522721c8f845b876cafbf1a53938e93 Mon Sep 17 00:00:00 2001
From: Tom Trevethan 
Date: Fri, 9 Jan 2026 11:05:04 +0000
Subject: [PATCH 08/21] updated abstract and motivation sections

---
 elip-0000.mediawiki | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki
index 210b3de..fd84087 100644
--- a/elip-0000.mediawiki
+++ b/elip-0000.mediawiki
@@ -15,7 +15,7 @@
 
 ===Abstract===
 
-This document proposes a modification to Elements to enable transaction fees to be paid in issued assets in addition to the policyAsset (or pegged asset). To enable this, the specific issued assets that will be accepted for fees and the corresponding fee rate (relative to the enforced policyAsset rate) must be agreed and configured by all block creators and also communicated to users and implemented in wallets. To enable the use of issued asset fees requires changes to policy rules for transaction relay mempool acceptance and ordering, and changes to block creation to enable issued asset fees to be paid to specified destinations in coinbase outputs. Issued asset fee acceptance and payout in the coinbase transaction is valid within the current consensus rules and so none of the features described here require any consensus fork to deploy. In addition to these changes to policy and block creation, this document also proposes features to enable both publication of accepted assets and fee rates on-chain for use by wallets, and a method to update the fee asset policy applied by nodes remotely via an authenticated on-chain transaction.
+This document proposes a modification to Elements to enable transaction fees to be paid in issued assets in addition to the policyAsset (or pegged asset). Individual nodes will be configured with the issued assets for fees accepted for mempool entry and relay, and at what fee rates relative to the policyAsset. The accepted fee assets and the relative applied fee rate can be either configured explicitly for each node, or set remotely with an authenticated on-chain transaction. Block creators can optionally publish the accepted fee assets and corresponding fee rates in the coinbase of blocks they generate to communicate this information to users and wallets. 
 
 ===Copyright===
 
@@ -23,13 +23,11 @@ This document is licensed under the 3-clause BSD license.
 
 ===Motivation===
 
-In Elements, transaction fees are required in order to prevent denial of service attacks and to prioritize transactions for limited block space and are specified by an explicit unblinded output (signified by an empty scriptPubKey). In the current Elements implementation, policy rules for transaction relay and mempool acceptance require that the assetID of this fee output be equal to the policyAsset. The current consensus rules however allow for any assetID for the fee asset (so long as the transaction is otherwise valid). The current consensus rules also allow for coinbase outputs for any asset with values equal to or less than the total fee outputs for that asset for all transactions in a block. However the current Elements code will only create a block with a single spendable coinbase output for the total of policyAsset fees. 
+Current policy rules for mempool acceptance and relay require that the assetID of the transaction fee output is the policyAsset. The consensus rules however allow for any assetID to be used for the fee asset (so long as the transaction is otherwise valid). The current restriction limits the usefulness of the Elements platform for transacting in issued assets, as policyAsset must be obtained before being able to transact. Block creators may choose to accept fee payments in other assets that have value, at specified exchange rates. 
 
-These current restrictions limit the usefulness of the Elements platform for transacting in issued assets, as policyAsset is always then required for the payment of transaction fees. For example, for a Bitcoin sidechain (e.q. Liquid) there are many issued assets for tokenized currencies (e.g. USDT) which many users will use for making payments. However they must obtain an amount of the policyAsset (e.g. LBTC) before they can transact, and the fees will be priced in BTC instead of USD. This leads to a poor user experience when transacting in tokenized stablecoins, as a new user will be required to purchase LBTC before they are able to spend USDT that they have received in their wallet. In addition, the value of transaction fee required will not be explicit in terms of USDT and depend on the current USD value of BTC. By enabling fees to be paid in specified issued assets and at specified rates, user wallets can transact in an issued asset like USDT seamlessly, with predictable and transparent fees.
+Block creators can configure individual nodes explicitly with accepted assets for fees and rates, but to enable a federation to be managed with consistent fee policy (which may require frequent changes), a method to perform this configuration remotely via an on-chain transaction is also required. Individual nodes can then be configured with a 'controller' script that enables changes to the fee asset policy without requiring direct configuration of individual nodes. 
 
-In addition to enabling the issued asset transaction fees to be accepted, and payment of these fees in the coinbase of new blocks, there needs to be a method for wallets and users to know which assets are accepted by block creators and at what rates. The simplest and most robust way of achieving this is with a publication of the accepted assets and rates by block creators on chain, in the coinbase transaction - this removes the requirement for separate servers to publish and relay this data. 
-
-Finally, a method is required to configure which assets and corresponding fee rates will be applied to policy on relaying and block creation nodes. These can be set with direct access to the node, either via the node configuration or the RPC interface. However, the accepted assets and fee rates may need to be updated frequently, and it may be impractical and have security implications to have direct access to functionary and bridge nodes to perform these updates via the RPC interface. Therefore, a method to perform this update remotely via an on-chain transaction is also required. Individual nodes can then be configured with a 'controller' script that authenticates changes to the asset fee policy without requiring direct access to any individual nodes. 
+In order for wallets and users to know which assets are being accepted by block creators for fees and at what rates, the block creators can publish this information in the coinbase of blocks they produce. This removes the requirement for separate servers to publish and relay this data. 
 
 ==Design==
 

From 6092cff066c5bdc09117dc2721e0ef884cfec218 Mon Sep 17 00:00:00 2001
From: Tom Trevethan 
Date: Fri, 9 Jan 2026 13:12:19 +0000
Subject: [PATCH 09/21] modifified the controler update specification section
 to clarify the nature of the controller transaction

---
 elip-0000.mediawiki | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki
index fd84087..553cdce 100644
--- a/elip-0000.mediawiki
+++ b/elip-0000.mediawiki
@@ -118,16 +118,15 @@ On node restart, the cached rates are updated from the latest publication.
 
 ====Controller fee asset rate updates====
 
-Feature for issued asset fees that enables an external controller to set issued asset fee policy via an onchain transaction, with asset fee rate multipliers encoded in OP_RETURN outputs.
-Any node can be configured to apply issued asset fee policy that is set by a controller (this does not need to be a single entity, but can be defined by any multisig script). This is set with a config option:
+Feature that enables an external controller to set issued asset fee policy via an onchain transaction, with asset fee rate multipliers encoded in OP_RETURN outputs. 
 
--confeeassetrate=scriptPubKey
+Any node can be configured to apply issued asset fee policy that is set by an external controller. This controller may be a single entity or several different entities which is given the authority to set fee policy. A node is then configured with the scriptPubKey that the controller (or multiple controllers, via a multisig script) can generate a valid witness for. 
 
-where scriptPubKey is the script that is used by the controller(s) to set the asset and rate multipliers in a transaction.
--conassetfeerate cannot be set at the same time as -setassetfeerate (this will generate an error).
-A valid controller transaction must spend from an output where the scriptPubKey is also the same controller script (this is used as an authentication mechanism).
+A valid controller transaction is defined by:
 
-The format for the OP_RETURN encoding is that same as that used for the fee rate publication.
+1. The first output being the specified controller scriptPubKey
+2. It spends from an output with the same specified controller scriptPubKey (this authenticates the transaction). 
+3. The other outputs are OP_RETURN with each having a fee asset and rate multiplier (encoding is that same as that used for the fee rate publication). 
 
 ==Backwards Compatibility==
 

From dff6536f91fd585f55fbe8af048d7f67a1b6e8ee Mon Sep 17 00:00:00 2001
From: Tom Trevethan 
Date: Mon, 12 Jan 2026 09:07:29 +0000
Subject: [PATCH 10/21] removed specification of specific config options and
 RPCs

---
 elip-0000.mediawiki | 60 +++++++++++----------------------------------
 1 file changed, 14 insertions(+), 46 deletions(-)

diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki
index 553cdce..e11dbc8 100644
--- a/elip-0000.mediawiki
+++ b/elip-0000.mediawiki
@@ -33,7 +33,7 @@ In order for wallets and users to know which assets are being accepted by block
 
 ===Overview===
 
-Each node has complete control over which assets and at what fee rates to accept as policy. However, in practice all block creators and relaying nodes should agree to apply the same asset fee policy in order to provide a reliable service for users. The assets and fee rates accepted by any node can be set as part of the node configuration, specifying the assetID, the relative rate multiplier (and optionally the coinbase destination for the fee collection by the block creator) before initialization, or this can be set dynamically at runtime via a new RPC. In addition, a new RPC will be added to query the current assets, rate multiplier (and destinations) currently being applied to policy by any node.
+Each node has complete control over which assets and at what fee rates to accept as policy. However, in practice all block creators and relaying nodes should agree to apply the same asset fee policy in order to provide a reliable service for users. The assets and fee rates accepted by any node can be set as part of the node configuration, specifying the assetID, the relative rate multiplier (and optionally the coinbase destination for the fee collection by the block creator) before initialization, or this can be set dynamically at runtime. 
 
 For fee outputs in an issued asset, the fee rate (in units/vbyte) will be scaled to determine an 'effective rate'. This effective rate is then used in place of the policyAsset fee rate for the purpose of meeting relay and mempool minimums and for mempool ordering. 
 
@@ -73,48 +73,26 @@ To calculate the value of an issued asset fee output required for a transaction
 fee_amount = vsize * policy_asset_rate * (1 + ((ASSET_RATE_SCALE_FACTOR - 1) / issued_asset_fee_rate_multiplier_int64))
 
 
-The fee asset and rate multiplier can be configured for an individual node with the option:
+Nodes can be configured for one or more issued asset fee multipliers. The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE (default 10).
 
--setfeeassetrate=: set the assetID:multiplier-script applied to relay and mempool policy for this node. 
-
-The multiplier is supplied as a floating point number (issued_asset_fee_rate_multiplier) which is converted to fixed point integer by ceiling multiplication with ASSET_MULTIPLIER_SCALE_FACTOR. 
-
-This can be repeated for additional fee assets and rates. The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE (default 10).
-
-A new accepted fee asset can be added on demand at runtime with the RPC, or an existing asset updated with:
-
-updatefeeassetrate add new assetID, multiplier, (optional) destination script to apply to node policy or update the rates of existing configured assets dynamically.
-
-Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block by a block creator) by the policyAsset fee rate (if the fee asset is `policyAsset`) or the effective_fee_rate if the fee output is an issued asset. The issued_asset_fee_rate_multiplier_int64 for a specific asset may be changed (e.g. via the updatefeeassetrate RPC) while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. 
-
-getassetfeerate get all assetID, multiplier, script applied by this client.
+Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is `policyAsset`) or the effective_fee_rate if the fee output is an issued asset. The issued_asset_fee_rate_multiplier_int64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. 
 
 Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently in BlockAssembler the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block. 
 
 ====Fee rate publication====
 
-Asset fee rates multipliers (and optional destination) encoded in additional coinbase OP_RETURN outputs as follows:
+Nodes can optionally be configured to include the issued assets and multipliers they are accepting for fees in blocks they produce in order to publicize these to wallets and other users. 
+
+Asset fee rates multipliers (and an optional destination) may be encoded in additional coinbase OP_RETURN outputs (one output per asset) as follows:
 
 * 4 bytes: AFEE
 * 32 bytes: asset ID
 * 16 bytes: fee rate multiplier (uint_64 encoded)
 * (up to) 35 bytes: fee destination script
 
-The maximum number of additional outputs is limited to MAX_ISSUED_ASSET_FEE_SIZE. The maximum size for transactions included in a generated block must be reduced by the additional space taken by the additional coinbase outputs.
-
-Config option:
-
--writefeeassetrate=n Write issued asset fee rate multipliers accepted as policy to the coinbase of created blocks with interval n (default: 0).
+Nodes can be configured to perform this publication either every block, or every n blocks. 
 
-If n is 0 then fee assets and rate multipliers are not written.
-
-If n > 0 then any accepted assets and fee rates set via RPC or config are published every n blocks.
-Any fee rates updated by setassetfeerate or controller script are not applied to policy until the end of an epoch of n blocks.
-
-A new RPC getsignerfeerates retrieves the latest published accepted assets and rate multipliers.
-getassetfeerate has a single boolean argument to either retrieve the latest updated list of assets and fee rate multipliers, or the list applied in the current epoch.
-
-On node restart, the cached rates are updated from the latest publication.
+A new RPC can be added to retrieve the latest assets and multipliers for use by wallets. 
 
 ====Controller fee asset rate updates====
 
@@ -136,12 +114,10 @@ No issues for backwards compatibility - all existing transactions with policy as
 
 * Configure issued asset with fee rate of issued_asset_fee_rate_multiplier = 0.5
 
-
-setfeeassetrate=e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0:0.5
+
asset: e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0
* Get configured fee asset rates -
getassetfeerate
- Result:
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('0.5'), 'script': ''}]
@@ -154,14 +130,12 @@ Result:
0200000001016f90831e415a738df732b62dd0b76e87bbe489096e43c892bf3dcbbfd173778d0000000000feffffff0301a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000002faf08000160014c5439a67eaa99a308719b544f327a942ae02e6a701a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000002f967de0016001467c8c91e8357e1e24d1721c4535a99608e47beb701a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee801000000000000020200006c0000000000024730440220031c1aa256e40a89cf622374e1ef281870a6abb6f588411800fe885f5195a49e02204f3c37cfe2318db87ac7ce4d7da70682e3477653f6222383ab72ea259a3b1fae0121029896a2fe9c710b8b71488b90038d3974144dc21c9c8750459162dc37d141459300000000000000
-* Update issued asset fee rate multiplier to 0.1 with RPC +* Update issued asset fee rate multiplier to 0.1 -
updatefeeassetrate e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0 0.1
+
asset: e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0
* Get configured fee asset rates -
getassetfeerates
- Result:
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('0.1'), 'script': ''}]
@@ -176,10 +150,8 @@ Result: * Configuration for fee asset rate publication: -
-writefeeassetrate=100
- -
updatefeeassetrate b92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22 1.86
-
updatefeeassetrate af6e22da7b20de2da56c6726c4a036c73f777c8fb5bfc356892c01a9a3dd58d3 3.781
+
asset: b92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22 multiplier: 1.86
+
asset: af6e22da7b20de2da56c6726c4a036c73f777c8fb5bfc356892c01a9a3dd58d3 multiplier: 3.781
Coinbase transaction: @@ -201,10 +173,6 @@ controller_addr = "ert1qt9ttcpchjy54hl23v8wmwu922d3gnvlrz7sqx7" private_key = "cP3N3Z3rMTo4L2gNRDWmzo8nGNyVNnWwFX9DRVPqwcPnMWE8Rn33"
-Configuration - -
-confeeassetrate=00145956bc071791295bfd5161ddb770aa536289b3e3
- Fee Asset setting:
asset_id = b92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22
@@ -219,7 +187,7 @@ Controller transaction:
020000000001500d712c870c96c0bec05f41d765f4f5f203bd8d5b7ea6ef333b53b8c2c2d164000000006a47304402207f1b2c580ee052b8d0da06a139cd1e910ec93c8b11585d692646e6267b8354c4022045caab622e3adbae204263dfdc86e341fd9f3c013cbf2234fdea2c8ee2ba0d81012103ba4a2b1f401eb59e1e6b104f8043ce41b38b65bd24c10edb3df8863b0241e5afffffffff0301230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b201000000012a046b60001600145956bc071791295bfd5161ddb770aa536289b3e301230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000000002c6a2aafeeb92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a220000000005f5e10001230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b20100000000000186a0000000000000
-
getassetfeerate
+Get asset and multiplier:
[{'asset': 'b92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22', 'rate': 1, 'script': ''}]
From a7d50d19ddfcd0c3da4e5cbb39e9a4885440dca5 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Mon, 12 Jan 2026 11:24:41 +0000 Subject: [PATCH 11/21] removed implementation details not relevant to spec --- elip-0000.mediawiki | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index e11dbc8..5f47e82 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -44,9 +44,6 @@ If the current minimum relay fee rate is 1 sat/vbyte in LBTC, then the minimum f ====Issued asset fees==== -Accepted assets, rate multipliers (and optional destination) are stored in a vector assetFeeRates of CAssetFeeRate objects. -This object contains the CAsset asset ID, the int64_t fee rate multiplier and optional CScript fee destination. - For each supported issued asset, there is a specified fee rate multiplier: issued_asset_fee_rate_multiplier (a float). This is represented internally as a fixed precision int64_t number issued_asset_fee_rate_multiplier_int64 (the number of decimal places supported is specified by the int64_t parameter ASSET_MULTIPLIER_SCALE_FACTOR (default 100000000) which is applied to all asset multipliers. @@ -73,7 +70,7 @@ To calculate the value of an issued asset fee output required for a transaction fee_amount = vsize * policy_asset_rate * (1 + ((ASSET_RATE_SCALE_FACTOR - 1) / issued_asset_fee_rate_multiplier_int64)) -Nodes can be configured for one or more issued asset fee multipliers. The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE (default 10). +Each node stores a local list of the accepted fee assets, corresponding rate multiplier (and optional destination). The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE (default 10). Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is `policyAsset`) or the effective_fee_rate if the fee output is an issued asset. The issued_asset_fee_rate_multiplier_int64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. From a8506a6f8a6f5efdc21b77119dc3c8495f7b371d Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Mon, 12 Jan 2026 11:51:40 +0000 Subject: [PATCH 12/21] added statment about rounding errors as multiplier approaches minimum --- elip-0000.mediawiki | 2 ++ 1 file changed, 2 insertions(+) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index 5f47e82..9ddabab 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -70,6 +70,8 @@ To calculate the value of an issued asset fee output required for a transaction fee_amount = vsize * policy_asset_rate * (1 + ((ASSET_RATE_SCALE_FACTOR - 1) / issued_asset_fee_rate_multiplier_int64))
+The minimum and maximum possible values of the fee rate multiplier limit the relative values of assets that can be used for fees, and as the multiplier gets closer to its minimum value the rounding rules will cause larger inaccuracies in the relative fees charged. + Each node stores a local list of the accepted fee assets, corresponding rate multiplier (and optional destination). The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE (default 10). Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is `policyAsset`) or the effective_fee_rate if the fee output is an issued asset. The issued_asset_fee_rate_multiplier_int64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. From ca0c084791d3d87d7e057d89a9d1887d850d0f0b Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Mon, 12 Jan 2026 13:21:59 +0000 Subject: [PATCH 13/21] changed syntax for the multiplier and rate arithmetic to rust --- elip-0000.mediawiki | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index 9ddabab..5112f7b 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -44,37 +44,37 @@ If the current minimum relay fee rate is 1 sat/vbyte in LBTC, then the minimum f ====Issued asset fees==== -For each supported issued asset, there is a specified fee rate multiplier: issued_asset_fee_rate_multiplier (a float). This is represented internally as a fixed precision int64_t number issued_asset_fee_rate_multiplier_int64 (the number of decimal places supported is specified by the int64_t parameter ASSET_MULTIPLIER_SCALE_FACTOR (default 100000000) which is applied to all asset multipliers. +For each supported issued asset, there is a specified fee rate multiplier: issued_asset_fee_rate_multiplier (a float). This is represented internally as a fixed precision u64 (using Rust syntax) number issued_asset_fee_rate_multiplier_u64 (the number of decimal places supported is specified by the u64 parameter ASSET_MULTIPLIER_SCALE_FACTOR (default 100000000) which is applied to all asset multipliers. -issued_asset_fee_rate_multiplier_int64 = static_cast(std::ceil(issued_asset_fee_rate_multiplier * ASSET_RATE_SCALE_FACTOR)) +issued_asset_fee_rate_multiplier_u64: u64 = ((issued_asset_fee_rate_multiplier * ASSET_RATE_SCALE_FACTOR).ceil() as u64); This value scales the calculated the fee rate of a transaction that has an issued asset fee to give an effective_fee_rate. -effective_fee_rate = (asset_fee_rate * issued_asset_fee_rate_multiplier_int64) / ASSET_MULTIPLIER_SCALE_FACTOR +effective_fee_rate = (asset_fee_rate * issued_asset_fee_rate_multiplier_u64) / ASSET_MULTIPLIER_SCALE_FACTOR Where asset_fee_rate is the fee rate calculated using the transaction vsize and the amount of the issued asset fee output. For every transaction in the mempool that has an issued asset fee, the effective_fee_rate is used in place of the policyAsset fee rate for the purposes of mempool ordering and minimum mempool fee rate calculation. -The value of issued_asset_fee_rate_multiplier_int64 must be between 1 and MAX_ASSET_MULTIPLIER +The value of issued_asset_fee_rate_multiplier_u64 must be between 1 and MAX_ASSET_MULTIPLIER By default MAX_ASSET_MULTIPLIER = 10^15. Scaled as a fixed precision number, this enables values of issued_asset_fee_rate_multiplier between 0.00000001 and 10,000,000. -The value of (asset_fee_rate * issued_asset_fee_rate_multiplier_int64) cannot exceed the maximum allowed value of int64_t. If it does, then the effective_fee_rate is set to maximum allowable value of std::numeric_limits::max() / ASSET_RATE_SCALE_FACTOR = 92233720368 (which is 922.3372 LBTC/vbyte). +The value of (asset_fee_rate * issued_asset_fee_rate_multiplier_u64) cannot exceed the maximum allowed value of u64::MAX. If it does, then the effective_fee_rate is set to maximum allowable value of u64::MAX / ASSET_RATE_SCALE_FACTOR = 92233720368 (which is equivalent to 922.3372 LBTC/vbyte). To calculate the value of an issued asset fee output required for a transaction of size vsize where the current minimum policyAsset fee rate is policy_asset_rate: -fee_amount = vsize * policy_asset_rate * (1 + ((ASSET_RATE_SCALE_FACTOR - 1) / issued_asset_fee_rate_multiplier_int64)) +fee_amount = vsize * policy_asset_rate * (1 + ((ASSET_RATE_SCALE_FACTOR - 1) / issued_asset_fee_rate_multiplier_u64)) The minimum and maximum possible values of the fee rate multiplier limit the relative values of assets that can be used for fees, and as the multiplier gets closer to its minimum value the rounding rules will cause larger inaccuracies in the relative fees charged. Each node stores a local list of the accepted fee assets, corresponding rate multiplier (and optional destination). The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE (default 10). -Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is `policyAsset`) or the effective_fee_rate if the fee output is an issued asset. The issued_asset_fee_rate_multiplier_int64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. +Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is `policyAsset`) or the effective_fee_rate if the fee output is an issued asset. The issued_asset_fee_rate_multiplier_u64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently in BlockAssembler the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block. @@ -86,7 +86,7 @@ Asset fee rates multipliers (and an optional destination) may be encoded in addi * 4 bytes: AFEE * 32 bytes: asset ID -* 16 bytes: fee rate multiplier (uint_64 encoded) +* 16 bytes: fee rate multiplier (u64 encoded) * (up to) 35 bytes: fee destination script Nodes can be configured to perform this publication either every block, or every n blocks. From 5678602d46fd18d00b3bc55617cf0905b09170a8 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Mon, 12 Jan 2026 13:58:17 +0000 Subject: [PATCH 14/21] changed references to single fee outputs to general fees and added statement on handling mutiple fee assets in a single transaction --- elip-0000.mediawiki | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index 5112f7b..8674cd4 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -23,7 +23,7 @@ This document is licensed under the 3-clause BSD license. ===Motivation=== -Current policy rules for mempool acceptance and relay require that the assetID of the transaction fee output is the policyAsset. The consensus rules however allow for any assetID to be used for the fee asset (so long as the transaction is otherwise valid). The current restriction limits the usefulness of the Elements platform for transacting in issued assets, as policyAsset must be obtained before being able to transact. Block creators may choose to accept fee payments in other assets that have value, at specified exchange rates. +Current policy rules for mempool acceptance and relay require that the assetID of the transaction fee is the policyAsset. The consensus rules however allow for any assetID to be used for the fee asset (so long as the transaction is otherwise valid). The current restriction limits the usefulness of the Elements platform for transacting in issued assets, as policyAsset must be obtained before being able to transact. Block creators may choose to accept fee payments in other assets that have value, at specified exchange rates. Block creators can configure individual nodes explicitly with accepted assets for fees and rates, but to enable a federation to be managed with consistent fee policy (which may require frequent changes), a method to perform this configuration remotely via an on-chain transaction is also required. Individual nodes can then be configured with a 'controller' script that enables changes to the fee asset policy without requiring direct configuration of individual nodes. @@ -35,10 +35,10 @@ In order for wallets and users to know which assets are being accepted by block Each node has complete control over which assets and at what fee rates to accept as policy. However, in practice all block creators and relaying nodes should agree to apply the same asset fee policy in order to provide a reliable service for users. The assets and fee rates accepted by any node can be set as part of the node configuration, specifying the assetID, the relative rate multiplier (and optionally the coinbase destination for the fee collection by the block creator) before initialization, or this can be set dynamically at runtime. -For fee outputs in an issued asset, the fee rate (in units/vbyte) will be scaled to determine an 'effective rate'. This effective rate is then used in place of the policyAsset fee rate for the purpose of meeting relay and mempool minimums and for mempool ordering. +For fees in an issued asset, the fee rate (in units/vbyte) will be scaled to determine an 'effective rate'. This effective rate is then used in place of the policyAsset fee rate for the purpose of meeting relay and mempool minimums and for mempool ordering. E.g. An issued asset is USDT. The current accepted exchange rate between USDT and LBTC (the policyAsset) is $114,171.23 to 1 LBTC. The issued asset fee rate multiplier for this ratio is then set to 1/114,171.23 = 0.00000876 (rounded to a precision of 1e-8). For a transaction that uses the USDT asset for the fee, the fee rate (in units/vbyte) multiplied by this number must be greater than or equal to the minimum fee rate (in sat/vbyte) in LBTC applied to policy. -If the current minimum relay fee rate is 1 sat/vbyte in LBTC, then the minimum fee rate required in USDT would be 1/0.00000876 = 114156 units/vbyte (rounded up to the nearest integer). To relay this transaction, a client will determine the fee rate in the USDT asset, multiply it by 0.00000876 to calculate the effective rate and then verify it is greater than or equal to 1 sat/vbyte. +If the current minimum relay fee rate is 1 sat/vbyte in LBTC, then the minimum fee rate required in USDT would be 1/0.00000876 = 114156 units/vbyte (rounded up to the nearest integer). To relay this transaction, a node will determine the fee rate in the USDT asset, multiply it by 0.00000876 to calculate the effective rate and then verify it is greater than or equal to 1 sat/vbyte. ===Specification=== @@ -56,15 +56,15 @@ This value scales the calculated the fee rate of a transaction that has an issue effective_fee_rate = (asset_fee_rate * issued_asset_fee_rate_multiplier_u64) / ASSET_MULTIPLIER_SCALE_FACTOR -Where asset_fee_rate is the fee rate calculated using the transaction vsize and the amount of the issued asset fee output. For every transaction in the mempool that has an issued asset fee, the effective_fee_rate is used in place of the policyAsset fee rate for the purposes of mempool ordering and minimum mempool fee rate calculation. +Where asset_fee_rate is the fee rate calculated using the transaction vsize and the amount of the issued asset fee. For every transaction in the mempool that has an issued asset fee, the effective_fee_rate is used in place of the policyAsset fee rate for the purposes of mempool ordering and minimum mempool fee rate calculation. In the case a transaction has fees paid in more than one asset type, the effective_fee_rate is computed for the total fee of each asset type and then summed together. The value of issued_asset_fee_rate_multiplier_u64 must be between 1 and MAX_ASSET_MULTIPLIER By default MAX_ASSET_MULTIPLIER = 10^15. Scaled as a fixed precision number, this enables values of issued_asset_fee_rate_multiplier between 0.00000001 and 10,000,000. -The value of (asset_fee_rate * issued_asset_fee_rate_multiplier_u64) cannot exceed the maximum allowed value of u64::MAX. If it does, then the effective_fee_rate is set to maximum allowable value of u64::MAX / ASSET_RATE_SCALE_FACTOR = 92233720368 (which is equivalent to 922.3372 LBTC/vbyte). +The value of (asset_fee_rate * issued_asset_fee_rate_multiplier_u64) cannot exceed the maximum allowed value of u64. If it does, then the effective_fee_rate is set to maximum allowable value of u64::MAX / ASSET_RATE_SCALE_FACTOR = 92233720368 (which is equivalent to 922.3372 LBTC/vbyte). -To calculate the value of an issued asset fee output required for a transaction of size vsize where the current minimum policyAsset fee rate is policy_asset_rate: +To calculate the value of an issued asset fee required for a transaction of size vsize where the current minimum policyAsset fee rate is policy_asset_rate: fee_amount = vsize * policy_asset_rate * (1 + ((ASSET_RATE_SCALE_FACTOR - 1) / issued_asset_fee_rate_multiplier_u64)) @@ -72,11 +72,11 @@ fee_amount = vsize * policy_asset_rate * (1 + ((ASSET_RATE_SCALE_FACTOR - 1) / i The minimum and maximum possible values of the fee rate multiplier limit the relative values of assets that can be used for fees, and as the multiplier gets closer to its minimum value the rounding rules will cause larger inaccuracies in the relative fees charged. -Each node stores a local list of the accepted fee assets, corresponding rate multiplier (and optional destination). The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE (default 10). +Each node stores a local list of the accepted fee assets, corresponding rate multiplier (and optional destination). The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE. -Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is `policyAsset`) or the effective_fee_rate if the fee output is an issued asset. The issued_asset_fee_rate_multiplier_u64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. +Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is `policyAsset`) or the effective_fee_rate if the fee is in an issued asset. The issued_asset_fee_rate_multiplier_u64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. -Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently in BlockAssembler the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block. +Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block. ====Fee rate publication==== From b7159df0db8172c1fe6229b3cec40958dbb3797e Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Mon, 12 Jan 2026 14:23:11 +0000 Subject: [PATCH 15/21] statement added concerning rate publication and ibd --- elip-0000.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index 8674cd4..2a63351 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -91,7 +91,7 @@ Asset fee rates multipliers (and an optional destination) may be encoded in addi Nodes can be configured to perform this publication either every block, or every n blocks. -A new RPC can be added to retrieve the latest assets and multipliers for use by wallets. +A new RPC can be added to retrieve the latest assets and multipliers for use by wallets. Wallets need to wait until initial block download is complete before retrieving the latest multipliers to avoid using incorrect fee rates. ====Controller fee asset rate updates==== From c89c3b71b440f1c6f7dd66c884a9d7d3f0e47966 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Mon, 12 Jan 2026 15:09:34 +0000 Subject: [PATCH 16/21] added clarification about separation of controller and publication features --- elip-0000.mediawiki | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index 2a63351..1003e8f 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -29,6 +29,8 @@ Block creators can configure individual nodes explicitly with accepted assets fo In order for wallets and users to know which assets are being accepted by block creators for fees and at what rates, the block creators can publish this information in the coinbase of blocks they produce. This removes the requirement for separate servers to publish and relay this data. +The controller feature is independent of the fee rate publication feature: the controller can remotely set the fee assets and multipliers to be used as policy by the block signers. The block signers can then publish the assets and multipliers being used (however they are set) for users/wallets. + ==Design== ===Overview=== @@ -95,7 +97,7 @@ A new RPC can be added to retrieve the latest assets and multipliers for use by ====Controller fee asset rate updates==== -Feature that enables an external controller to set issued asset fee policy via an onchain transaction, with asset fee rate multipliers encoded in OP_RETURN outputs. +This enables an external controller to set issued asset fee policy for a federation via an onchain transaction, with asset fee rate multipliers encoded in OP_RETURN outputs. Any node can be configured to apply issued asset fee policy that is set by an external controller. This controller may be a single entity or several different entities which is given the authority to set fee policy. A node is then configured with the scriptPubKey that the controller (or multiple controllers, via a multisig script) can generate a valid witness for. From bcd0cb8620e8e98dfded56342fc5e523bdbd1a63 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Mon, 12 Jan 2026 15:38:31 +0000 Subject: [PATCH 17/21] added rules for the application of conflicting controller updates --- elip-0000.mediawiki | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index 1003e8f..d7e32c6 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -76,7 +76,7 @@ The minimum and maximum possible values of the fee rate multiplier limit the rel Each node stores a local list of the accepted fee assets, corresponding rate multiplier (and optional destination). The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE. -Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is `policyAsset`) or the effective_fee_rate if the fee is in an issued asset. The issued_asset_fee_rate_multiplier_u64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. +Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is policyAsset) or the effective_fee_rate if the fee is in an issued asset. The issued_asset_fee_rate_multiplier_u64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block. @@ -107,6 +107,10 @@ A valid controller transaction is defined by: 2. It spends from an output with the same specified controller scriptPubKey (this authenticates the transaction). 3. The other outputs are OP_RETURN with each having a fee asset and rate multiplier (encoding is that same as that used for the fee rate publication). +A node can be configured with both explicit assets and multipliers and a controller scriptPubKey. An asset and multiplier encoded in a valid controller transaction overrides any previously set and explicitly configured multipliers. + +Controller transactions become valid after 2 confirmations, and if there is more than one controller transaction in a block, they are applied in the sequence of the block verification. + ==Backwards Compatibility== No issues for backwards compatibility - all existing transactions with policy asset fees are unaffected by these features. No changes to consensus rules are required. From bfe7c53d88db3b4a0429bb1a90c94bb3a8e773b3 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Fri, 16 Jan 2026 15:44:09 +0000 Subject: [PATCH 18/21] update of publication statement Co-authored-by: Byron Hambly --- elip-0000.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index d7e32c6..548246d 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -29,7 +29,7 @@ Block creators can configure individual nodes explicitly with accepted assets fo In order for wallets and users to know which assets are being accepted by block creators for fees and at what rates, the block creators can publish this information in the coinbase of blocks they produce. This removes the requirement for separate servers to publish and relay this data. -The controller feature is independent of the fee rate publication feature: the controller can remotely set the fee assets and multipliers to be used as policy by the block signers. The block signers can then publish the assets and multipliers being used (however they are set) for users/wallets. +The controller feature is independent of the fee rate publication feature: the controller can remotely set the fee assets and multipliers to be used as policy by the block signers. The block signers can then publish the assets and multipliers being used for users/wallets. ==Design== From 21b9876a279a3d73bc243bb4d517831e7127e996 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Fri, 16 Jan 2026 15:44:46 +0000 Subject: [PATCH 19/21] add units to effective_fee_rate Co-authored-by: Byron Hambly --- elip-0000.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index 548246d..8df7cc6 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -52,7 +52,7 @@ For each supported issued asset, there is a specified fee rate multiplier: -This value scales the calculated the fee rate of a transaction that has an issued asset fee to give an effective_fee_rate. +This value scales the calculated the fee rate of a transaction that has an issued asset fee to give an effective_fee_rate in sat/vbyte. effective_fee_rate = (asset_fee_rate * issued_asset_fee_rate_multiplier_u64) / ASSET_MULTIPLIER_SCALE_FACTOR From 451f9861cb506b0e4d54727731276bce9b63681e Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Fri, 16 Jan 2026 15:45:10 +0000 Subject: [PATCH 20/21] fix typo Co-authored-by: Byron Hambly --- elip-0000.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index 8df7cc6..16200bb 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -76,7 +76,7 @@ The minimum and maximum possible values of the fee rate multiplier limit the rel Each node stores a local list of the accepted fee assets, corresponding rate multiplier (and optional destination). The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE. -Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is policyAsset) or the effective_fee_rate if the fee is in an issued asset. The issued_asset_fee_rate_multiplier_u64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If an fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. +Unconfirmed transactions are ordered in the mempool (for selection for inclusion in a block) by the policyAsset fee rate (if the fee asset is policyAsset) or the effective_fee_rate if the fee is in an issued asset. The issued_asset_fee_rate_multiplier_u64 for a specific asset may be changed while there are issued asset fee transactions in the mempool awaiting confirmation. When this happens, the effective_fee_rate is updated and the ordering updated with the new rate. If a fee asset is removed completely, the previous effective_fee_rate remains in effect for ordering. Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block. From 619bb3dde6169f714f29a9c1fe401e50680b96c7 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Fri, 16 Jan 2026 15:46:49 +0000 Subject: [PATCH 21/21] specify big endian for encoded fee rate multiplier --- elip-0000.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki index 16200bb..19a3907 100644 --- a/elip-0000.mediawiki +++ b/elip-0000.mediawiki @@ -88,7 +88,7 @@ Asset fee rates multipliers (and an optional destination) may be encoded in addi * 4 bytes: AFEE * 32 bytes: asset ID -* 16 bytes: fee rate multiplier (u64 encoded) +* 16 bytes: fee rate multiplier (u64 encoded big-endian) * (up to) 35 bytes: fee destination script Nodes can be configured to perform this publication either every block, or every n blocks.