# Reward index math

This page explains the math that underlies the reward accumulator in `TitheHook`. The pattern — sometimes called "Synthetix math" or "MasterChef math" — is the standard way to do O(1) proportional reward distribution on-chain.

### The problem

We need to pay every TITHE holder their pro-rata share of ETH fees, where:

* Fees arrive frequently (every swap on the pool)
* The holder set can be very large (thousands of addresses)
* We cannot iterate over all holders on every fee deposit
* Holders should be able to claim independently, at any time
* Holders should not lose rewards when transferring TITHE between wallets

A naive solution — store each holder's accrued balance, update on every deposit — is O(n) in holders per deposit. This doesn't work on Ethereum.

### The solution: a single global accumulator

We maintain one global value: `rewardPerTokenStored`. This represents the cumulative ETH paid per unit of TITHE since deployment, scaled by `PRECISION = 1e18` for precision.

On each fee deposit of `ethAmount` ETH from the hook's swap fee:

```solidity
rewardPerTokenStored += ethAmount * PRECISION / effectiveSupply
```

This single state write is O(1). It doesn't matter how many holders exist.

### Per-holder accounting

Each holder has two state variables:

```solidity
mapping(address => uint256) public userRewardPerTokenPaid;  // accumulator snapshot at last settlement
mapping(address => uint256) public pendingRewards;          // settled ETH waiting to be claimed
```

A holder's **pending** rewards at any moment is:

```
pending = balance × (rewardPerTokenStored − userRewardPerTokenPaid[holder]) / PRECISION
        + pendingRewards[holder]
```

The first term is what they've earned *since their last settlement*. The second term is what they earned earlier and haven't claimed yet.

When the hook settles a holder, it moves the first term into the second term and updates the snapshot:

```solidity
function _settle(address user) internal {
    uint256 balance = token.balanceOf(user);
    uint256 owed    = balance * (rewardPerTokenStored - userRewardPerTokenPaid[user]) / PRECISION;
    if (owed > 0) pendingRewards[user] += owed;
    userRewardPerTokenPaid[user] = rewardPerTokenStored;
}
```

After settlement, the first term goes back to zero (until the accumulator moves again or the balance changes).

### When settlement happens

Settlement is triggered automatically in two places:

1. **Before any TITHE transfer involving the holder** — the token's `_update` override calls `TitheHook.settleRewards(from, to)`, which calls `_settle` on both addresses. This ensures the sender doesn't lose accrued rewards by reducing their balance, and the recipient doesn't earn rewards for tokens they didn't hold yet.
2. **Inside `claim()`** — the hook first calls `_settle(msg.sender)` to ensure the claimer's `pendingRewards` is up to date before zeroing and transferring.

A read-only `pendingRewardsOf(address)` view computes the current pending value without modifying state, for UI display.

### Worked example

Assume:

* Effective supply = 800,000,000 TITHE (`8e26` wei in base units, where 1 TITHE = `1e18` wei)
* `rewardPerTokenStored` starts at 0
* Alice holds 8,000,000 TITHE (`8e24` wei) — 1% of effective supply
* Bob holds 80,000,000 TITHE (`8e25` wei) — 10% of effective supply

#### Event 1: Swap deposits 1 ETH (`1e18` wei) to the accumulator

```
rewardPerTokenStored += (1e18 × 1e18) / 8e26
                     = 1e36 / 8e26
                     = 1.25e9
```

#### Event 2: Alice checks her pending rewards

```
pending = (8e24 × 1.25e9) / 1e18 − 0
        = 1e16 wei
        = 0.01 ETH
```

Alice's pending = 0.01 ETH = 1% of the 1 ETH deposited. Correct (she holds 1% of supply).

#### Event 3: Bob checks his pending rewards

```
pending = (8e25 × 1.25e9) / 1e18 − 0
        = 1e17 wei
        = 0.1 ETH
```

Bob's pending = 0.1 ETH = 10% of the 1 ETH deposited. Correct.

#### Event 4: Another swap deposits 0.5 ETH

```
rewardPerTokenStored += (5e17 × 1e18) / 8e26
                     = 6.25e8

rewardPerTokenStored is now 1.875e9
```

