afloat is one coin and one program. The coin has a fixed supply of one billion and trades wherever Solana coins trade. The program owns the coin's creator fee account and is called the hull, because that is what it does: it sits under the coin and keeps it up.
Every claim of creator fees is swapped into one of three paired tokens and locked in a vault the program controls. There are three vaults, called tanks, holding USDC, SOL and ZEC. Nothing leaves a tank except to buy the coin back or to pay the coin's holders, and both of those only happen when a trade big enough to count lands in the pool.
There are no timers, epochs or schedules anywhere in the program. Every state change is caused by a trade. The program reads the pool's reserves, measures how much they moved since it last looked, and fires only if the move is large enough. Anyone can press the crank; a bot presses it so you do not have to.
Below, each piece is explained next to the live numbers it produces. The numbers are read from the program's own accounts on Solana and from Birdeye. If a number is missing, the thing it describes has not happened yet.
The hull is three tanks. Each claim of creator fees arrives as SOL and is routed to whichever tank is furthest below its target share of the hull, then swapped into that tank's token and deposited. The target shares are 40% USDC, 35% SOL and 25% ZEC. USDC is there so the hull keeps a floor in flat money, SOL so it rises with the chain it lives on, and ZEC so part of the hull is uncorrelated with both. When the program spends from a tank it records the tank's cost basis in SOL at fill, so the site can show which tank has gained most since it was filled. That gain is what decides which tank fires.
| tank | balance | value in SOL | cost basis SOL | gain | fills | spends |
|---|---|---|---|---|---|---|
| USDC | ||||||
| SOL | ||||||
| ZEC |
The waterline is a price the program remembers. It is set the first time the hull claims fees, at the pool price in that slot, and from then on it can only rise. When a buy wave lands and the price is above the waterline, the waterline moves up by one eighth of the distance between them. It never moves down and it never moves on a sell.
Everything else is measured against it. If the price is above the waterline the gap is called freeboard. If the price is below it the gap is called draft, as a fraction of the waterline. Draft is what sizes the hull's buys and what gives a buyer their depth factor. A price exactly at the waterline, or within half a percent either side, is trim, and in trim nothing fires.
| slot | price before | price after | waterline before | waterline after | step |
|---|
A trade's size is measured as displacement: how many tokens the pool's token reserve moved since the program last recorded it, divided by the supply. Forty basis points of supply is the threshold. Below it, the crank instruction refuses to run and nothing changes. At or above it, the move is a wave, and the crank records the new reserves, the price, the draft and whatever it did about it.
Because displacement is read from the pool reserves and not from a feed or an indexer, the measurement is the same for everyone and can be checked by anyone with an RPC node. The crank is permissionless. A bot watches the pool and presses it the moment a trade clears the threshold, but anybody could.
| slot | kind | displacement | price | draft | action | amount | tank |
|---|
All 256 recorded waves are on the register page.
When a sell wave lands below the waterline the hull answers it. The spend is the hull's value in SOL multiplied by the draft and by one half, capped at one fifth of the chosen tank. The chosen tank is the one whose value has gained most against its cost basis since it was filled, so the hull always sells its strongest asset to buy the coin. The coin it buys is burned in the same instruction. Supply falls, the hull's share of what remains rises, and the next wave is measured against a smaller supply.
The table shows what the hull would spend right now at each draft, from the live hull value and the live tank balances. It is arithmetic on real numbers, not a forecast.
| draft | uncapped spend SOL | cap, 20% of | spend SOL | tokens at live price | share of supply |
|---|---|---|---|---|---|
| 5% | |||||
| 10% | |||||
| 15% | |||||
| 20% | |||||
| 30% | |||||
| 40% | |||||
| 50% | |||||
| 60% |
Holders are paid in the paired token out of the hull. On a buy wave above the waterline the program releases two percent of the selected tank into a payout round. The round is split by weight, and a wallet's weight is its balance multiplied by its depth factor.
The depth factor rewards buying under the waterline. It is one plus four times the wallet's average entry draft, so a wallet that bought everything at a forty percent draft carries a factor of 2.6 and a wallet that bought at the waterline carries 1.0. The cap is 5.0. Selling any amount resets the wallet's factor to 1.0; the balance keeps its weight, the depth is gone. There is no time in the formula, only where you bought relative to the waterline.
The payout bot computes each round from the on chain trades and holder balances, publishes the round's merkle root and totals in a round account the program owns, and then sends the tokens. The root is what you check.
| entry draft | factor | weight per 1,000,000 tokens |
|---|---|---|
| 0% | 1.00 | 1,000,000 |
| 10% | 1.40 | 1,400,000 |
| 20% | 1.80 | 1,800,000 |
| 30% | 2.20 | 2,200,000 |
| 40% | 2.60 | 2,600,000 |
| 50% | 3.00 | 3,000,000 |
| 60% | 3.40 | 3,400,000 |
| 100% | 5.00 | 5,000,000 |
Every round is listed on the register page.
If the draft reaches sixty percent the coin is breached. The waterline freezes where it is. The hull stops paying holders and defends: on each sell wave it spends as above, but it will not take any tank below the floor reserve, which is thirty percent of the hull's value in SOL at the moment of the breach. The floor is recorded in the state account the moment it is set, so it cannot be moved afterwards.
A breach closes when a buy wave carries the price back over the frozen waterline. That is the refloat. The waterline unfreezes, payouts resume, and the breach is written to the register with its deepest draft, the number of waves it lasted and what the hull spent inside it. Breaches never leave the register.
| opened at slot | closed at slot | deepest draft | waves | spent SOL | burned |
|---|
Everything above is decoded in your browser from one program account and a handful of token accounts. You can do the same with any RPC node. The state account is a program derived address with the seeds "hull" and the coin's mint, under the program id. Its layout is fixed and written out here, offsets in bytes, all integers little endian.
| offset | size | field | meaning |
|---|---|---|---|
| 0 | 8 | discriminator | Anchor account discriminator |
| 8 | 32 | mint | the coin's mint |
| 40 | 32 | pool_token_vault | the pool's token account for the coin |
| 72 | 1 | bump | PDA bump |
| 73 | 1 | mode | 0 buoyant, 1 trim, 2 submerged, 3 breached, 4 refloated |
| 74 | 8 | waterline | lamports per 1,000,000 base units |
| 82 | 8 | last_price | same units, at the last crank |
| 90 | 8 | last_token_reserve | pool token reserve at the last crank, base units |
| 98 | 8 | last_slot | slot of the last crank |
| 106 | 8 | total_bought | coin base units bought back |
| 114 | 8 | total_burned | coin base units burned |
| 122 | 8 | wave_count | waves since creation |
| 130 | 8 | breach_count | closed breaches |
| 138 | 8 | breach_open_wave | wave index that opened the open breach, 0 if none |
| 146 | 8 | floor_reserve | lamports |
| 154 | 8 | spent_in_breaches | lamports, cumulative |
| 162 | 8 | hull_value | lamports, at the last crank |
| 170 | 8 | round_count | payout rounds published |
| 178 | 24 | paid_total[3] | three u64, cumulative payouts per tank in that tank's base units |
| 202 | 32 | register_head | hash chaining every wave record |
| 234 | 2 | ring_head | index of the newest ring entry |
| 236 | 270 | tanks[3] | three tank records of 90 bytes each |
| 506 | 10240 | waves[256] | 256 wave records of 40 bytes each |
| 10746 | 1536 | breaches[32] | 32 breach records of 48 bytes each |
| offset | size | field | meaning |
|---|---|---|---|
| 0 | 32 | mint | 32 |
| 32 | 32 | vault | 32 |
| 64 | 8 | value_lamports | u64 |
| 72 | 8 | cost_basis_lamports | u64 |
| 80 | 2 | target_weight_bps | u16 |
| 82 | 4 | fills | u32 |
| 86 | 4 | spends | u32 |
| offset | size | field | meaning |
|---|---|---|---|
| 0 | 8 | slot | u64 |
| 8 | 1 | kind | u8, 0 buy, 1 sell |
| 9 | 1 | action | u8, 0 nothing, 1 raised, 2 bought, 3 paid |
| 10 | 1 | tank | u8, 0 USDC, 1 SOL, 2 ZEC, 255 none |
| 11 | 1 | pad | u8 |
| 12 | 2 | displacement_bps | u16 |
| 14 | 2 | draft_bps | i16, negative means freeboard |
| 16 | 8 | price | u64 |
| 24 | 8 | amount | u64 |
| 32 | 8 | waterline_after | u64 |
| offset | size | field | meaning |
|---|---|---|---|
| 0 | 8 | opened_slot | u64 |
| 8 | 8 | closed_slot | u64, 0 while open |
| 16 | 2 | deepest_draft_bps | u16 |
| 18 | 6 | pad | 6 |
| 24 | 4 | waves | u32 |
| 28 | 4 | pad | 4 |
| 32 | 8 | spent_lamports | u64 |
| 40 | 8 | burned | u64 |
| offset | size | field | meaning |
|---|---|---|---|
| 0 | 8 | discriminator | 8 |
| 8 | 8 | index | u64 |
| 16 | 8 | slot | u64 |
| 24 | 1 | tank | u8 |
| 25 | 7 | pad | 7 |
| 32 | 8 | total | u64 |
| 40 | 4 | recipients | u32 |
| 44 | 4 | pad | 4 |
| 48 | 32 | root | 32 |
These sentences are filled from the live values on this page. They change only when the values change.
The hull does not hold the coin. Everything it buys is burned in the same instruction, so there is no program owned supply that could be sold later. The tanks hold USDC, SOL and ZEC and nothing else.
The waterline is not a price target and not a promise. It is a record of where buy waves have carried the price, and it is the only thing the hull measures against. A coin can sit under its waterline for as long as sellers keep it there; the hull spends against each sell wave in proportion to the draft and never all at once.
The depth factor is not a lock. Nobody's tokens are held. Selling simply returns a wallet's factor to one.
Nothing here runs on a clock. If no trade clears the threshold, nothing happens, however long that takes.
The ZEC in the hull is the bridged ZEC on Solana, not native Zcash, and it trades in thinner pools than USDC or SOL, so the ZEC tank's value in SOL moves more per trade than the other two. The trim rule takes that into account only by measuring gain, not by measuring liquidity.