> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cosmos.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Fee Distribution

> Fee distribution mechanics and algorithms in the PoA module

# Fee Distribution

## Overview

The PoA module implements a custom fee distribution mechanism based on validator power. Unlike the standard Cosmos SDK x/distribution module, PoA uses a checkpoint-based system to allocate fees proportionally to validators without automatic distribution.

## How Fees Accumulate

Fees flow through the PoA system differently than standard Cosmos SDK:

1. **Block Fees**: Transaction fees collected in each block go to the `fee_collector` module account by default, or to the PoA module account if configured (see [Fee Routing Setup](#fee-routing-setup))

2. **Checkpoint System**: Allocated fees are updated for validators when:
   * Any validator power changes
   * Any validator withdraws fees

**Why Checkpointing?**: Ensures fair distribution when power changes. If power changes mid-period, fees are allocated based on old power distribution before the change takes effect.

**Location**: [`x/poa/keeper/distribution.go:18`](https://github.com/cosmos/cosmos-sdk/blob/main/enterprise/poa/x/poa/keeper/distribution.go#L18)

## Distribution Algorithm

### Checkpoint-Based Allocation

The PoA module uses a checkpoint system to allocate fees fairly when validator power changes. Rather than distributing fees actively at every block, allocation efficiently happens at discrete checkpoints.

**Checkpoint Triggers**:

* Any validator power change (via `MsgUpdateValidators`)
* Any fee withdrawal (via `MsgWithdrawFees`)

**Unallocated Fees Calculation**:

At checkpoint time $t$, calculate unallocated fees:

$$
U_t = B_{collector}(t) - A_{total}(t)
$$

Where:

* $U_t$ = unallocated fees at checkpoint $t$
* $B_{collector}(t)$ = current balance in the PoA module account
* $A_{total}(t) = \sum_{i=1}^{n} F_i(t)$ = sum of all previously- allocated fees across all validators (0 if no checkpoints have been done)

**Proportional Share Allocation**:

For each active validator $i$ (where $P_i(t) > 0$), allocate a share proportional to their power:

$$
S_i(t) = U_t \times \frac{P_i(t)}{P_{total}(t)}
$$

Where:

* $S_i(t)$ = share allocated to validator $i$ at checkpoint $t$
* $P_i(t)$ = voting power of validator $i$ at checkpoint $t$
* $P_{total}(t) = \sum_{j=1}^{n} P_j(t)$ = sum of all validator powers

**Accumulated Fees Update**:

After allocation, update each validator's accumulated fees:

$$
F_i(t+1) = F_i(t) + S_i(t)
$$

Where:

* $F_i(t)$ = validator $i$'s accumulated fees before checkpoint
* $F_i(t+1)$ = validator $i$'s accumulated fees after checkpoint
* $S_i(t)$ = share allocated in this checkpoint

**Total Allocated Tracking**:

Update the global allocated tracker:

$$
A_{total}(t+1) = A_{total}(t) + U_t
$$

After this checkpoint, $A_{total}(t+1) = B_{collector}(t)$ (all fees are now allocated).

### Example Checkpoint Sequence

**Initial State** (before checkpoint):

* PoA module account balance: $B_{collector} = 1000$ tokens
* Total allocated: $A_{total} = 400$ tokens (from previous checkpoints)
* Validator A: $P_A = 50$, $F_A = 200$ tokens allocated
* Validator B: $P_B = 50$, $F_B = 200$ tokens allocated
* Total power: $P_{total} = 100$

**Admin Action**: Admin submits `MsgUpdateValidators` to change power distribution to 30/70

**Checkpoint Triggered** (before power change takes effect):

1. Calculate unallocated: $U = 1000 - 400 = 600$ tokens

2. Allocate shares based on **current power** (50/50):
   * Validator A: $S_A = 600 \times \frac{50}{100} = 300$ tokens
   * Validator B: $S_B = 600 \times \frac{50}{100} = 300$ tokens

3. Update accumulated fees:
   * Validator A: $F_A = 200 + 300 = 500$ tokens
   * Validator B: $F_B = 200 + 300 = 500$ tokens

4. Update total allocated: $A_{total} = 400 + 600 = 1000$ tokens

**After Checkpoint** - Power Change Applied:

* Validator A: $P_A = 30$ (new power for future blocks)
* Validator B: $P_B = 70$ (new power for future blocks)
* All 1000 tokens now allocated ($A_{total} = B_{collector}$)
* Each validator has updated $F_i$ available for withdrawal

**Why This Matters**: Validator A earned 300 tokens (50% share) based on their power during the period when those fees were collected. After the checkpoint, their power drops to 30%, so future fees will be split 30/70. Checkpointing ensures validators are rewarded based on the work they actually performed.

**Precision**: Uses `DecCoins` (decimal coins) to prevent rounding dust accumulation. Each validator tracks fractional amounts that are too small to withdraw.

## Withdrawing Fees

**MsgWithdrawFees** ([`x/poa/keeper/msg_server.go:91`](https://github.com/cosmos/cosmos-sdk/blob/main/enterprise/poa/x/poa/keeper/msg_server.go#L91))

Any validator operator can withdraw accumulated fees:

1. **Submit Withdrawal**: Signed by operator address
2. **Checkpoint**: System checkpoints all validators first (allocates any pending fees)
3. **Truncate**: Decimal coins truncated to whole coins
4. **Transfer**: Coins transferred from the PoA module account to operator address
5. **Update Tracking**: Total allocated decreases by withdrawn amount
6. **Remainder**: Decimal remainder stays in validator's allocated balance

**Example**:

```
Validator has: 100.7543 utokens allocated
Withdrawal:    100 utokens transferred to operator
Remainder:     0.7543 utokens remain allocated (less than least significant utoken digit)
```

**Location**: [`x/poa/keeper/distribution.go:106`](https://github.com/cosmos/cosmos-sdk/blob/main/enterprise/poa/x/poa/keeper/distribution.go#L106)

## Withdrawal Formula

When validator $i$ withdraws fees:

$$
W_i = \lfloor F_i \rfloor
$$

$$
F_i' = F_i - W_i
$$

$$
A_{total}' = A_{total} - W_i
$$

Where:

* $W_i$ = amount withdrawn (truncated to integer coins)
* $F_i$ = validator's allocated fees before withdrawal
* $F_i'$ = validator's allocated fees after withdrawal (decimal remainder)
* $\lfloor F_i \rfloor$ = floor function (truncate decimals)
* $A_{total}'$ = updated total allocated across all validators

## Fee Routing Setup

PoA has its own module account for collecting fees. Enabling the PoA module account is recommended to keep fee accounting isolated and accurate. If not enabled, fees are deposited into the standard `fee_collector` account by default.

To enable the PoA module account, two wiring changes are required:

### 1. Register the PoA Module Account

Register `poatypes.ModuleName` in the `maccPerms` map passed to `authkeeper.NewAccountKeeper`:

```go  theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
app.AccountKeeper = authkeeper.NewAccountKeeper(
    appCodec,
    runtime.NewKVStoreService(storeKeys[authtypes.StoreKey]),
    authtypes.ProtoBaseAccount,
    map[string][]string{
        authtypes.FeeCollectorName: nil,
        govtypes.ModuleName:        {authtypes.Burner, authtypes.Staking},
        poatypes.ModuleName:        nil, // register PoA module account
    },
    // ...
)
```

**Source**: [`simapp/app.go`](https://github.com/cosmos/cosmos-sdk/blob/7bc1b146d437d834d971f415924104188203c96f/enterprise/poa/simapp/app.go#L191)

### 2. Configure the Ante Handler

Use `WithFeeRecipientModule` on `NewDeductFeeDecorator` to route fees to the PoA module account:

```go {9} theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
anteDecorators := []sdk.AnteDecorator{
    ante.NewSetUpContextDecorator(),
    ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
    ante.NewValidateBasicDecorator(),
    ante.NewTxTimeoutHeightDecorator(),
    ante.NewValidateMemoDecorator(options.AccountKeeper),
    ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
    ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker).
        WithFeeRecipientModule(poatypes.ModuleName), // redirect fees to PoA module account
    ante.NewSetPubKeyDecorator(options.AccountKeeper),
    ante.NewValidateSigCountDecorator(options.AccountKeeper),
    ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
    ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler, options.SigVerifyOptions...),
    ante.NewIncrementSequenceDecorator(options.AccountKeeper),
}
```

**Source**: [`simapp/ante.go`](https://github.com/cosmos/cosmos-sdk/blob/7bc1b146d437d834d971f415924104188203c96f/enterprise/poa/simapp/ante.go#L49)

`WithFeeRecipientModule` is backwards compatible — omitting it defaults to the standard `fee_collector` behavior.

## Security Considerations

1. **Decimal Precision**:
   * Uses DecCoins to prevent dust accumulation
   * Validators track fractional amounts
   * Remainders preserved across withdrawals
   * Prevents rounding errors from accumulating
