Introduce Fee module

LIP: 0048
Title: Introduce Fee module
Author: Maxime Gagnebin <maxime.gagnebin@lightcurve.io>
        Mitsuaki Uchimoto <mitsuaki.uchimoto@lightcurve.io>
Discussions-To: https://research.lisk.com/t/introduce-fee-module/318
Status: Draft
Type: Standards Track
Created: 2021-08-09
Updated: 2021-09-24
Requires: Define state and state transitions of Token module

Abstract

The Fee module is responsible for handling the fee of transactions. It allows chains to choose the token used to pay the fee and to define a minimum fee for transactions to be valid.

Copyright

This LIP is licensed under the Creative Commons Zero 1.0 Universal.

Motivation

This LIP defines the fee system in a modular way, as currently used in the Lisk ecosystem. The fee handling is implemented in a separate module to allow sidechains to freely update or replace the fee handling module, possibly to implement a more complex fee structure, without needing to modify or update the Token module.

Rationale

Fee Token ID

Each chain can configure the token used to pay fees. On the Lisk mainchain, the token used for transaction fees is the LSK token.

Minimum Fee per Transaction

As introduced in LIP 0013, all transactions must have a fee greater or equal to a minimum fee (which can be zero). The minimum fee is computed from a part based on the transaction size and a part specific to the command (called extra command fee). Chains can configure their minimum fee per byte and the eventual extra fees.

For example, on the Lisk mainchain, the following extra fees are defined:

  • extraCommandFee(MODULE_ID_DPOS, COMMAND_ID_DELEGATE_REGISTRATION) = 1000000000,
  • extraCommandFee(MODULE_ID_INTEROPERABILITY, COMMAND_ID_SIDECHAIN_REG) = 1000000000.

The constants MODULE_ID_DPOS and COMMAND_ID_DELEGATE_REGISTRATION are defined in LIP “Define state and state transitions of DPoS module”. The constants MODULE_ID_INTEROPERABILITY and COMMAND_ID_SIDECHAIN_REG are defined in LIP 0045.

Burning the Minimum Fee

The minimum part of the fee should be burned, this is to avoid that validators can send transactions in the blocks they generate without cost. This is only possible if the chosen fee token is a native token (with chain ID equal 0). If the chosen fee token is not a native token (for example the LSK token on sidechains), the minimum fee per byte and the extra fees should be set to zero. This is the only choice that makes sense, as burning non-native tokens is not supported by the Token module.

Specification

Notation and Constants

We define the following constants:

Name Type Value Description
General Constants
MODULE_ID_FEE uint32 1 Module ID of the Fee module.
Configurable Constants
MIN_FEE_PER_BYTE uint64 1000 Minimum amount of fee per byte required for transaction validity.
TOKEN_ID_FEE object TOKEN_ID_LSK = {
"chainID": 0,
"localID": 0
}
Token ID of the token used to pay the transaction fees.

Extra Fee

The Fee module allows to specify extra fees for each command. This is specified in the module configuration and is written as extraCommandFee(moduleID, commandID) in this LIP. All (moduleID, commandID) pairs that do not have a specified extra fee are assumed to have extraCommandFee(moduleID, commandID) = 0.

Fee Module Store

The Fee module does not store information in the state.

Commands

The Fee module does not contain any commands.

Protocol Logic for Other Modules

The Fee module does not expose any functions.

Block Processing

Verify Transaction

During trs verification, the following logic is applied:

minFee = MIN_FEE_PER_BYTE * size(trs)
         + extraCommandFee(trs.moduleID, trs.commandID)
if trs.fee < minFee:
     trs is invalid

Before Transaction Execution

Before a transaction trs is executed, the following logic is applied:

minFee = MIN_FEE_PER_BYTE * size(trs)
         + extraCommandFee(trs.moduleID, trs.commandID)
senderAddress is derived from trs.senderPublicKey
generatorAddress is the address of the generator of the block including trs

if TOKEN_ID_FEE.chainID == 0: # Fee token is a native token
    token.burn(senderAddress,
               TOKEN_ID_FEE,
               minFee)
    token.transfer(senderAddress,
                   generatorAddress,
                   TOKEN_ID_FEE,
                   trs.fee - minFee)
else:
    token.transfer(senderAddress,
                   generatorAddress,
                   TOKEN_ID_FEE,
                   trs.fee)

if any of those function fails, the transaction is invalid

The functions token.burn and token.transfer are defined in the Token module. Burning the fee was specified in LIP 0013.

Endpoints for Off-Chain Services

To be completed by the dev team.

Backwards Compatibility

This LIP defines a new Fee module, which follows the same protocol as currently implemented. Changing the implementation to include the Fee module will be backwards compatible.

Reference Implementation

TBA

2 Likes

I just created the pull request for this proposal on GitHub: Add LIP "Introduce Fee module" by MaximeGagnebin · Pull Request #101 · LiskHQ/lips · GitHub

The PR has been merged.