Field guide / Rewards · 8 min read

Holder rewards and epochs

How a snapshot becomes a Merkle root every 30 minutes, and how holding time sets the size of every share.

Live, and clear about what is not.
A trade on the degen.zone curve pays about 4.19% all in. About 0.34% of that goes to degen.zone, about 0.84% to Meteora, and the rest, about 3.02% of volume, reaches the protocol. After graduation, at about 80 SOL raised, trading moves to a Meteora DAMM v2 pool that charges 1% regardless of the curve tier. Meteora keeps 20% of that, and the rest is split by liquidity share between the protocol's locked position and degen.zone's, so about 0.52% of volume reaches the protocol. The protocol's take per trade falls at graduation. It funds holder payouts in tokenized stocks, protocol-owned liquidity, a staking reserve and operations, in a fixed ratio the treasury program enforces. Staking itself is planned, phase 2: the allocation is set aside, but no staking program is deployed and nothing accepts a deposit. Values that have not been read from the chain render as a dash with the reason beside them.

An epoch every 30 minutes

The keeper runs one cycle every 1800 seconds, about 48 times a day. Each cycle claims fees, splits the vault, buys stock for the holder leg, snapshots holders and publishes an epoch. A cycle that finds nothing to distribute publishes nothing; there are no empty epochs.

The snapshot

Holders are enumerated from every $FLUX token account at one recorded slot. Balances owned by protocol addresses, pools, the keeper and the team leave the sample; the exclusion list is published in the deployment file with a reason per address. A minimum holding threshold, if set, is published the same way and stamped into every epoch file it applied to.

The snapshot decides who is eligible and records each wallet's eligible balance for that epoch. It does not, on its own, decide the size of anyone's share: that is set by holding time, the next section, and one snapshot is one sample in it. A balance that arrives and leaves between two snapshots is in no sample and earns nothing.

The longer you hold, the larger your share

Your share of a payout is not your balance at the snapshot. It is your average eligible balance over the last 48 snapshots, one per published epoch, which at the 30-minute cadence is 24 hours. The keeper folds the balance table of each of the last 47 epoch files together with the snapshot it has just taken, sums your balance across those samples, and divides by the number of samples. That figure is your weight; the epoch's total_shares is the sum of every weight; your share is your weight over that total. A payout is then that share of whatever the fee actually bought.

Read it as balance times time. A wallet that bought just before the snapshot is in one sample of 48 and weighs 1/48 of an equal wallet that held all day. Nothing resets and there is no cliff to time: the weight grows by one sample's worth every epoch you stay, and after you sell it decays by one sample's worth every epoch, so a wallet that has sold is still paid, at a falling weight, until it is out of the window. The rule is symmetric on purpose: it defeats buying for a payout and selling straight after, and it does not punish a wallet that genuinely held and then left.

Wallet, equal balance throughoutSamples presentWeight, relative to a full 24 hours
Held for the whole window48 of 48Full
Bought 12 hours ago24 of 48Half
Held all day, sold half its position 12 hours ago48 of 48, half of them at half the balance3/4
Bought just before this snapshot1 of 481/48
Sold everything 12 hours ago24 of 48, and falling by one every epochHalf, and falling

For epoch N, with W = 48 from payout_weighting.holding_window_epochs

window(N) = { N-W+1, ..., N }               clamped at epoch 1
S         = the ids in window(N) with a usable sample
weight    = floor( sum over s in S of balance[s] / |S| )
share     = weight / total_shares           total_shares = sum of every weight
payout    = floor( available x weight / total_shares )

The first day, gaps and quiet stretches

  • The window is counted in published epochs, not on the clock. A cycle with nothing to distribute publishes no epoch and consumes no id, so a quiet stretch does not age anyone out of the window: holding time is measured in payouts. 48 epochs is 24 hours only when an epoch is published every cycle.
  • The first day after launch is a shorter window. Epoch 1 has one sample, so it pays by current balance exactly as it would have without the rule; epoch k has k samples, a wallet present in all of them has full weight and a newcomer at epoch k has 1/k. The window reaches its full 48 samples at epoch 48.
  • A gap counts as zero. An epoch inside the window whose proof file is missing, or was written before the weights existed, contributes nothing for every wallet and is listed in the epoch file as missing. It is never interpolated and no earlier balance is carried into it, because either could be gamed. The divisor shrinks with the gap, so a gap dilutes nobody; it only means the holding time it would have witnessed is not counted.
  • Exclusions and the minimum holding threshold apply per sample. A wallet excluded, or below the threshold, at one snapshot is simply not in that sample.
  • The weighting changes how much of the pot each eligible wallet is weighted for. It does not change who is eligible, and it cannot tell one wallet from many under one owner.

Recompute your own weight

Every sample is public. Each epoch file carries a weighting block: the rule's version, the configured window, the ids whose balance tables it folded, the ids it could not, and one row per wallet with that epoch's own eligible balance and the weight it was paid by. The balance column of one epoch's file is the sample later epochs read, so nothing is needed beyond the files themselves.

  • Open the file for epoch N and read weighting.window, the ids it folded, ending in N.
  • For each id in the window, take your balance from that file’s weighting.holders row for your wallet; zero if you have no row.
  • Sum those balances and divide by the number of ids in the window, rounding down. That is your weighting.holders weight for epoch N.
  • Sum every wallet’s weight: that is total_shares. Your weight over total_shares is your share of the epoch, and each leaf addressed to you is that share of the stock the epoch allocated.
  • An id listed under weighting.missing is not re-checked; the rule is that an unavailable sample contributes zero.
The public epoch record

From shares to stock

The pot is every stock the payout leg bought this cycle plus stock harvested in kind from the protocol pools. It is valued at one Pyth reading for every feed at once, then allocated so that each holder receives their holding-time weighted share of the VALUE, converted into the stocks their basket names. Rounding to whole base units is repaired one unit at a time toward the holders who lost the most to flooring, never above their entitlement. Whatever remains is dust and stays in the treasury.

Publish, fund, activate

  • The keeper writes the epoch file (root, entries, every leaf and its proof, and every wallet’s balance sample and weight) to disk BEFORE publishing. A root whose leaves were never written down cannot be claimed against.
  • publish_epoch stores the root, the per-stock allocation and the publish time on chain.
  • The treasury funds each stock vault. activate_epoch refuses unless every vault holds at least its allocation.
  • Only an active epoch pays. The chain enforces that no epoch pays more than it was funded and that no leaf is claimed twice.

Merkle format

leaf = sha256(0x00 || claimant || epoch_id_le_u64 || mint || amount_le_u64)
node = sha256(0x01 || min(a, b) || max(a, b))

What is trusted, what is enforced

The keeper is trusted to compute honest allocations; that the leaves sum to the allocation, and that every weight is the average the published samples give, are keeper invariants the chain cannot check but anyone with the files can. Everything after publication is enforced: the root is immutable, a claim is bound to its epoch, stock and claimant, an epoch cannot pay more than its funded budget, and a stolen keeper key can move money only into program vaults.