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):
| Key | Type | Description |
|---|---|---|
n | uint64 | Number of tokens in the pool |
sumX | uint64 | Sum of all reserves (∑xᵢ) |
sumXSq | uint64 | Sum of squared reserves (∑xᵢ²) |
rInt | uint64 | Interior radius (sum of interior tick radii) |
sBound | uint64 | Boundary effective radius |
kBound | uint64 | Boundary hyperplane offset |
totalR | uint64 | Total liquidity (sum of all tick radii) |
virtualOffset | uint64 | AMOUNT_SCALE factor (1000) |
fee_bps | uint64 | Fee in basis points (e.g., 30 = 0.3%) |
numTicks | uint64 | Number 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)
| Offset | Size | Field |
|---|---|---|
| 0 | 8 bytes | reserves[0] (microunits) |
| 8 | 8 bytes | reserves[1] |
| ... | ... | ... |
| (n-1)×8 | 8 bytes | reserves[n-1] |
Total size: n × 8 bytes
fee_growth Box
Key: "fee_growth"
| Offset | Size | Field |
|---|---|---|
| 0 | 8 bytes | fee_growth[0] (per PRECISION) |
| 8 | 8 bytes | fee_growth[1] |
| ... | ... | ... |
Total size: n × 8 bytes
Token Boxes
Key: "token:{idx}" where idx is 0 to n-1
| Offset | Size | Field |
|---|---|---|
| 0 | 8 bytes | ASA 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)
| Offset | Size | Field |
|---|---|---|
| 0 | 8 bytes | r (sphere radius) |
| 8 | 8 bytes | k (hyperplane offset) |
| 16 | 1 byte | state (0=INTERIOR, 1=BOUNDARY) |
| 17 | 8 bytes | totalShares (LP shares in this tick) |
Total size: 25 bytes per tick
Position Boxes
Key: "pos:{owner}{tickId}" where owner is 32-byte address
| Offset | Size | Field |
|---|---|---|
| 0 | 8 bytes | shares (LP shares owned) |
| 8 | 8 bytes | fee_checkpoint[0] |
| 16 | 8 bytes | fee_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⁵³.