Protocol
The floating leg
How Tack reads Phoenix's SOL perp funding and turns it into a payment.
The source
The floating leg is SOL perp funding on Phoenix, the on-chain perpetuals exchange on Solana, read by the program itself from one account:
| Address | |
|---|---|
| Phoenix program | EtrnLzgbS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih |
| PerpAssetMap | 2nHGAaEw3D5dd4hVueaUNoygkQFmoeKqRQWnSPqSMFUC |
| SOL market (order book) | 71Si24E4uc3oCaPbPZTozC1ptSNNqygjjebxSmErSsC2 |
The PerpAssetMap is where Phoenix keeps every market's mark price, open interest and funding accumulator. SOL is its first slot. The field that matters is the cumulative funding rate: Phoenix's running total of funding per unit of SOL, the same total every Phoenix position settles against. A rise in it is funding Phoenix shorts received from longs; a fall is funding they paid.
Phoenix measures funding over hourly intervals. Through each hour it accumulates the gap between its mark price and the index price; when the hour ends, it adds that hour's funding, clamped to the market's limit, to the cumulative total and starts the next interval. Each interval is one funding record, identified by the time it starts, always on the hour.
From funding to USDC
Phoenix counts the total in quote lots per base lot: millionths of a dollar per 0.01 SOL. Tack converts it once, exactly, into USD per SOL at 1e9 precision (one quote lot per base lot is $0.0001 per SOL). For a swap with notional N that starts at a record whose total is C0 (the first record at or after its fill; see When a swap starts), in a market whose reference price is P0:
With N in micro-USDC, C in 1e-9 USD per SOL and P0 in micro-USD per SOL, the units come out in micro-USDC. Read it as: the funding on N ÷ P0 SOL between the two readings.
When funding is positive, longs on Phoenix pay shorts, and the floating leg flows from Tack's receiver of fixed to its payer of fixed. That is what lets a Phoenix short pass its funding through: it receives the funding on Phoenix and pays the same funding on Tack.
Why the SOL amount is fixed
P0 is Phoenix's SOL mark price when the market opened, and it never changes. Funding accrues per SOL, so the floating leg needs some amount of SOL to be paid on. Tack fixes that amount once, at N ÷ P0.
The alternative, dividing the accumulator by today's price, would create gains and losses whenever SOL's price moved, with no funding paid at all. Holding the SOL amount fixed keeps the floating leg equal to what a Phoenix position of that size receives, which is what a hedge needs.
The consequence: in dollar terms, a swap's floating leg grows and shrinks with SOL's price. If SOL rises 20% after a market opens, the same funding rate pays 20% more dollars on the floating leg, while the fixed leg stays on N. The rates in the app use P0, so the floating rate shown is the rate on the swap, not on today's price.
What the program checks
Phoenix upgrades its program often, so Tack does not pin a deployment. It checks the account itself, every time it reads it, and refuses on any mismatch:
- the account is the PerpAssetMap above, owned by the Phoenix program, exactly 1,622,064 bytes, with its account discriminator;
- slot 0 holds SOL: its symbol, asset id 0, the SOL market above, a live (not removed) perp entry;
- the units have not changed: base lots of 0.01 SOL and a tick of 100 quote lots per base lot;
- the funding interval is the one the market opened with, interval starts sit on that grid, and the accumulator was updated within its interval;
- Phoenix's funding period and its per-interval clamp, which bound how far one record can move, are the ones the market opened with;
- the accumulator update and the newest index-oracle update are not in the future and are no older than the market's
maxAge.
The layout is read field by field from fixed offsets reviewed against Phoenix's published account definitions and checked against finalized mainnet reads; the program never casts the account onto a struct. An upgrade that keeps the layout changes nothing for Tack.