Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HIP-991 update according to latest discussion #1079

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
246 changes: 50 additions & 196 deletions HIP/hip-991.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
hip: 991
title: Permissionless revenue-generating Topic Ids for Topic Operators
author: Michael Kantor (@kantorcodes), Ty Smith (@ty-swirldslabs)
working-group: Michael Heinrichs (@netopyr)
requested-by: TierBot
type: Standards Track
category: Core
Expand All @@ -10,7 +11,7 @@ status: Accepted
last-call-date-time: 2024-07-24T07:00:00Z
created: 2024-06-14
discussions-to: https://github.com/hashgraph/hedera-improvement-proposal/pull/991
updated: 2024-09-11
updated: 2024-12-05
---

## Abstract
Expand Down Expand Up @@ -90,65 +91,48 @@ We propose adding a fixed fee mechanism to the Hedera Consensus Service (HCS) fo
* The Fee Schedule Key can be updated according to the same rules that currently apply to the Submit key. In addition, to update the Fee Schedule Key, the new key must sign the transaction.
* If the topic was created without a Fee Schedule Key, the key cannot be added later.

#### Fee Payment Pre-Requisites

* The fee payer must set an allowance (total fees and maximum fee per message) in HBAR or HTS tokens to be used to pay fees for message submissions on an HCS topic basis.
* Allowance granted to a Topic ID does not allow the Topic's Admin Key (or any of the Topic's keys) to spend that allowance outside of fee payments.

#### Fee Payment Pre-Requisites (SDK)

* When using the official SDK, the allowance transaction uses the `MAX_FEES` value for total fees and maximum fees per message by default. Developers must explicitly set these values if they don't want the defaults to be applied.
* When sending HCS messages via the SDKs, applications must set the `allowCustomFeesPayment` flag to `true`.
* The default value of the `allowCustomFeesPayment` flag is false.
* If the `allowCustomFeesPayment` flag is not set or is not true, the SDK returns an error.
* The `allowCustomFeesPayment` flag exists only in the SDK and isn't enforced by the network.

#### Allowance Transactions

* An account submits a transaction to grant an allowance (total fees and maximum fee per message) to a topic for both HBAR and HTS tokens. This transaction type reuses the existing allowance concept, with no need for a separate delete allowance transaction. To remove an allowance, the user will submit the same transaction with the allowance values set to 0 or a dedicated flag indicating allowance removal.
* This allowance only permits the topic to pay for the custom fees when messages are submitted, not for any other purpose.

#### Fee Payment

* The account submitting a message to the topic will cover network transaction fees, and if necessary, a custom fee via the approved allowance. The topic initiates the transfer of the custom fee to the fee collector using `transferFrom`, moving funds from the message sender's account to the designated fee collector.
* No balance will be held by the topic itself. Funds remain in the sender's account, and insufficient funds will result in the message submission failing with an appropriate error. The sender still pays network fees for failed transactions.
* A `ConsensusSubmitMessageTransactionBody` will include a new optional field `max_custom_fee` that a user can set to limit the paid custom fees.
* The account submitting a message to the topic will cover network transaction fees, and if necessary, a custom fee. The topic initiates the transfer of the custom fee to the fee collector using a synthetic `CryptoTransfer`, moving funds from the message sender's account to the designated fee collector.
* If the fee of submitting a message exceeds the `max_custom_fee`, the transaction will fail with an appropriate error. The sender still pays node and network fees for failed transactions.
* No balance will be held by the topic itself. Funds remain in the sender's account, and insufficient funds will result in the message submission failing with an appropriate error. The sender still pays node and network fees for failed transactions.

#### Fee Exclusions

* Topics can have a Fee Exempt Key List (FEKL)
* FEKL can be set at creation time
* FEKL can be updated with a `ConsensusUpdateTopicTransaction` signed by the topic's Admin Key
* FEKL has a maximum of `MAX_ENTRIES_FOR_FEE_EXEMPT_KEY_LIST` entries.
* If a TopicMessageSubmitTransaction is submitted to the network and it contains a signature from a key included in the FEKL, no fees will be charged for that message.
* Topics can have a Fee Exempt Key List
* The Fee Exempt Key List can be set at creation time
* The Fee Exempt Key List can be updated with a `ConsensusUpdateTopicTransaction` signed by the topic's Admin Key
* The Fee Exempt Key List has a maximum of `MAX_ENTRIES_FOR_FEE_EXEMPT_KEY_LIST` entries.
* If a TopicMessageSubmitTransaction is submitted to the network and it contains a signature from a key included in the Fee Exempt Key List, no fees will be charged for that message.

##### Handling Duplicates

