Skip to content

Commit 2942692

Browse files
committed
reference level update
1 parent 14e0666 commit 2942692

1 file changed

Lines changed: 175 additions & 46 deletions

File tree

text/0045-transaction_fee_minting.md

Lines changed: 175 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
- Feature Name: transaction_fee_minting
1+
- Feature Name: Fee custom tokens
22
- Start Date: 2025-02-24
3-
- RFC PR: (to be created)
4-
- Hathor Issue: (leave this empty)
3+
- RFC PR: https://github.com/HathorNetwork/rfcs/pull/94
4+
- Hathor Issue:
55
- Author: Raul Soares de Oliveira
66

77
# Summary
@@ -15,7 +15,7 @@ This proposal suggests an alternative mechanism where, instead of requiring an u
1515

1616
Dozer suggested an alternative to the HTR deposit requirement when minting tokens. The idea is to create a new type of custom token where tokens would be minted for free (i.e., no deposits) and fees would be charged for transactions. [RFC](https://github.com/Dozer-Protocol/hathor-rfcs/blob/new-token-economics/projects/new-token-economics/token-economics.md)
1717

18-
This change would reduce the upfront cost of minting tokens, making it more accessible to users who may not have sufficient HTR at the time of minting.
18+
This change would reduce the upfront cost of minting tokens, making them more accessible to users who may not have sufficient HTR at the time of minting.
1919

2020
# Guide-level explanation
2121
[guide-level-explanation]: #guide-level-explanation
@@ -41,10 +41,17 @@ This proposal suggests **0.01 HTR per output**.
4141
Apart from accepting HTR for fee payment, any deposit-based token will be accepted. In this case, since the token was created with a 100:1 ratio of HTR ([deposit model](#deposit-based-model-as-is)), the fee needs to be 100x the HTR rate. That means **0.01 HTR or 1.00 deposit-based-token**.
4242

4343
### Fee and melting operations
44-
The fee will be charged before any melting operation and doesn't require any authority for it.
44+
Melting tokens without authority is forbidden. Fee payments will be accepted as long as their total is exactly equal to the required fee. Any other case will be rejected.
45+
46+
In the examples below we'll use Fee-based Token (FBT), and Deposit-based Token (DBT) as our tokens.
47+
48+
To accept deposit tokens, the transaction should have an amount >= 100, for example:
49+
50+
Inputs: [100 FBT, 150 DBT]
51+
Outputs: [100 FBT, 100 DBT]
52+
Since 50 DBT represents an withdraw of 0 HTR, this operation should be blocked and considered an attempt to melt without an authority.
4553

4654
For instance, if there is a transaction with:
47-
Fee-based Token (FBT), Deposit-based Token (DBT)
4855

4956
Inputs: [1000 FBT, FBT Melt Authority, 500 DBT]
5057
Outputs: [500 FBT, 400 DBT]
@@ -58,84 +65,205 @@ For a combination of paying the fee and also melting the token, we'll have the f
5865

5966
Here, 100 DBT is used to pay the fee, and there is a melt of 500 FBT and 100 DBT. For melting tokens, the behavior remains the same, requiring authority.
6067

68+
### Fee and nano contracts
69+
70+
Based on this [issue](https://github.com/HathorNetwork/rfcs/issues/97), the items below will be charged **0.01 HTR** when dealing with Fee-based tokens.
71+
- #### Sys-call
72+
- create_fee_token
73+
- mint_tokens
74+
- melt_tokens
75+
- #### Actions
76+
- Deposit
77+
- Withdraw
78+
79+
For transactions that are calling a Deposit/Withdraw action, they will be charged both from the outputs count and by the action cost. Nano contracts transferring funds between each other will pay fees based on the actions they might call.
80+
81+
Transaction depositing funds in a nano contract:
82+
```
83+
0.01 * len(outputs) + len(deposits)
84+
```
85+
If the same fee-based token is in the inputs but not in the outputs, we'll consider the same fee used for melting operations: 0.01 HTR (check [Fee cost](#fee-cost)).
86+
87+
Transaction withdrawing funds from a nano contract:
88+
```
89+
0.01 * len(outputs) + len(withdraws)
90+
```
91+
92+
Nano contracts transferring funds within a nano contract:
93+
```
94+
0.01 * (len(deposits) + len(withdraws))
95+
```
96+
97+
To demonstrate the above with an example we will consider:
98+
- A nano contract (nc1) that is already initialized.
99+
- The initial balance of the nc1 is 0.00 HTR.
100+
- The `create_token` method always creates 5 million FBT.
101+
- The `create_token` method always charges fees from its own balance.
102+
- The `no_op` method does nothing.
103+
- The nc1 doesn't call any other contract.
104+
105+
##### Creating a Fee-based token (FBT) - UTXO x Nano contract
106+
```
107+
Inputs: [100.02 HTR]
108+
Outputs: [1MM FBT]
109+
Nano header:
110+
nc_id: nc1,
111+
action: deposit (100.00 HTR), withdraw (1B FBT),
112+
method: create_token
113+
```
114+
Fee:
115+
- 0.01 HTR from the outputs count
116+
- 0.01 HTR from the `withdraw` action
117+
- 0.01 HTR from the `create_fee_token` sys-call <- charged from the contract's balance
118+
Contract State:
119+
- balance:
120+
HTR: 99.99
121+
FBT: 4 MM (million)
61122
### Fee destination
62123

63124
The fees will be burned.
64125

65-
## Explorer
126+
## Affected projects
127+
128+
The implementation in the following projects does not support fee payments with custom deposit-based tokens, although it will be available at the protocol level.
66129

67-
Add the fee field in the transaction view, and the token_info_version in the token creation transaction.
130+
### Wallet-lib
68131

69-
## Wallet (desktop, mobile, wallet-lib)
132+
This project handles methods that are responsible for preparing a transaction and choosing UTXOs that will serve as inputs. That said, with the Fee-based token version, those methods need to be refactored to handle it and prepare the UTXOs according to the requirements described in this document.
70133

71-
Since Hathor has an easy-to-use approach, we should provide users the option to select between the two models (deposit and fee) when minting. Also, in the wallet, we should always require fee payment in HTR to incentivize HTR demand.
134+
#### Wallet-service
72135

73-
**At the protocol level, other tokens are accepted**.
136+
The wallet service has its own database with the token details data stored. It will be necessary to add the token `version` field introduced by this document, since before it wasn't used and was hardcoded.
137+
### Explorer
138+
139+
The Explorer service ingestors are prepared to bring all columns from the wallet-service database into Elasticsearch, where they will be used by the Explorer service to provide data for the token detail API. We will update the token list view, token detail view, and transaction detail view by adding fee data and token version descriptions.
140+
141+
#### Wallet-headless
142+
143+
This is the official headless wallet of Hathor. There we'll need to update the create tokens methods (utxo and nano) arguments to support the new token version.
144+
145+
#### Rpc-lib
146+
147+
The rpc lib acts as a bridge between the wallet-lib and the desktop and mobile clients. It's required to update the create tokens handlers (utxo and nano) to accept the token version argument.
148+
149+
### Wallet (desktop, mobile)
150+
151+
The wallets will have a new create token experience. It will allow the user to choose between the tokens versions (deposit or fee based), see the fees and check the token detail data.
152+
153+
Also, we'll provide information about the transaction fee while approving a transaction and before sending it. The user will be able to check the paid fee in the transaction detail screen, similar to explorer.
74154

75155
# Reference-level explanation
76156
[reference-level-explanation]: #reference-level-explanation
77157

78158
In this section, technical details are expanded for what was described above.
79159

80-
## Token info version
81-
Given Hathor's [Anatomy of a Transaction RFC](https://github.com/HathorNetwork/rfcs/blob/master/text/0015-anatomy-of-tx.md#token-creation), it is reasonable to suggest that the byte used by the token creation transaction `token_info_version` will be used to determine fee-created tokens.
160+
## Token info version (aka Token Version)
161+
Given Hathor's [Anatomy of a Transaction RFC](https://github.com/HathorNetwork/rfcs/blob/master/text/0015-anatomy-of-tx.md#token-creation), it is reasonable to suggest that the byte used by the token creation transaction `token_info_version` will be used to determine fee-based tokens.
82162

83163
Since each custom token `id` is the hash of the token creation transaction that created it, we can assume the enum values below can be assigned to the `token_info_version` byte in the token creation tx and then we can retrieve it.
84164

85-
So, by adding a TokenInfoVersion enum we have:
165+
So, by adding a TokenVersion enum we have:
166+
- NATIVE = 0 (internal)
86167
- DEPOSIT = 1 (as is)
87168
- FEE = 2
88169

89170
Then, we must allow the versions above in the `deserialize_token_info` method by checking the enum values.
90171

91-
## Transaction token_dict
92-
From the section above, we know how to differentiate between deposit and fee tokens. Based on that, we need to add the input and output data to the current `token_dict` in order to calculate the outputs and check for melting operations without outputs.
172+
## Transaction class
173+
174+
Inside the transaction class we can obtain a dictionary `token_dict: dict[TokenUid, TokenInfo]` which is generated by the `get_complete_token_info()` method. This method iterates over the inputs and the outputs consolidating the amount, and checking the authorities of each token. To determine the token info it requires checking the spent txs and fetching the spent outputs for each token input.
93175

94-
## Fee (fee.py)
95-
This new file is responsible for keeping the fee logic, but not for orchestrating it; this will be done in the transaction verifier.
176+
With the spent outputs and the authorities in hand we can define which of them are what we call `chargeable_outputs` (non authority outputs).
96177

97-
The `should_charge_fee` method will check if the transaction has any fee-based input or output and if the `FEE_FEATURE_FLAG` is enabled.
178+
By creating `TokenInfoDict` class, that extends `dict[TokenUid, TokenInfo]`, we add two counters: chargeable_outputs and chargeable_spent_outputs. Since the previous token_dict iterates over the outputs as said before, incrementing the counters here saves resources by avoiding the need to retrieve the information elsewhere.
98179

99-
The `calculate_fee` method will receive a `token_dict` as input, then count the fee-based non-authority outputs and apply a constant value `FEE_OUTPUT_VALUE` to each, returning the total fee in HTR.
180+
##### Calculate fee method:
181+
With the counters in hand we just need to add a method to calculate the fee, below is the method implementation:
100182

101-
It will consider 1 output for melting operations that don't have any output.
102-
It won't consider mint and melt authorities.
183+
```python
184+
def calculate_fee(self, settings: 'HathorSettings') -> int:
185+
fee = 0
186+
if self.chargeable_spent_outputs > 0 and self.chargeable_outputs == 0:
187+
fee += settings.FEE_PER_OUTPUT
188+
189+
fee += self.chargeable_outputs * settings.FEE_PER_OUTPUT
190+
return fee
191+
```
103192

104-
The `collect_fee` method will receive the `token_dict` and the fee. It will use the differences between the input and output amounts and return the paid fee in HTR. For each token that has a difference, it will collect the fee by incrementing the amount value in the corresponding `token_dict` entry.
193+
As mentioned in the [fee cost](#fee-cost) section:
194+
>"For melting operations that don't contain any outputs, we'll count them as 1 output in the fee calculation."
105195
106196
## Transaction verifier
107197

108-
### verify_fee()
109-
Inside the transaction verifier, the `verify_fee` method will be added. It's responsible for orchestrating the fee flow by checking if the fee should be charged, calculating it, and checking the payment availability:
198+
### verify_sum()
199+
200+
With the new `TokenVersion` enum and the `calculate_fee` from the `token_dict` we'll be able to distinguish between fee and deposit based tokens.
201+
202+
From this point we'll accept an imbalance of HTR and deposit-based tokens; they should be used to pay fees.
203+
204+
As mentioned in the guide-level explanation:
205+
> Melting tokens without authority is forbidden. Fee payments will be accepted as long as their total is exactly equal to the required fee. Any other case will be rejected.
206+
207+
The expected htr amount should be calculated this way:
110208

111-
Example:
112209
```python
113-
if not should_charge_fee(token_dict):
114-
return
115-
116-
fee = calculate_fee(token_dict)
117-
paid_fee = collect_fee(token_dict)
118-
119-
if fee - paid_fee > 0:
120-
raise InputOutputMismatch(
121-
'HTR or deposit tokens are not enough to pay the fee. (amount={}, expected={})'.format(
122-
paid_fee,
123-
fee,
124-
))
210+
htr_expected_amount = (withdraw + withdraw_without_authority) - deposit - fee
125211
```
126212

127-
### verify_sum()
213+
where:
214+
withdraw: amount of HTR resulting from melting deposit-based tokens with authority
215+
withdraw_without_authority: amount of HTR resulting from melting deposit-based tokens without authority
216+
deposit: required amount in HTR for minting deposit-based tokens with authority
217+
218+
While iterating over the tokens in the tx we should consider:
219+
220+
Deposit tokens:
221+
- It should sum all the deposit based tokens withdraws with melt authority
222+
- It should sum all the deposit-based tokens withdrawals without a melt authority:
223+
- When the token amount * 0.01 (deposit rate) is not an integer, it will be **treated as an attempt to melt without an authority**.
224+
- For example, `99 DBT` doesn't represent `1 HTR` and an `InputOutputMismatch` error will be raised.
225+
- Also, `101 DBT` and `199 DBT` represent `1 HTR` and an `InputOutputMismatch` error will be raised, blocking the melting.
226+
- It will **reject** any deposit-based token which tries to **mint without an authority**.
227+
- It'll **reject** if the sum of the **withdrawals without an authority** is **higher than** the `fee`. If this validation fails, an `InputOutputMismatch` error will be raised.
228+
229+
For fee tokens:
230+
- It will reject any Fee-based token which tries to **mint** or **melt** without an authority.
231+
232+
Let's use the same example from the guide-level explanation using the following tokens: Deposit-Based Token (DBT), and Fee-Based Token (FBT).
233+
234+
Inputs: [2 HTR, 100 FBT, 200 DBT, 100 DBT3, 100 DBT3 melt authority]
235+
Outputs: [
236+
1 HTR,
237+
50 FBT,
238+
50 FBT,
239+
200 DBT2,
240+
DBT2 mint authority,
241+
DBT2 melt authority
242+
]
243+
Fee: 2 HTR
244+
245+
diffHTR = 2 HTR
246+
fee = 2 HTR
247+
withdraw_without_authority = 2 HTR (resulting from 200 DBT)
248+
withdraw = 1 HTR (resulting from 100 DBT3)
249+
deposit = 2
250+
251+
The balance check is valid:
252+
253+
diffHTR + fee = withdraw + withdraw_without_authority - deposit
254+
-1 + 2 = 1 + 2 - 2
255+
1 = 1
128256

129-
Since we already normalized the `token_dict` values in the `verify_fee` method before calling this one, we just need to adjust the withdraw and deposit amounts here, collecting only for deposit tokens.
130257

131258
## Feature flag in settings
132-
For development purposes, this feature will be feature-flagged to run only on the local network by setting the `FEE_FEATURE_FLAG` in settings to true.
259+
For development purposes, this feature will be feature-flagged to run only on the local network by setting the `ENABLE_FEE_TOKENS` in settings to true.
133260

134261
## Feature activation
135262
For production, we'll rely on feature activation to release this feature.
136263

137-
## Transaction fee resource
138-
Add an endpoint to calculate fees based on the inputs and outputs, in order to expose the logic used in the transaction verifiers to the wallets.
264+
## Transaction resource
265+
266+
We should provide a field with the tx fee and the token version in the detailed response.
139267

140268
# Drawbacks
141269

@@ -164,9 +292,8 @@ The Base Fee is burned, reducing the Ether supply, while the Priority Tip goes t
164292
# Unresolved questions
165293
[unresolved-questions]: #unresolved-questions
166294

167-
- How should melt operations be handled for fee-based custom tokens?
168-
- ~~How will fee adjustments be governed?~~
169-
- By removing the transaction fee from mining, how will it affect the tx-mining-service?
295+
- ~~How should melt operations be handled for fee-based custom tokens?
296+
- ~~How will fee adjustments be governed?
170297

171298
# Future possibilities
172299
[future-possibilities]: #future-possibilities
@@ -182,3 +309,5 @@ The Base Fee is burned, reducing the Ether supply, while the Priority Tip goes t
182309
- https://eips.ethereum.org/EIPS/eip-1559
183310
- https://ethereum.org/en/developers/docs/gas/
184311
- https://developer.bitcoin.org/devguide/transactions.html#transaction-fees-and-change
312+
- https://beta-test.docs.hathor.network/explanations/features/nano-contracts/
313+
- https://beta-test.docs.hathor.network/explanations/features/nano-contracts/how-it-works/

0 commit comments

Comments
 (0)