Skip to content

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. TideMirror is a stateless DN404-style mirror; the hook mutates balances but the canonical Transfer / Approval / ApprovalForAll events are emitted from the mirror’s address via onlyHook callbacks. 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.
EventSignatureWhen it fires
SeededSeeded(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.
NFTMintedNFTMinted(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.
NFTBurnedNFTBurned(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.
FeesPokedFeesPoked(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
ClaimedClaimed(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.
PendingCreditedPendingCredited(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.
OwedProcessedOwedProcessed(address indexed user, uint256 ethConverted, uint256 tideBought, uint256 tideVested)Owed is converted — the ETH leg is bought back into TIDE in-pool (ethConvertedtideBought) and the total is vested (tideVested). There is no direct ETH payout.
VestedVested(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.
VestWithdrawnVestWithdrawn(address indexed user, uint256 amount)withdrawVested() pays out vested TIDE. Does not restart the vesting clock.
BuybackBuyback(uint256 ethIn, uint256 tideOut)An in-pool ETH→TIDE buyback executed (claim conversion path). The hook’s own swap is fee-exempt.
PrizeAwardedPrizeAwarded(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.
PrizeRedistributedPrizeRedistributed(uint256 amount)No eligible winner could be drawn; the forfeited amount is shared pro-rata across all live shares via the TIDE accumulator.
PrizeActivatedPrizeActivated(address indexed winner, uint256 amount)The winner called activatePrize() within the activation window; the prize re-vests like normal gains.
PrizeExpiredPrizeExpired(address indexed winner, uint256 amount)An un-activated prize passed its window and was routed to the Treasury (via permissionless expirePrize).
EventSignatureWhen it fires
BuybackBurnedBuybackBurned(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.
GuardianTransferredGuardianTransferred(address indexed from, address indexed to)The guardian role was handed off. The new guardian still can only burn.

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.

EventSignatureWhen it fires
TransferTransfer(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.
ApprovalApproval(address indexed owner, address indexed approved, uint256 indexed tokenId)Single-token approval set via the mirror’s approve.
ApprovalForAllApprovalForAll(address indexed owner, address indexed operator, bool approved)Operator approval set via the mirror’s setApprovalForAll.

Every revert in the protocol is one of these. They take no arguments unless noted.

ErrorPlain-English meaning
AlreadySeededseed() was already called. It is one-shot; the pool can never be re-seeded.
NotSeededThe pool has not been seeded yet. (Declared in the interface but not thrown anywhere in the current TideHook source — listed for completeness.)
ZeroAddressA required constructor address argument was the zero address.
NotOwnerOrApprovedThe caller is not the owner of the NFT and not approved (single approval or operator) to act on it.
InvalidTokenIdThe token id has no owner (never minted / already burned), or the from in a transfer does not match the actual owner.
MirrorOnlyA handleNFT* function was called by something other than the mirror. Clients must never call these directly — go through TideMirror.
TransferToZeroAn NFT transfer targeted address(0). Use a sell (which burns) to remove a share, not a transfer to zero.
SelfTransferDisallowedAn NFT transfer where from == to. Disallowed because it would be a free harvest of the position’s accrual.
InvalidDurationA bad constructor duration: a zero window, or feeWindow1 > feeWindow2. See Degressive launch fee.
SlippageExceededAn in-pool buyback returned fewer TIDE than the slippage floor (expected * (10_000 - MAX_BUYBACK_SLIPPAGE_BPS) / 10_000, i.e. a 10% band).
NoActivatablePrizeNo pending lottery prize exists for the caller, or its activation window has passed (context-dependent on activatePrize vs expirePrize).
TreasuryOnlyburn(amount) was called by something other than the Treasury. Only the Treasury can burn, and only its own held TIDE.
ErrorPlain-English meaning
GuardianOnlyA guardian-gated call (executeBuyback, transferGuardian) was made by a non-guardian.
ZeroGuardiantransferGuardian(to) was given address(0). The role cannot be handed to the zero address.
ErrorPlain-English meaning
OnlyHookAn emitTransfer / emitApproval / emitApprovalForAll callback was called by something other than the hook. These exist only so events log from the canonical ERC-721 address.
NonERC721ReceiverA safeTransferFrom target contract did not return the correct onERC721Received selector (or reverted).
ErrorPlain-English meaning
HookNotImplementedA 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.