Hold = LP (DN404)
This is the mechanic everything else in TIDE rests on: holding the token is providing liquidity. There is no deposit, no staking, no LP NFT to mint by hand, and no range to manage. The entire fixed supply lives in one protocol-owned Uniswap V4 position, and your wallet’s balance of TIDE is, by construction, your slice of that position.
The bridge between “an ERC-20 balance” and “a tradable LP share” is a DN404-style mirror: a single share of state, presented through two token faces.
Two faces, one state
Section titled “Two faces, one state”TIDE uses a DN404-inspired architecture. The fungible and non-fungible representations of the same liquidity are exposed as two separate contracts, but all canonical state lives on the hook — the mirror holds none.
| Face | Token | Name / Symbol | Contract |
|---|---|---|---|
| ERC-20 | TIDE | "Tide" / "TIDE" | TideHook |
| ERC-721 | Tide-LP | "Tide-LP" / "TIDE-LP" | TideMirror (reached as hook.mirror()) |
The ERC-20 is the hook itself. The ERC-721 collection is a separate, stateless contract that forwards every mutating call back to the hook and emits the canonical Transfer/Approval events from its own address. For the exact function surface of each, see ../contracts/tidehook.md and ../contracts/tidemirror.md.
The invariant
Section titled “The invariant”The relationship between the two faces is a single hard invariant, maintained on every ERC-20 transfer:
nftBalanceOf(user) == balanceOf(user) / UNIT // integer (floor) divisionwith the constants from TideHook:
uint256 public constant SUPPLY = 10_000 ether; // 10,000 * 1e18uint256 public constant UNIT = 1 ether; // 1e18Each whole TIDE (one UNIT, i.e. 1e18 base units) entitles its holder to exactly one Tide-LP NFT. Because SUPPLY / UNIT = 10_000, the collection caps at 10,000 NFTs, and each whole NFT is 1/10,000 of the pool.
Fractional dust is not a share
Section titled “Fractional dust is not a share”The division is a floor. Any fractional remainder below a whole UNIT is dust: it sits in your ERC-20 balance, but it mints no NFT and earns no fees. It only becomes a share once it completes a whole token.
balance = 3.4 TIDE → nftBalance = 3 (0.4 TIDE is idle dust, earning nothing)balance = 4.0 TIDE → nftBalance = 4This matters because fees accrue per live NFT, not per token. Until your dust crosses the next whole-UNIT threshold, it is economically inert.
How balances mint, burn, and move NFTs
Section titled “How balances mint, burn, and move NFTs”NFTs are minted and burned purely by crossing whole-UNIT thresholds on the underlying ERC-20 — you never call a mint function. The sync engine is _afterTokenTransfer, which runs after every ERC-20 transfer and drives each affected party’s owned-NFT count toward its target balanceOf(party) / UNIT:
- Buying ETH → TIDE raises your balance. Each new whole token you cross mints one Tide-LP NFT to you (
NFTMinted). - Selling / transferring lowers your balance. Each whole token you drop below burns the tail NFT (
NFTBurned), concentrating everyone else’s fee share onto fewer live NFTs. - A wallet→wallet transfer of whole tokens moves existing NFTs from sender to receiver where both sides’ targets allow, minting/burning only the leftover difference.
The realignment logic distinguishes the two cases (the PoolManager, the hook itself, address(0), and the Treasury are excluded from being “users”):
// _realignSolo(user): one side is a useruint256 target = balanceOf(user) / UNIT;uint256 cur = _ownedLength(user);// mint (target - cur) NFTs, or burn (cur - target) tail NFTs
// _realignPair(from, to): both sides are users// move min(fromLoses, toGains) existing NFTs from -> to,// then burn the rest of from's surplus and mint the rest of to's shortfallOne NFT = one live share
Section titled “One NFT = one live share”totalShares counts only live (minted, not burned) NFTs. It is incremented on _mintNFT, decremented on _burnNFT, and is the denominator for all fee distribution. When fees are harvested into the pool’s per-share accumulators, each accumulator is bumped by amount * ACC_SCALE / totalShares (with ACC_SCALE = 1e12), so every live NFT accrues an identical pro-rata slice.
A direct consequence: when someone sells and burns NFTs, totalShares falls, and the same fee stream is divided among fewer shares — the surviving holders’ per-NFT accrual rises. This is the on-chain expression of “exits enrich those who stay.”
nftBalanceOf(owner) returns the realigned owned-NFT count (_ownedLength(owner)), which equals balanceOf(owner) / UNIT after realignment.
How the dapp surfaces this
Section titled “How the dapp surfaces this”Because fractional dust earns nothing, the dapp makes the shortfall to your next NFT explicit rather than letting idle dust go unnoticed. The “My position” view computes the exact amount needed to complete the next whole token:
const remainder = balance % UNIT;const need = remainder === 0n ? (balance === 0n ? UNIT : 0n) : UNIT - remainder;// need = UNIT - (balance mod UNIT)It renders a progress meter toward the next whole token, copy like “You’re N TIDE from your next Tide-LP,” and a Get-TIDE deep link that grosses need up into the ETH amount required to clear the threshold (accounting for the live price and current fee). When you are exactly whole (need === 0), it shows an “all set — no idle dust” state instead. See ../dapp/collection.md.
Why this design
Section titled “Why this design”- No impermanent loss, no ranges, no liquidations. You hold an ERC-20; the protocol owns and never re-ranges the single position. You exit only by selling
TIDEback into the same pool. - Composable. TIDE is a plain Solady ERC-20 with no transfer tax — it works with CEX deposits, bridges, and other DeFi, and does not trip honeypot/scam detectors.
- Each NFT is dual-purpose. A Tide-LP NFT is simultaneously a unit of fee accounting (1/10,000 of the pool) and a piece of fully on-chain generative art rendered deterministically from its
(tokenId, seed). See ../contracts/tideart.md.
Related
Section titled “Related”- ../contracts/tidehook.md — the ERC-20 + hook surface,
SUPPLY/UNIT, realign internals, and fee accounting. - ../contracts/tidemirror.md — the stateless ERC-721 mirror and its forwarding model.
- ./claim-and-buyback.md — how live shares accrue and claim fees.
- ../dapp/collection.md — the collection explorer and shortfall-to-next-NFT UI.