If the FEKL list contains duplicate keys, the transaction will fail with an `FEKL_CONTAINS_DUPLICATED_KEYS` error. This ensures that duplicate entries are not silently ignored, preventing potential bugs or issues in the calling code.
If the Fee Exempt Key List contains duplicate keys, the `ConsensusCreateTopicTransaction`, respectively `ConsensusUpdateTopicTransaction`, will fail with a `FEE_EXEMPT_KEY_LIST_CONTAINS_DUPLICATED_KEYS` error. This ensures that duplicate entries are not silently ignored, preventing potential bugs or issues in the calling code.

##### Signatures for Invalid/Inactive/Deleted Accounts

The FEKL list will only require keys to be formally valid as per protobuf specifications, meaning they must be correctly formatted key structures. These are keys, not accounts. Even if an account associated with a key is inactive, deleted, or non-existent, the key itself can still be added to the FEKL.
The Fee Exempt Key List will only require keys to be formally valid as per protobuf specifications, meaning they must be correctly formatted key structures. These are keys, not accounts. Even if an account associated with a key is inactive, deleted, or non-existent, the key itself can still be added to the Fee Exempt Key List.

##### Threshold Keys

Threshold keys are supported in the FEKL. To avoid paying custom fees, the threshold must be met, meaning that the number of required signatures from the FEKL must be satisfied for a message submission. Any valid threshold key configuration will be processed normally.
Threshold keys are supported in the Fee Exempt Key List. To avoid paying custom fees, the threshold must be met, meaning that the number of required signatures from the Fee Exempt Key List must be satisfied for a message submission. Any valid threshold key configuration will be processed normally.

##### Pre-Filled Keys in FEKL
##### Pre-Filled Keys in Fee Exempt Key List

When creating a topic, the FEKL list is independent and must be explicitly populated. By default, the FEKL will not include the Admin Key, Submit Key, or Fee Schedule Key, unless explicitly provided in the creation or update transaction. The SDK might suggest default behavior, but the HIP doesn't enforce automatic inclusion of these keys in the FEKL.
When creating a topic, the Fee Exempt Key List is independent and must be explicitly populated. By default, the Fee Exempt Key List will not include the Admin Key, Submit Key, or Fee Schedule Key, unless explicitly provided in the creation or update transaction. The SDK might suggest default behavior, but the HIP doesn't enforce automatic inclusion of these keys in the Fee Exempt Key List.

### HIP Parameters

After discussing with the engineering team, the HIP parameters are defined as follow:
The HIP parameters are defined as follow:

* `MAX_CUSTOM_FEE_ENTRIES_FOR_TOPICS = 10`
* `MAX_ENTRIES_FOR_FEE_EXEMPT_KEY_LIST = 10`
* `MAX_FEE = unsigned int 64`

### User Flows and Interaction

* Users will specify the fee settings during the topic creation process through a simple interface in their Hedera client (refer to the creation of token custom fees/fixed fee for reference).
* Before submitting a message to a topic through an application or wallet interface, users must set an allowance and a maximum fee per message.
* When submitting a message to a topic with custom fees through an application or wallet interface, users must set the maximum fee for the message. Alternatively, users can add a flag to accept all custom fees from a topic id. If the topic has no custom fees, neither is required.
* In case of user wallets, applications show the custom fees to the user before submitting the message.
* Operators will get fee collections and distributions automatically through the custom fees just like they do in the token service currently.

