Events & custom errors
This page is the canonical reference for everything TIDE logs and everything it reverts with. The contracts emit no telemetry beyond what is listed here, and they revert only with the custom errors below — there are no string-revert messages to parse.
Two architectural facts shape this list:
- State lives on the hook, ERC-721 events come from the mirror.
TideMirroris a stateless DN404-style mirror; the hook mutates balances but the canonicalTransfer/Approval/ApprovalForAllevents are emitted from the mirror’s address viaonlyHookcallbacks. Wallets, marketplaces, and indexers MUST watch the mirror address for NFT ownership — not the hook. - The hook emits both ERC-20 accounting events and lifecycle events. Fee accrual, vesting, the lottery, and buybacks are all hook events.
Events to listen / index
Section titled “Events to listen / index”| Event | Signature | When it fires |
|---|---|---|
Seeded | Seeded(uint256 indexed posmTokenId, uint160 sqrtPriceX96, uint128 liquidity) | Once, on the one-shot seed() — pool initialized and the full SUPPLY minted into a single V4 position. posmTokenId is the PositionManager token id of the hook’s LP. |
NFTMinted | NFTMinted(address indexed to, uint256 indexed tokenId) | A buy/transfer pushes a holder across a whole-UNIT threshold, minting one Tide-LP share. First minted id is 1. |
NFTBurned | NFTBurned(address indexed from, uint256 indexed tokenId) | A sell/transfer drops a holder below a whole UNIT, burning the tail NFT and concentrating fees onto remaining shares. |
FeesPoked | FeesPoked(uint256 ethGained, uint256 tideGained) | pokeFees() pulls accrued ETH/TIDE fees out of the V4 position into the per-share accumulators. **Only emitted when `ethGained > 0 |
Claimed | Claimed(uint256 indexed tokenId, address indexed owner, uint256 ethOut, uint256 tideOut) | An NFT’s accrual is harvested to its owner’s owed buckets (via claim / claimMany). Always credits the current owner, never the caller. |
PendingCredited | PendingCredited(address indexed user, uint256 ethAmount, uint256 tideAmount) | Involuntary harvest: when an NFT moves or burns, the departing holder’s accrual is captured into their owed buckets. |
OwedProcessed | OwedProcessed(address indexed user, uint256 ethConverted, uint256 tideBought, uint256 tideVested) | Owed is converted — the ETH leg is bought back into TIDE in-pool (ethConverted → tideBought) and the total is vested (tideVested). There is no direct ETH payout. |
Vested | Vested(address indexed user, uint256 amountAdded, uint256 lockedTotal, uint256 vestEnd) | A vesting tranche is (re)started. A new claim re-locks the still-locked remainder over a fresh window; vestEnd = block.timestamp + vestingDuration. See 72-hour linear vesting. |
VestWithdrawn | VestWithdrawn(address indexed user, uint256 amount) | withdrawVested() pays out vested TIDE. Does not restart the vesting clock. |
Buyback | Buyback(uint256 ethIn, uint256 tideOut) | An in-pool ETH→TIDE buyback executed (claim conversion path). The hook’s own swap is fee-exempt. |
PrizeAwarded | PrizeAwarded(address indexed winner, uint256 amount, address indexed forfeitedBy) | A seller forfeited unvested TIDE and the lottery drew a random eligible holder as winner. See the forfeited-vesting lottery. |
PrizeRedistributed | PrizeRedistributed(uint256 amount) | No eligible winner could be drawn; the forfeited amount is shared pro-rata across all live shares via the TIDE accumulator. |
PrizeActivated | PrizeActivated(address indexed winner, uint256 amount) | The winner called activatePrize() within the activation window; the prize re-vests like normal gains. |
PrizeExpired | PrizeExpired(address indexed winner, uint256 amount) | An un-activated prize passed its window and was routed to the Treasury (via permissionless expirePrize). |
| Event | Signature | When it fires |
|---|---|---|
BuybackBurned | BuybackBurned(uint256 tideBurned) | The guardian called executeBuyback(); the Treasury’s entire TIDE balance was burned, permanently reducing supply. This is the Treasury’s only outward action. |
GuardianTransferred | GuardianTransferred(address indexed from, address indexed to) | The guardian role was handed off. The new guardian still can only burn. |
TideMirror (Tide-LP NFT) events — what wallets & marketplaces index
Section titled “TideMirror (Tide-LP NFT) events — what wallets & marketplaces index”These are the standard ERC-721 events. They originate from the mirror’s address, emitted by the hook through onlyHook callbacks (emitTransfer / emitApproval / emitApprovalForAll). Index these, not the hook, for NFT ownership.
| Event | Signature | When it fires |
|---|---|---|
Transfer | Transfer(address indexed from, address indexed to, uint256 indexed tokenId) | NFT moved, minted (from = address(0)), or burned (to = address(0)). Off-chain rank tenure (heldSince) is derived from the most recent incoming Transfer. See TideMirror. |
Approval | Approval(address indexed owner, address indexed approved, uint256 indexed tokenId) | Single-token approval set via the mirror’s approve. |
ApprovalForAll | ApprovalForAll(address indexed owner, address indexed operator, bool approved) | Operator approval set via the mirror’s setApprovalForAll. |
Custom errors
Section titled “Custom errors”Every revert in the protocol is one of these. They take no arguments unless noted.
| Error | Plain-English meaning |
|---|---|
AlreadySeeded | seed() was already called. It is one-shot; the pool can never be re-seeded. |
NotSeeded | The pool has not been seeded yet. (Declared in the interface but not thrown anywhere in the current TideHook source — listed for completeness.) |
ZeroAddress | A required constructor address argument was the zero address. |
NotOwnerOrApproved | The caller is not the owner of the NFT and not approved (single approval or operator) to act on it. |
InvalidTokenId | The token id has no owner (never minted / already burned), or the from in a transfer does not match the actual owner. |
MirrorOnly | A handleNFT* function was called by something other than the mirror. Clients must never call these directly — go through TideMirror. |
TransferToZero | An NFT transfer targeted address(0). Use a sell (which burns) to remove a share, not a transfer to zero. |
SelfTransferDisallowed | An NFT transfer where from == to. Disallowed because it would be a free harvest of the position’s accrual. |
InvalidDuration | A bad constructor duration: a zero window, or feeWindow1 > feeWindow2. See Degressive launch fee. |
SlippageExceeded | An in-pool buyback returned fewer TIDE than the slippage floor (expected * (10_000 - MAX_BUYBACK_SLIPPAGE_BPS) / 10_000, i.e. a 10% band). |
NoActivatablePrize | No pending lottery prize exists for the caller, or its activation window has passed (context-dependent on activatePrize vs expirePrize). |
TreasuryOnly | burn(amount) was called by something other than the Treasury. Only the Treasury can burn, and only its own held TIDE. |
| Error | Plain-English meaning |
|---|---|
GuardianOnly | A guardian-gated call (executeBuyback, transferGuardian) was made by a non-guardian. |
ZeroGuardian | transferGuardian(to) was given address(0). The role cannot be handed to the zero address. |
| Error | Plain-English meaning |
|---|---|
OnlyHook | An emitTransfer / emitApproval / emitApprovalForAll callback was called by something other than the hook. These exist only so events log from the canonical ERC-721 address. |
NonERC721Receiver | A safeTransferFrom target contract did not return the correct onERC721Received selector (or reverted). |
BaseHook
Section titled “BaseHook”| Error | Plain-English meaning |
|---|---|
HookNotImplemented | A Uniswap V4 hook entrypoint that TIDE does not implement was invoked. TideHook overrides only beforeSwap, afterSwap, and the unlock callback; all other entrypoints revert with this. |
See also
Section titled “See also”- TideHook — the contract that emits the fee, vesting, and lottery events.
- TideMirror (Tide-LP NFT) — where the ERC-721 events originate.
- Treasury —
BuybackBurnedand the no-rug invariant. - Architecture overview — how the hook, mirror, art, and treasury fit together.