#### Event 5: Alice claims

The hook calls `_settle(alice)`:

```
owed = (8e24 × (1.875e9 − 0)) / 1e18 = 1.5e16

pendingRewards[alice]        = 1.5e16
userRewardPerTokenPaid[alice] = 1.875e9
```

Then claim zeroes `pendingRewards[alice]` and transfers 0.015 ETH to Alice.

After the claim:

* `userRewardPerTokenPaid[alice]` = 1.875e9
* `pendingRewards[alice]` = 0
* Alice's wallet: gained 0.015 ETH

#### Event 6: Another swap deposits 2 ETH

```
rewardPerTokenStored += (2e18 × 1e18) / 8e26
                     = 2.5e9

rewardPerTokenStored is now 4.375e9
```

#### Event 7: Alice checks her pending again

```
pending = (8e24 × (4.375e9 − 1.875e9)) / 1e18 + 0
        = (8e24 × 2.5e9) / 1e18
        = 2e16 wei
        = 0.02 ETH
```

Alice's new pending = 0.02 ETH = 1% of the 2 ETH deposited since her last claim. Correct.

The math correctly accounts for: (a) only deposits that happened while she held the balance, (b) the fact that she's already claimed her earlier share, (c) her current balance proportion.

### Handling balance changes

The key insight is that **the math always uses the current balance** at the moment of settlement. The token's `_update` override calls `settleRewards(from, to)` *before* the balance change, so `_settle` uses the holder's pre-transfer balance — which is the correct balance for the period being settled.

After settlement, `userRewardPerTokenPaid` is set to the current `rewardPerTokenStored`. Then `super._update` runs and changes the balance. From the next settlement onward, the math uses the new balance.

This pattern is loss-free: a holder can transfer all their TITHE and their accrued rewards remain in `pendingRewards`, claimable at any future time.

### Why scale by 1e18

The accumulator stores rewards per token, but rewards (wei) are vastly smaller than supply (in wei × 10^18 because of TITHE's 18 decimals). Without scaling, integer division would round to zero for small deposits.

By scaling the accumulator by `PRECISION = 1e18`, we get precision down to 1 wei of reward per 1 wei of supply for any individual holder, even with deposits as small as a single wei. The `PRECISION` factor is divided back out when computing pending rewards, so the units balance.

### Precision and dust

There is a theoretical dust loss bounded by `balance / 1e18` wei per settlement, due to integer truncation in the division. For any holder with at least 1 TITHE (`1e18` wei), this is bounded at 1 wei per settlement — effectively zero. For holders with sub-1-TITHE balances (which would already be uneconomic to claim), the dust scales proportionally with their tiny balance.

### Effective supply: how it tracks

The hook computes `effectiveSupply` on each accrual:

```solidity
uint256 supply    = token.totalSupply();
uint256 excluded  = token.balanceOf(address(poolManager))
                  + token.balanceOf(address(this));
uint256 effective = supply > excluded ? supply - excluded : 0;
```

This is read at the moment of each fee accrual. Excluded balances are:

* The PoolManager's TITHE (LP collateral backing the locked liquidity)
* The hook's own TITHE balance (defensive exclusion; the hook never holds TITHE in practice)
* Implicitly, the zero address (burnt tokens are not in `totalSupply`)

Sablier streams are **not** excluded — tokens still in vesting earn the Blessing, accruing to the Sablier contract on behalf of the recipient. See Vesting for implications.

### Summary

The reward index pattern gives:

* **O(1) cost per deposit** regardless of holder count
* **O(1) cost per claim** regardless of how long the holder has held
* **Exact proportional distribution** with sub-wei precision for any reasonable balance
* **Loss-free transfers** — moving TITHE between wallets does not forfeit accrued rewards
* **Indefinite accrual** — there is no expiry on unclaimed rewards

This is why the same pattern is used by every major reward-distribution system in DeFi: it is the only known design that scales to arbitrary holder counts while preserving fairness.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tithe-coin.gitbook.io/tithe-coin/tithe-coin/mechanic/reward-index-math.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
