State Layout

This page documents the exact byte layout of all contract storage. Use this reference when reading state directly (without the SDK) or when debugging.

Global State

These keys are in the app's global state (accessible via appl_account):

KeyTypeDescription
nuint64Number of tokens in the pool
sumXuint64Sum of all reserves (∑xᵢ)
sumXSquint64Sum of squared reserves (∑xᵢ²)
rIntuint64Interior radius (sum of interior tick radii)
sBounduint64Boundary effective radius
kBounduint64Boundary hyperplane offset
totalRuint64Total liquidity (sum of all tick radii)
virtualOffsetuint64AMOUNT_SCALE factor (1000)
fee_bpsuint64Fee in basis points (e.g., 30 = 0.3%)
numTicksuint64Number of active ticks

Box Storage

Box storage is used for per-token, per-tick, and per-position data. Each box is accessed via box_get(key).

reserves Box

Key: "reserves" (literal string)

OffsetSizeField
08 bytesreserves[0] (microunits)
88 bytesreserves[1]
.........
(n-1)×88 bytesreserves[n-1]

Total size: n × 8 bytes

fee_growth Box

Key: "fee_growth"

OffsetSizeField
08 bytesfee_growth[0] (per PRECISION)
88 bytesfee_growth[1]
.........

Total size: n × 8 bytes

Token Boxes

Key: "token:{idx}" where idx is 0 to n-1

OffsetSizeField
08 bytesASA ID of token at index idx

Total size: 8 bytes per box, n boxes total

Tick Boxes

Key: "tick:{id}" where id is the tick ID (uint64)

OffsetSizeField
08 bytesr (sphere radius)
88 bytesk (hyperplane offset)
161 bytestate (0=INTERIOR, 1=BOUNDARY)
178 bytestotalShares (LP shares in this tick)

Total size: 25 bytes per tick

Position Boxes

Key: "pos:{owner}{tickId}" where owner is 32-byte address

OffsetSizeField
08 bytesshares (LP shares owned)
88 bytesfee_checkpoint[0]
168 bytesfee_checkpoint[1]
.........
8 + n×8-(end)

Total size: 8 + n × 8 bytes per position

Reading State Directly

To read state without the SDK:

// Get global state
const appInfo = await algodClient.getApplicationByID(POOL_APP_ID).do();
const globalState = appInfo.params['global-state'];

// Decode reserves box
const reservesBox = await algodClient
  .getApplicationBoxByName(POOL_APP_ID, new Uint8Array(Buffer.from('reserves')))
  .do();
const reserves = [];
for (let i = 0; i < n; i++) {
  const offset = i * 8;
  const value = BigInt(
    '0x' + reservesBox.value.slice(offset, offset + 8).toString('hex')
  );
  reserves.push(value);
}
Note: All multi-byte values are big-endian. Use BigIntfor values that may exceed 2⁵³.