Expand Down Expand Up @@ -193,31 +177,31 @@ message ConsensusCreateTopicTransactionBody {
repeated Key fee_exempt_key_list = 9;

/**
* The custom fee to be assessed during a message submission to this topic
* The custom fee to be assessed during a message submission to this topic. Empty if no custom fees are applied.
*/
repeated ConsensusCustomFee custom_fees = 10;
}
```

#### ConsensusUpdateTopicTransactionBody

The `ConsensusUpdateTopicTransactionBody` message is updated to include the optional Fee Schedule Key, the optional Fee Exempt Key List, and the `custom_fees` property for specifying fixed fees during topic creation.
The `ConsensusUpdateTopicTransactionBody` message is updated to include the optional Fee Schedule Key, the optional Fee Exempt Key List, and the `custom_fees` property for specifying fixed fees during topic update.

```protobuf
message ConsensusUpdateTopicTransactionBody {
[..]
/**
* Access control for update/delete of custom fees. Null if there is no key.
* Access control for update/delete of custom fees. Null if the key should not be updated.
*/
Key fee_schedule_key = 10;

/**
* If the transaction contains a signer from this list, no custom fees are applied.
* If the transaction contains a signer from this list, no custom fees are applied. Null if the list should not be updated.
*/
FeeExemptKeyList fee_exempt_key_list = 11;

/*
* The custom fee to be assessed during a message submission to this topic
* The custom fee to be assessed during a message submission to this topic. Null if the fees should not be updated.
*/
ConsensusCustomFeeList custom_fees = 12;

Expand All @@ -232,6 +216,25 @@ message ConsensusCustomFeeList {
}
```

#### ConsensusSubmitMessageTransactionBody

The `ConsensusSubmitMessageTransactionBody` message is updated to include the optional `max_custom_fees` property for specifying the maximum fee that the user is willing to pay for the message.
netopyr marked this conversation as resolved.
Show resolved Hide resolved

```protobuf
message ConsensusSubmitMessageTransactionBody {
[..]
/**
* The maximum custom fee that the user is willing to pay for the message. This field will be ignored if `accept_all_custom_fees` is set to `true`.
*/
repeated FixedFee max_custom_fees = 4;

/**
* If set to true, the transaction will accept all custom fees from the topic id
*/
bool accept_all_custom_fees = 5;
}
```

#### ConsensusTopicInfo

The `ConsensusTopicInfo` message is updated to include the Fee Schedule Key and the current list of custom fixed fees associated with the topic.
Expand All @@ -256,155 +259,15 @@ message ConsensusTopicInfo {
}
```

#### ConsensusApproveAllowanceTransactionBody

The `ConsensusApproveAllowanceTransactionBody` message is added and includes one or more `ConsensusCryptoFeeScheduleAllowance` and one or more `ConsensusTokenFeeScheduleAllowance` messages.

```protobuf
message ConsensusApproveAllowanceTransactionBody {
/**
* List of hbar allowances approved by the account owner.
*/
repeated ConsensusCryptoFeeScheduleAllowance consensus_crypto_fee_schedule_allowances = 4;

/**
* List of fungible token allowances approved by the account owner.
*/
repeated ConsensusTokenFeeScheduleAllowance consensus_token_fee_schedule_allowances = 5;
}
```

#### ConsensusCryptoFeeScheduleAllowance

This is a new protobuf message definition to enable crypto allowance for topics.

```protobuf
/**
* An approved allowance of hbar transfers for a spender.
*/
message ConsensusCryptoFeeScheduleAllowance {
/**
* The account ID of the hbar owner (ie. the grantor of the allowance).
*/
AccountID owner = 1;

/**
* The topic ID enabled to spend fees from the hbar allowance.
*/
TopicID spender = 2;

/**
* The amount of the spender's allowance in tinybars.
*/
uint64 amount = 3;

/**
* The maximum amount of the spender's token allowance per message.
*/
uint64 amount_per_message = 4;
}
```

#### ConsensusTokenFeeScheduleAllowance

This is a new protobuf message definition to enable token allowance for topics.

```protobuf
/**
* An approved allowance of fungible token transfers for a spender.
*/
message ConsensusTokenFeeScheduleAllowance {
/**
* The token that the allowance pertains to.
*/
TokenID tokenId = 1;

/**
* The account ID of the token owner (ie. the grantor of the allowance).
*/
AccountID owner = 2;

/**
* The topic ID enabled to spend fees from the token allowance.
*/
TopicID spender = 3;

/**
* The maximum amount of the spender's token allowance.
*/
uint64 amount = 4;

/**
* The maximum amount of the spender's token allowance per message.
*/
uint64 amount_per_message = 5;
}
```

### Mirror Node

To comply with this HIP, the Mirror Node provides the following features:

* Support for querying a topic's allowances and custom fees via REST APIs. This may include listing all accounts that have granted allowances to a particular topic.
* Support for querying a topic's custom fees via REST APIs.
* Custom fees should be able to be queried by topic, in the same structure as custom fees exist and can be queried on token entities.

#### REST API changes

Here is the list of endpoints affected by this HIP and the expected changes.

* `/api/v1/accounts/{idOrAliasOrEvmAddress}/allowances/crypto`
* The endpoint lists allowances set by both `CONSENSUSAPPROVEALLOWANCE` and `CRYPTOAPPROVEALLOWANCE`
* Changes to query parameters. Topic IDs are valid parameters for the `spender.id` field.
* Changes in the body of the response. The response includes the new `amount_per_message` field, and may include topic IDs in the spender field. A sample response payload follows.

```json
{
"allowances": [
{
"amount": 75,
"amount_per_message": 5,
"amount_granted": 100,
"owner": "0.0.2",
"spender": "0.0.2",
"timestamp": {
"from": "1586567700.453054000",
"to": "1586567700.453054000"
}
}
],
"links": {
"next": null
}
}
```

* `/api/v1/accounts/{idOrAliasOrEvmAddress}/allowances/tokens`
* The endpoint lists allowances set by both `CONSENSUSAPPROVEALLOWANCE` and `CRYPTOAPPROVEALLOWANCE`
* Changes to query parameters. Topic IDs are valid parameters for the `spender.id` field.
* Changes in the body of the response. The response includes the new `amount_per_message` field, and may include topic IDs in the spender field. A sample response payload follows.

```json
{
"allowances": [
{
"amount": 75,
"amount_per_message": 5,
"amount_granted": 100,
"owner": "0.0.2",
"spender": "0.0.2",
"timestamp": {
"from": "1586567700.453054000",
"to": "1586567700.453054000"
},
"token_id": "0.0.2"
}
],
"links": {
"next": null
}
}
```

* `/api/v1/topics/{topicId}`
* Changes to the body of the response. The response should include three new fields: `fee_schedule_key`, `fee_exempt_key_list`, and `custom_fee`. The fields will expose any data defined in their corresponding protobuffer definition. A sample response payload follows.

Expand Down Expand Up @@ -455,10 +318,6 @@ Here is the list of endpoints affected by this HIP and the expected changes.
}
```

* `/api/v1/transactions`
* Changes in query parameters. The new allowance transaction type `CONSENSUSAPPROVEALLOWANCE` is included in the list of supported transaction type for the `transactiontype` parameter.
* Changes in the body of the response. The endpoint returns the same fields as `CRYPTOAPPROVEALLOWANCE`.

### SDKs

This document does not include the details of the implementation updates required by the SDKs to comply with the HIP-991 specifications.
Expand All @@ -472,9 +331,7 @@ There are no known backward compatibility issues. Existing topics without custom

## Security Implications

The introduction of custom fees adds another layer of economic control, but also introduces potential vectors for abuse, such as fee manipulation. To address these issues, this HIP adheres to current security requirements regarding the authorization of moving user funds. In particular, before being able to send paid HCS messages to a topic, users should first set an allowance for the recipient topic, just as in the case of HTS, to allow payment of fixed or custom fees. The user can also set a maximum charge per message.

At the network level, the default value for both allowance parameters is 0. At SDK level, the fees (total maximum amount and maximum fee per message) are always the maximum amount `MAX_FEE`. For this reason, the SDK documentation should contain a considerable callout on this point and be clear about the implications of not changing these default values.
The introduction of custom fees adds another layer of economic control, but also introduces potential vectors for abuse, such as fee manipulation. To address these issues, this HIP adheres to current security requirements regarding the authorization of moving user funds. In particular, the user can set a maximum charge for each message.

## How to Teach This

Expand All @@ -496,7 +353,7 @@ const feeCollectorAccountId = AccountId.fromString("0.0.12345");

// Define the fixed fee
const topicCustomFee = new TopicCustomFee()
.setAmount(100) // 100 tokens are transferred to the fee collecting account each time this token is transferred
.setAmount(100) // 100 tokens are transferred to the fee collecting account each time a message is submitted to the topic
.setDenominatingTokenId(TokenId.fromString("0.0.56789")) // // The token to charge the fee in. HBAR if unset
.setFeeCollectorAccountId(feeCollectorAccountId); // 100 tokens are sent to this account for each HCS message to the topic

Expand All @@ -512,16 +369,14 @@ console.log(`Created topic with ID: ${receipt.topicId}`);

[...]

// Set allowance
const spenderTopicId = new TopicID("0.0.54321")
transactionResponse = new TopicAllowanceApproveTransaction()
.approveHbarAllowance(ownerAccount, spenderTopicId, Hbar.from(100), Hbar.from(2)); // Set owner, topicId, maximum allowance, and max per message

[...]
// Define max custom fee
const maxCustomFee = new FixedFee()
.setAmount(100) // a maximum of 100 tokens is paid for submitting the message
.setDenominatingTokenId(TokenId.fromString("0.0.56789")); // The token to charge the fee in. HBAR if unset

// Send message to the topic
transactionResponse = await new TopicMessageSubmitTransaction({ topicId: topicId, message: "Hello, HCS!" })
.setAllowCustomFeesPayment(true) // Make the agreement on payment explicit at the application level. Useful for any backend / non-front-user-facing application.
.setMaxCustomFees([maxCustomFee]) // Set the maximum fee for the message
.execute(client);

```
Expand All @@ -531,7 +386,6 @@ This implementation shows the creation of a topic where each message submission
## Rejected Ideas

* Setting a mandatory network enforced `allowCustomFeesPayment` flag in HCS message transactions to allow payment of fees.
* Setting 0 as default in SDKs for maximum fees.

## References

Expand Down
Loading