Core concepts
Account model, value encoding, trade intents, and book sides — the fundamentals every Phasis SDK integration must understand.
Account model
Phasis uses a registry-resident account model. There is one account per wallet address, stored as a child object inside the shared AccountRegistry.
Key facts
- No per-account shared object. The account is embedded in the registry; there is no separate object address to track per user.
- Lookup by owner. Use
findAccountId(client, registryId, ownerAddress)to retrieve the account's object ID fromAccountRegistry.account_index. - Auth via sender. Move entries resolve the caller's account from
ctx.sender()— no capability object to manage.
Account fields
| Field | Type | Description |
|---|---|---|
owner | address | The controlling wallet address |
balance_quote | Coin<Quote> | Free USDC (not escrowed by resting bids) |
locked_margin | u64 | Total margin lock across all assets (USDC raw, 6 decimals) |
lock_by_asset | VecMap<u8, u64> | Per-asset margin lock breakdown (asset code → USDC raw) |
positions | VecMap<ID, Position> | Active positions keyed by series ID |
pending_by_order | VecMap<PendingKey, PendingOrder> | Resting limit orders |
quote_locked | u64 | USDC reserved by resting bids (counted against free balance, not escrowed) |
fee_paid | u64 | Cumulative fees paid (analytics) |
Free balance (withdrawable)
// All values are USDC raw (6 decimals)
const free = account.balanceQuote - account.lockedMargin - account.quoteLocked;Use accountFreeQuote(account) from the SDK for this calculation.
Account NAV
- Cash NAV:
account.balanceQuote— resting bids do not escrow principal, so the cash component equals the balance alone. - Mark-to-market NAV: requires a fresh
StressSnapshotand is computed on-chain viavault_api::account_nav_quote_v2(devInspect). - Total NAV: cash + mark-to-market = value if all positions closed at fair prices.
Value encoding
The protocol uses fixed-point encoding throughout. Getting these scales right is critical — a factor-of-1e9 error is the most common integration bug.
Price — UD30x9
Prices are unsigned 30-integer 9-fractional fixed-point values.
- Raw type in Move:
u128(newtypeUD30x9) - Human unit: USDC per contract
- Raw = human × 1,000,000,000
// Examples
const price_0_001 = 1_000_000n; // $0.001
const price_0_05 = 50_000_000n; // $0.05
const price_1_00 = 1_000_000_000n; // $1.00
const price_100 = 100_000_000_000n; // $100.00
// Encode for SDK calls
const limitPrice = BigInt(Math.round(humanPrice * 1e9));
// Decode from SDK reads
const humanPrice = Number(rawPrice) / 1e9;Strike — also UD30x9
Strikes use the same UD30x9 encoding as prices.
// BTC $70,000 strike
const strike = 70_000n * 1_000_000_000n; // 70_000_000_000_000nQuantity — 1e6 (contract decimals)
Quantities represent numbers of option contracts. Each contract covers one unit of underlying.
- Raw type in Move:
u64 - Human unit: number of contracts
- Raw = human × 1,000,000
// Examples
const qty_1 = 1_000_000n; // 1 contract
const qty_2_5 = 2_500_000n; // 2.5 contracts
const qty_100 = 100_000_000n; // 100 contracts
// Encode
const qty = BigInt(Math.round(humanQty * 1e6));
// Decode
const humanQty = Number(rawQty) / 1e6;USDC amounts — 6 decimals
USDC deposits and withdrawals use standard 6-decimal encoding (consistent with the USDC coin type).
const deposit_10_usdc = 10_000_000n; // 10 USDC
const deposit_100_usdc = 100_000_000n; // 100 USDCMargin — USDC 6-decimal (fp9 internal)
Margin locks are stored internally in fp9 (1e9 scale) but exposed at the account level in USDC 6-decimal units. The SDK reader functions return already-converted values at 6-decimal scale.
Trade intents and book sides
The Phasis book is a standard price-time-priority limit order book where BID = buy side and ASK = sell side. Option semantics map onto book sides as follows:
| Intent | Book side | Meaning |
|---|---|---|
OpenLong | BID | Buy to open a long position |
CloseShort | BID | Buy to close an existing short |
OpenShort | ASK | Sell to open a short position |
CloseLong | ASK | Sell to close an existing long |
TradeIntent enum
import { TradeIntent } from '@phasis-lab/sdk';
TradeIntent.OpenLong // 'open_long'
TradeIntent.CloseLong // 'close_long'
TradeIntent.OpenShort // 'open_short'
TradeIntent.CloseShort // 'close_short'intentToSide helper
import { intentToSide, TradeIntent, DeepBookSide } from '@phasis-lab/sdk';
// Convert intent to the u8 book side expected by the Move entry
const side = intentToSide(TradeIntent.OpenLong, null);
// → DeepBookSide.BID (0)
// If the account already has a position, pass its netIsShort field
const side = intentToSide(TradeIntent.CloseLong, position.netIsShort);
// → DeepBookSide.ASK (1)DeepBookSide constants
import { DeepBookSide } from '@phasis-lab/sdk';
DeepBookSide.BID // 0 — buy side
DeepBookSide.ASK // 1 — sell sideOrderType constants
import { OrderType } from '@phasis-lab/sdk';
OrderType.NO_RESTRICTION // 0 — rest if not immediately matched (default)
OrderType.IMMEDIATE_OR_CANCEL // 1 — fill what's available, cancel remainder
OrderType.FILL_OR_KILL // 2 — fill entirely or abort
OrderType.POST_ONLY // 3 — rest only; abort if it would match immediatelyAsset codes
Assets are identified by a u8 code in Move and in the SDK:
| Code | Asset |
|---|---|
0 | SUI |
1 | BTC |
2 | ETH |