AFX LP Bonus Window: Reward Formula
This is the exact rule used to calculate rewards for the six month AFX/ETH liquidity program. It is published so that any participant can check the method against their own reading of on-chain data. Every input below is public.
See the program announcement for context, timing, and how to claim.
Scope
Pool. The AFX/ETH Uniswap V4 pool on Ethereum mainnet, pool id 0x0a1f2292e4f2f495c1deb64caf7dfed831fead1a39ce7a11c877eeab574c4ab8, on PoolManager 0x000000000004444c5dc75cB358380D2e3dE08A90. No other pool, chain, or venue accrues rewards.
Tokens. ETH (native) and AFX 0x9414E9af3a531E47f0F2B48d1a7fAf8178cF0BD7. Native ETH always sorts first in Uniswap V4, so ETH is currency0 and AFX is currency1 in this pool.
Positions. Only positions minted through the canonical V4 PositionManager 0xbD216513d74C8cf14cf4747E6AaA6420FF64ee9e, which is what "provide liquidity" on the Uniswap app does. Liquidity added directly to the PoolManager without an NFT is not counted. You keep custody of your NFT throughout. There is nothing to deposit and nothing to approve.
Owner attribution. Rewards accrue to the NFT's owner address at each moment, read from ERC-721 Transfer history. Positions owned by contracts such as Safes and smart wallets accrue normally. A position owned by the zero address, or burned, has its weight removed from the denominator rather than attributed to 0x0.
Excluded addresses, fixed for the whole window:
0x550F25f78DAF6CCBda6c4F77e239B15b6AD48A8B treasury multisig
0x1cFd452EB369a7B9475B07D1457dd1d0500fD788 deployer
These hold the project's own two liquidity positions in this pool. They earn nothing, so the entire 300,000,000 AFX goes to outside liquidity providers. The list is fixed at program start and cannot grow afterwards, because adding an address later would drop a published cumulative total to zero and strand that account's unclaimed proof.
Weight
At every instant, a position's weight is its ETH-side value if the position is currently in range, and zero otherwise:
in range ⇔ tickLower ≤ currentTick < tickUpper
weight = amount0 + amount1 · (2^96)² / sqrtPriceX96² (in ETH terms)
amount0 and amount1 are the position's current token amounts, derived from its liquidity and range at the current price. amount0 is the ETH side.
Concentration does not multiply weight. Weight is capital present, not liquidity units. A position that drifts out of range accrues nothing until price returns to it. This is why a full range position is the simpler choice for most providers.
Time integration
Accrual is event driven and continuous. There are no snapshots to game.
Every Initialize, Swap, and ModifyLiquidity event on the pool, and every PositionManager Transfer, is replayed in block and log order. Between consecutive events, each in-range position earns:
reward_i = rate(t) · Δt · weight_i / Σ weight
rate(t) is that epoch's budget divided by its duration.
Intervals in which no eligible position is in range emit nothing. That emission stays in the treasury and is reported as undistributed. It does not roll over and it is not re-emitted later.
Because excluded positions leave the denominator entirely, the project's own liquidity dilutes nobody. At every instant the emission is divided only among eligible providers. Both positions in the pool at program start are excluded, so the earliest outside providers take the full emission rate for as long as they are the only eligible in-range positions.
Schedule
The window opens on Monday 2026-08-03 and runs 180 days as six contiguous 30 day epochs of 2,592,000 seconds each, closing on or about 2027-01-30.
| Epoch | Days | Budget (AFX) |
|---|---|---|
| 1 | 0 to 30 | 60,000,000 |
| 2 | 30 to 60 | 55,000,000 |
| 3 | 60 to 90 | 50,000,000 |
| 4 | 90 to 120 | 47,500,000 |
| 5 | 120 to 150 | 45,000,000 |
| 6 | 150 to 180 | 42,500,000 |
Total 300,000,000 AFX, ratified by the 2026-07-24 governance vote.
All dates are approximate. Epoch 1 begins at the timestamp of the start block, a marker transaction posted on-chain that day, whose hash and block number are published in the announcement once it lands. Every epoch boundary is that timestamp plus a multiple of 2,592,000 seconds. The schedule is therefore anchored to a block, not to a clock, and the exact boundaries are knowable only after the start block exists. They are then fixed permanently.
Accrual starts the instant the marker block lands, and every eligible position in range at that moment begins earning together, split by ETH-side value. There is no advantage to adding liquidity on the day over adding it a week early. What matters is being in range when the window opens.
Claims
Rewards are published monthly. After each epoch, the per-address cumulative totals, the merkle root, and each address's proof are published at /afx-lp-reports, and the root is posted to the distributor contract 0xFA9f14E13FEece4eE62AC2b74d7194d64a857495.
Leaves are OpenZeppelin StandardMerkleTree pairs of ["address","uint256"], where the amount is a cumulative lifetime total. A claim pays the cumulative amount minus whatever that address has already claimed, so skipping a month loses nothing and a late publication costs nobody anything.
The distributor holds only the current month's rewards. It never holds liquidity positions.