DOCUMENTATION
Nadir is a two-contract protocol layer attached to a fixed-supply token launched on Pons v2 (Robinhood Chain) and trading on Uniswap v4. It turns the token's creator fees into ETH yield for stakers and into buybacks that pay out bonds.
Overview
- FeeSplitter is the token's
creatorFeeRecipienton Pons. Anyone can callharvest(); it claims the accrued ETH and splits it between the treasury and the engine using a share fixed at deploy. - BondEngine receives the protocol share. Part is streamed to stakers as ETH, part is held as a reserve that buys the token back on Uniswap v4 every epoch. Bought tokens go to the crypt, which pays bonds.
- Bonds let holders deposit the token when it trades below its moving target and receive a bonus after vesting. The deeper the discount, the larger the bonus.
Lifecycle
- Deploy FeeSplitter with the treasury address and share.
- Launch on Pons with
creatorFeeRecipient = FeeSplitterand the chosencreatorTaxBps. Both the tax and the fact that fees route through the splitter are visible on-chain from block one. - Deploy BondEngine with the token address. The v4 pool id is derived on-chain from the Pons pool key. The deployer wires
FeeSplitter.setProtocol(engine)exactly once. - Curve phase. While the token is on the Pons bonding curve, fees accrue in the escrow and can already be harvested. The engine waits:
start()only succeeds once the v4 pool exists. - Graduation. Pons migrates liquidity to Uniswap v4. Anyone calls
start(), the first price sample is taken, and epochs begin. - Steady state. Every epoch a keeper calls
poke(). Stakers accrue ETH, buybacks fill the crypt, bonds mature and are settled in order.
Fee split
Pons charges a base fee on every trade (currently 1%, of which 70% goes to the creator) plus an optional creator tax chosen at launch and immutable afterwards. All creator proceeds are credited in ETH to the recipient's balance in the Pons FeeEscrow.
| Destination | Share | Mechanism |
|---|---|---|
| Treasury | treasuryBps of every harvest | Constant in FeeSplitter. Cannot be changed, cannot be paused. |
| Stakers | stakingShareBps of the protocol share | Added to a per-share accumulator the moment ETH arrives; claimable any time. |
| Buyback reserve | the rest | releaseBps of the reserve is swapped for the token every epoch and locked in the crypt. |
Oracle & target
The engine reads sqrtPriceX96 from the Uniswap v4 StateView for the exact pool key Pons created (currency0 = ETH, currency1 = token, fee 0, tick spacing 200, hook = Pons MemeHook). The value is stored as tokens per ETH once per epoch, in a ring buffer of up to 168 samples (7 days at 1-hour epochs).
Target is the arithmetic mean of the last window samples. Discount is how far the current spot is below the target: discount = (spotTPE − targetTPE) / spotTPE, where a higher tokens-per-ETH means a cheaper token. When spot is at or above target the discount is zero and bonds are closed.
window epochs of sustained price to move. Bonuses are computed against the target, not against the last trade.Bonds
bond(amount, minBonusBps) is available when the engine has at least minSamples samples, entries are not paused, and the discount is above zero.
entryBurnBpsof the amount is sent to the dead address.- The remainder is the principal and goes to the crypt.
- The payout is
principal × (1 + bonus), wherebonus = maxBonusBps × min(discount, bandBps) / bandBps. - The bond matures after
vestEpochsepochs.
Bonds are paid strictly first-in, first-out by settle(n), which anyone can call. A bond is paid when it has matured and the crypt holds at least its payout. If the crypt is short, the queue waits for the next buybacks; nothing is paid out of order.
exit(id) cancels an unsettled bond at any time: the owner gets the principal minus penaltyBps, and the penalty stays in the crypt for the other bondholders.
Buybacks
On every poke() the engine takes releaseBps of its ETH reserve and swaps it for the token directly on the Uniswap v4 PoolManager (unlock → exact-input swap → settle ETH → take tokens). The Pons hook applies its normal fees to that swap, exactly as it would for any trader. Tokens received are added to the crypt and counted in totalBoughtBack.
Releasing a fixed fraction per epoch makes the buyback a predictable, continuous bid that cannot be front-run for size: the amount is public before the epoch ends.
Staking
stake(amount) deposits the token; unstake(amount) withdraws it at any time. Rewards are ETH, tracked with a per-share accumulator. earned(user) shows what is claimable, and claimRewards() sends it. Staking never pauses withdrawals: only new deposits can be paused by the guardian.
Keepers
Three functions keep the protocol moving and all of them are permissionless:
| Function | When | Incentive |
|---|---|---|
| FeeSplitter.harvest() | whenever the escrow shows a balance | none needed; gas is negligible on Robinhood Chain |
| BondEngine.poke() | once per epoch | caller receives TIP_BPS of the reserve, capped at TIP_CAP |
| BondEngine.settle(n) | after bonds mature | none; bondholders will call it |
# reference keeper (Hardhat project)
ENGINE=0x… SPLITTER=0x… LOOP=1 npx hardhat run scripts/3-start-and-poke.js --network robinhood
Parameters
| Name | Default | Meaning |
|---|---|---|
| epochLength | 3600 s | one sample and one buyback per hour (immutable) |
| window | 24 | target = mean of the last 24 samples |
| minSamples | 6 | bonds open 6 epochs after start() |
| maxBonusBps | 5000 | +50% payout at the full discount band |
| bandBps | 5000 | full bonus reached at 50% below target |
| entryBurnBps | 100 | 1% of each bond entry burned |
| vestEpochs | 24 | bonds mature after 24 epochs |
| penaltyBps | 2000 | early exit returns principal minus 20% |
| releaseBps | 1000 | 10% of the ETH reserve bought back per epoch |
| stakingShareBps | 5000 | half of incoming ETH to stakers, half to the reserve |
Changing any parameter requires proposeParams() by the guardian followed by a public executeParams() at least 48 hours later. Bounds are enforced on-chain (for example, entry burn ≤ 10%, penalty ≤ 50%).
Security & governance
| Actor | Can | Cannot |
|---|---|---|
| Guardian | pause new bonds and stakes for ≤ 7 days per call; propose parameters with a 48h delay | move any token or ETH, block unstake / exit / settle / claims / poke, change addresses or the pool |
| FeeSplitter deployer | set the protocol address once | change shares, withdraw, set it twice |
| Anyone | harvest, poke, settle, start, executeParams after the delay | — |
- No proxy, no upgrade path. The bytecode you verify is the bytecode that runs forever.
- The token itself is the unmodified Pons launcher token: fixed supply, no tax logic, no owner.
- Reentrancy guards on every state-changing entry point; ETH is only sent via
callafter state updates.
Risks
- Bond queue risk. Bond payouts exceed principal. If fee inflows stop and no new bonds are opened, the queue can stall with matured bonds unpaid.
exit()is always available at the penalty, subject to crypt liquidity. - Price risk. Buybacks buy a volatile token. The crypt's ETH value can fall faster than it is refilled.
- Oracle risk. The target is a simple moving average of on-chain spot samples. A sustained manipulation over many epochs would move it.
- Smart-contract risk. The code is tested (unit and mainnet-fork) but not audited at the time of writing.
- Platform risk. Fee accrual depends on the Pons escrow and hook contracts, which are outside this protocol's control.
Contracts
| Contract | Address |
|---|---|
| BondEngine | see config |
| FeeSplitter | see config |
| Token | see config |
| Pons FeeEscrow | 0xd3AFEB2a57f70eF218Aa82451c51B2fb0416Ac9e |
| Pons MemeHook | 0xE5e702641Ea86F4ae6cC3cDaeD2B886f976Be044 |
| Uniswap v4 PoolManager | 0x8366a39cc670b4001a1121b8f6a443a643e40951 |
| Uniswap v4 StateView | 0xf3334192d15450cdd385c8b70e03f9a6bd9e673b |
FAQ
Why are rewards paid in ETH and not in the token?
Because the fees arrive in ETH and because a reward that does not come from printing the token is the only kind that does not dilute holders.
Who decides when to pause?
The guardian address, and only for new entries. Anyone can always withdraw. A pause expires by itself after at most 7 days unless renewed.
Can the creator tax change?
No. It is set once in the Pons launch transaction and is immutable. The fee recipient can only be changed through the Pons factory with a 3-day timelock, which is public.
What happens to fees before graduation?
They accrue in the escrow and can be harvested at any time. The engine holds the protocol share as reserve until the pool exists, then starts buying back.