Claim dashboard
The /claim route is the holder’s cockpit. It reads every piece of your on-chain
position — Tide-LP NFTs, accrued fees, vesting, and any lottery prize — and exposes
every write the protocol supports: pokeFees, claim / claimMany, processOwed,
withdrawVested, activatePrize / expirePrize, plus a buy/sell swap widget against the
canonical Uniswap V4 pool.
Everything on this page is keyed to the connected wallet. Until you connect, the main column shows a connect prompt — your positions and fees live against your address, not in any server.
Layout
Section titled “Layout”The dashboard is two columns:
| Column | Contents |
|---|---|
| Main | Position summary · Lottery panel (conditional) · Vesting card · Fees-by-position list |
| Sidebar | Get TIDE swap widget · Launch fee gauge · Wallet readout |
Position summary
Section titled “Position summary”Three tiles read from useUserPosition(address) and usePendingFees(ids):
| Tile | Source | Meaning |
|---|---|---|
| Your TIDE | balanceOf(you) | Your ERC-20 TIDE balance. |
| Tide-LP held | nftBalanceOf(you) | Live NFT count — one per whole TIDE held. |
| Accrued, unclaimed | Σ pendingFees(id) over owned ids | ETH leg (accent) + TIDE leg, summed across every NFT you own. |
“Accrued, unclaimed” is the sum of pendingFees(tokenId) for each owned token. That view
returns each NFT’s (eth, tide) accrual since its last checkpoint and excludes anything
not yet poked into the accumulators — so the figure can lag real swap fees until someone
calls pokeFees(). Use Sync fees (below) to refresh it.
Launch fee gauge
Section titled “Launch fee gauge”The sidebar Launch fee card renders the live FeeGauge from useProtocolStats():
currentFee, seeded, launchTime, and the two window lengths feeWindow1 / feeWindow2.
It shows which tier of the degressive schedule is active right now and counts down to the
next step:
| Tier | Pips | Rate | Window |
|---|---|---|---|
| 1 | 250_000 | 25% | t < feeWindow1 |
| 2 | 100_000 | 10% | feeWindow1 ≤ t < feeWindow2 |
| Final | 50_000 | 5% | t ≥ feeWindow2 |
t is block.timestamp − launchTime. The window lengths are read live from the hook’s
feeWindow1 / feeWindow2 immutables (Sepolia runs compressed test durations), but the
three tier rates are fixed constants. If the chain is unreachable the card shows a
Retry button. Full mechanics: Degressive launch fee.
Vesting card
Section titled “Vesting card”The Vesting card pairs a VestingBar with the write buttons that drive your vesting
tranche.
The bar
Section titled “The bar”VestingBar is fed claimableNow, lockedOf, vestEndsAt, and vestingDuration. It
splits your tranche into two legends — Claimable (claimableNow(you)) and Locked
(lockedOf(you)) — and fills a progress meter from the linear unlock:
start = vestEndsAt - vestingDurationpct = clamp(0, 100, (now - start) / vestingDuration * 100)When something is still locked it shows an unlocks in countdown to vestEndsAt, ticking
once per second. With nothing locked it reads fully unlocked; with no tranche at all it
prompts you to claim fees to start one. See 72-hour linear vesting.
Sync fees — pokeFees()
Section titled “Sync fees — pokeFees()”The card header carries a Sync fees button calling pokeFees() (permissionless,
unguarded). It pulls accrued swap fees out of the V4 position into the per-share
accumulators so your “Accrued, unclaimed” and per-position rows reflect reality.
Withdraw vested — withdrawVested()
Section titled “Withdraw vested — withdrawVested()”The primary button calls withdrawVested(), paying out everything currently unlocked
(claimableNow) as TIDE. It is disabled when claimableNow === 0. The button label
shows the exact amount, e.g. Withdraw 9.88 TIDE.
Convert owed — processOwed()
Section titled “Convert owed — processOwed()”A secondary Convert owed button appears only when you have raw owed fees
(owedEth > 0 or owedTide > 0). Owed fees are accruals captured involuntarily — when
an NFT you held moved or burned, the protocol harvested its pending fees into your
pendingETH / pendingTIDE buckets (see PendingCredited). They are not yet vested.
processOwed() converts them: it zeroes your owed buckets, buys TIDE in-pool with the ETH
leg (Buyback), and deposits tideOwed + bought into a fresh vesting tranche. There is no
direct ETH payout anywhere — the ETH leg is always converted to TIDE. The button label
itemizes what will convert, e.g. Convert owed (0.0021 ETH + 14.2 TIDE). A no-op (no owed
fees) surfaces “You had no owed fees to convert.”
Fees by position
Section titled “Fees by position”This card lists your earning NFTs and lets you claim them.
Claim all — claimMany(ids)
Section titled “Claim all — claimMany(ids)”The header Claim all button calls claimMany(tokenIds) over every owned id. In one
transaction it pokes fees, harvests each NFT to its current owner, then converts and
vests your owed. It is disabled while another action is pending or when you own no NFTs.
Per-position rows — claim(tokenId)
Section titled “Per-position rows — claim(tokenId)”Each NftFeeRow shows the token id (linked to the explorer), its pending ETH + TIDE, and a
per-row Claim button calling claim(tokenId). The list is capped at the first 60
rows; a note clarifies that Claim all still processes every position beyond the cap.
What a claim actually does (and re-reads)
Section titled “What a claim actually does (and re-reads)”A claim is not an ETH payout. For each harvested NFT the protocol credits the owner’s owed
buckets (Claimed), then _process buys TIDE with the ETH leg in the pool and deposits
the result into vesting (OwedProcessed → Vested):
pokeFees()— pull fresh fees into the accumulators.- Harvest each NFT’s accrual to its owner’s owed.
- Buy TIDE in-pool with your ETH owed (this is the in-pool buyback that creates buy pressure on every claim).
- Deposit
tideOwed + boughtinto your vesting tranche.
Lottery panel
Section titled “Lottery panel”The LotteryPanel renders only when you have a pending prize (prizeAmount > 0). It
appears between the position summary and the vesting card. You win a prize when another
holder sells before their vesting finishes and forfeits the locked remainder, and an
on-chain draw routes that forfeit to you. (Forfeits are biased by prevrandao; see
The forfeited-vesting lottery.)
The panel shows the prize amount and a countdown to prizeExpiresAt, and offers one of two
actions depending on the window:
| State | Button | Call | Effect |
|---|---|---|---|
| Within window | Activate prize | activatePrize() | Pull payment (you pay gas); re-vests the prize over a fresh vesting window. |
Window passed (expired or countdown ≤ 0) | Send to Treasury | expirePrize(you) | Permissionless; routes the un-activated prize to the Treasury. |
expirePrize is callable by anyone for any stale winner; the dashboard wires it to your own
address. Prize status (amount, expiresAt, expired) comes from the hook’s
prizeStatus(user) view.
Get TIDE — the swap widget
Section titled “Get TIDE — the swap widget”The sidebar SwapWidget is a Buy/Sell interface against the canonical V4 pool, routed
through the swap router (PoolSwapTest on Sepolia).
Native ETH in. The widget builds a zeroForOne: true swap with
amountSpecified = -ethIn and value = ethIn:
swapRouter.swap( POOL_KEY, { zeroForOne: true, amountSpecified: -ethIn, sqrtPriceLimitX96: limit }, { takeClaims: false, settleUsingBurn: false }, "0x",)limit comes from sqrtLimitForSlippage(spot, slippage, true). Every whole TIDE you
end up holding auto-mints one Tide-LP NFT (success toast: “Whole tokens auto-mint Tide-LP
NFTs in your wallet.”).
TIDE in. Selling first needs an ERC-20 approve of TIDE to the router — the widget
reads allowance(you, swapRouter) and shows an Approve TIDE button (sets maxUint256)
until the allowance covers the amount. The sell is zeroForOne: false,
amountSpecified = -tideIn.
Slippage, estimate, and price
Section titled “Slippage, estimate, and price”- Max slippage field, default
5, validated to0 < s ≤ 50. Out-of-range shows “Set a slippage between 0 and 50%.” - You receive (est.) and Min received are computed from spot price
(
usePoolPrice→tidePerEth) and the livecurrentFee; the estimate already nets the swap fee. - If the pool price is unavailable (
sqrtPriceX96 == 0), the widget warns “Pool price unavailable, the swap will run unprotected.” - Not connected → Connect wallet to trade; wrong network → Switch to {chain}.
?buy= deep-link prefill
Section titled “?buy= deep-link prefill”On mount the widget reads a buy query param and, if it is a positive decimal, switches to
the Buy tab and prefills the amount:
/claim/?buy=0.0123This is the target of the Get TIDE CTA on the Collection / My position card, which grosses up the ETH needed to clear your next whole-token threshold.
Wallet readout
Section titled “Wallet readout”When connected, the sidebar shows a Wallet card: your address (as a copyable pill), your
native ETH balance, and a Vest unlocks clock counting down to vestEndsAt (shown
only while you have something locked).
Related
Section titled “Related”- Claim = in-pool buyback — what a claim does on-chain.
- 72-hour linear vesting — the unlock curve and re-lock rule.
- The forfeited-vesting lottery — how prizes are drawn and activated.
- Degressive launch fee — the 25% → 10% → 5% schedule.
- TideHook — the contract behind every button on this page.
- Events & custom errors — the events the dapp verifies against.