# Holder rewards

A launch can send its creator tranche to the token's holders instead of the creator. On the create page
this is **Fees go to: Holders**. The trading fee is unchanged, and the protocol keeps its 0.5%.

> [!WARNING]
> This is a one-way door. Only the current recipient can move a fee stream, and a distributor has no
> function that could call `transferCreatorFeeRecipient`. Once a launch pays its holders, it pays them
> for the life of the token, barring the owner's [timelocked override](/documentation/trust).

## Launching with holder rewards

Set `creatorFeeRecipient` to the factory's `HOLDER_REWARDS()` constant:

```ts
const HOLDER_REWARDS = await client.readContract({
  address: FACTORY, abi: factoryAbi, functionName: "HOLDER_REWARDS",
});

const params = { ...tokenParams, creatorFeeRecipient: HOLDER_REWARDS, salt: freshSalt };
```

The factory resolves that to the token's distributor address itself, so the address cannot be
mispredicted. Use a **fresh salt** for each launch.

The distributor is a CREATE2 contract keyed by creator, salt and quote asset, and the launch names its
address before it has code. Fees can accrue to it from the first trade. If you want the address ahead
of time, call `rewardsFactory.predict(creator, salt, quoteToken)`. `POST /v1/launch/predict` with
`feesTo: "holders"` returns it as `distributor`, and gives you the value to send as
`paramsCreatorFeeRecipient`.

## How a distribution works

1. **Deploy.** `rewardsFactory.deploy(token, salt)` puts the contract in place. It is permissionless,
   and every argument is checked against the launch record, so it does not matter who sends it. The
   Tartarus jobs service does this soon after launch. `rewardsFactory.distributorOf(token)` is zero
   until then. The salt is in the `LaunchSalt` event.
2. **Sweep.** Fees reach the escrow under the distributor's address, like any recipient's.
3. **Harvest.** `distributor.harvest()` pulls that balance into its `unallocated` pot. Permissionless.
   The keeper runs it once about $1 is waiting.
4. **Snapshot.** About weekly, holder balances are read at one block. Each holder's share is
   `balance / eligible supply`, rounded down.
5. **Publish.** The shares become a Merkle tree, and the distributor owner calls
   `publishEpoch(root, total)`. An epoch can never commit more than `unallocated`. Epochs under about
   $1 are skipped and roll into the next one.
6. **Claim.** Holders have **90 days** to claim each epoch.
7. **Roll over.** After 90 days, anyone can call `rollOver(epochId)`. The unclaimed remainder returns
   to `unallocated` and funds a later epoch.

### Who is excluded

Addresses that hold supply but could never claim get no share, and they do not count toward eligible
supply: the zero address, the PoolManager, the locker, the hook, the escrow, the factory, the
launch-and-buy router and the distributor itself. Paying the pool's locked position would destroy the
money, so most of the balance held at launch is excluded.

This is enforced twice: off chain when the tree is built, and on chain by `isExcluded` inside `claim`.
A faulty root still cannot pay an excluded address.

## Checking an epoch

Everything needed to rebuild an epoch is public: `Transfer` events give the balances, `isExcluded` gives
the exclusions, and `leafFor(index, account, amount)` gives the exact leaf encoding:

```solidity
keccak256(abi.encode(index, account, amount))
```

Anyone can recompute the tree and compare it with `getEpoch(epochId).merkleRoot`. The distributor owner
can publish any root, so this check is what keeps that role honest. See
[Trust model](/documentation/trust).

## Claiming

The portfolio page claims every open epoch in one transaction. In your own client, the API supplies
the proofs:

```ts
const { claimable } = await fetch(`${API}/users/${account}`).then((r) => r.json());

for (const t of claimable.holders.byToken) {
  const open = t.epochs.map((e) => e.calldata);
  await wallet.writeContract({
    address: t.distributor, abi: distributorAbi, functionName: "batchClaim",
    args: [
      open.map((c) => BigInt(c.epochId)),
      account,
      open.map((c) => BigInt(c.amountRaw)),
      open.map((c) => BigInt(c.index)),
      open.map((c) => c.proof),
    ],
  });
}
```

`batchClaim` is all-or-nothing, so one bad proof reverts the batch. `claim(epochId, account, amount,
index, proof)` settles a single epoch.

Anyone may submit a claim, but the proof fixes the recipient. A claim always pays `account`, never
`msg.sender`. Each `(epochId, account)` pair claims once. Rewards arrive in the launch's quote asset,
straight from the distributor, not through the escrow.

| Error | Meaning |
|---|---|
| `InvalidProof` | The proof does not match this `(epoch, index, account, amount)` |
| `AlreadyClaimed` | Already settled |
| `ClaimWindowClosed` | More than 90 days old |
| `ExcludedAccount` | On the exclusion list |
| `NoEpoch` | That epoch does not exist |

## What the numbers mean

- **Claimable** is the sum of your leaves in epochs still inside their 90-day window. It is exact.
- **Pending** is an estimate: your share of what has accrued since the last epoch, from your current
  balance. It moves with every trade and transfer until the next snapshot.

Selling before a snapshot forfeits that epoch's share, and buying just before one earns a full share
of it. Only the balance at the snapshot block counts.