Docs

Developers

Accounts and math

The accounts Socket owns, how their addresses are derived, and the fixed-point math every amount goes through.

Socket is one native Rust program. It owns every pool's state and vaults. The built-in hooks are a second program that owns only hook state. Neither has an admin key, a withdrawal authority or a way to change a pool after creation.

Accounts#

AccountSeedsProgramSizeTag
Pool["pool", creator, nonce (u64 LE)]Socket8,192SOCKPOOL
Vault A, vault B["vault-a", pool], ["vault-b", pool]Socket (seeds); owned by SPL Token, authority: the pool165—
Position["position", pool, owner, nonce (u64 LE)]Socket256SOCKPOSN
Hook account list["extra-account-metas", pool]Socket1,024SOCKMETA
Hook authority["hook-authority", pool]Socket (signer PDA, no data)0—
Hook state (built-in)["hook-state", pool]Built-in hooks2,048SOCKHOOK

Every account starts with its eight-byte tag, then a Borsh body, then zero padding. The SDK checks the owner, size, tag, version, canonical address and padding before it trusts any of them.

Pool#

The pool holds the mints, vaults, hook program, permissions, base and max fee, surcharge cap, hook budget, tick spacing, the surcharges owed to the hook in each token, the CLMM state (square-root price, tick, active liquidity, fee growth in each token) and the sorted list of initialized ticks.

The tick list is part of the pool account. That is why a pool holds at most 64 initialized tick boundaries: every boundary a position starts or ends on counts once, however many positions share it. A swap never needs extra tick accounts, and a route through Socket always has the same fixed accounts.

Position#

A position records its pool, owner, nonce, range, liquidity, fee checkpoints in each token, the fractional fee carry, and fees owed. Ranges are half-open: a position at [lower, upper) is in range while lower ≤ tick < upper. Positions are bound to their owner's key; there is no transfer.

Hook account list#

Up to eight literal entries, each an address, the program that must own it, and whether it is writable. The layout is Socket's own versioned format. It follows the idea of SPL's transfer-hook account discovery, but it is not the SPL TLV encoding and has no seed resolution.

Tokens#

Classic SPL Token mints only, including wrapped SOL. Socket rejects Token-2022 mints and extensions, mints with a freeze authority, frozen token accounts, and token accounts with a delegate or a close authority. A mint whose issuer keeps a freeze authority, as several major stablecoins do, can't be pooled. Mints are sorted by public-key bytes: mint_a < mint_b.

Prices and ticks#

The price is the amount of token B one token A buys, in atomic units. Socket stores its square root as an unsigned Q64.64 number:

sqrt_price = sqrt(price) × 2^64
price      = 1.0001^tick
BoundValue
Lowest tick−443,636
Highest tick443,636
Lowest square-root price4,295,048,016
Highest square-root price79,226,673,515,401,279,992,447,579,055
Tick spacing1 to 32,768
Liquidity per active rangeup to 2^64 − 1

To show a human price, adjust for decimals: price × 10^(decimals_A − decimals_B).

Rounding#

Rounding always favors the pool:

  • Deposits and swap input round up.
  • Withdrawals and swap output round down.
  • Fee growth is a Q64 accumulator per token; each position carries its fractional remainder forward, so no fee is lost to repeated collection.

Dust from rounding stays in the vaults. A swap whose output rounds to zero fails.

Swaps#

An exact-output swap must deliver the whole amount or fail. An exact-input swap stops at its price limit, or at the end of the pool's liquidity, and leaves the rest of the input unspent. The swap instruction returns 24 bytes of return data: the total input including any surcharge, the output, and the fee, each a little-endian u64.

The program also logs, for indexers:

socket:swap:<pool>:<a_to_b>:<total_in>:<out>:<fee>
socket:price:<pool>:<tick>:<sqrt_price>
socket:hook:<phase>:<compute_units>
socket:liquidity:<position>:<amount_a>:<amount_b>
socket:initialize:<pool>

Only trust these lines when Socket itself is at the top of the invoke stack: a hook can write log lines too. The Socket API's activity route applies that check.

Math provenance#

Tick-to-price conversion is adapted from an Apache-2.0 revision of Orca's Whirlpools math, with its notices kept. The swap and accounting loop is Socket's own. The TypeScript SDK carries an exact bigint port of the Rust math crate, compared against it on about 59,000 generated cases with no differences.