Deposit, trade, withdraw
Step-by-step SDK examples for opening an account, depositing USDC, placing option trades, cancelling orders, refreshing margin, and withdrawing.
All write operations use Sui programmable transaction blocks (PTBs). The pattern is:
- Create a
Transactionobject. - Call the SDK builder to append move calls.
- Set the sender and gas payment on the transaction.
- Sign and execute via your Sui client.
Step 1: Open an account
Every address must open an account once before depositing or trading.
import { genUserEntry, PACKAGE_ID_TESTNET, QUOTE_COIN_TYPE_TESTNET, REGISTRY_ID_TESTNET } from '@phasis-lab/sdk';
import { Transaction } from '@mysten/sui/transactions';
const ACCOUNT_REGISTRY_ID = '0x5b6ca0125fb39530de62186f7e5973d60f6cf27c879a901ce6f415cdafe38704';
const tx = new Transaction();
genUserEntry.openAccountV2({
package: PACKAGE_ID_TESTNET,
typeArguments: [QUOTE_COIN_TYPE_TESTNET],
arguments: {
registry: REGISTRY_ID_TESTNET,
accounts: ACCOUNT_REGISTRY_ID,
},
})(tx);
tx.setSender(walletAddress);
tx.setGasPayment([gasPaymentRef]);
// sign and executeThis is a one-time call per address. It creates the account inside the shared AccountRegistry with zero balance and no positions.
Step 2: Deposit USDC
import { genUserEntry, PACKAGE_ID_TESTNET, QUOTE_COIN_TYPE_TESTNET, REGISTRY_ID_TESTNET } from '@phasis-lab/sdk';
import { Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
genUserEntry.depositUsdcV2({
package: PACKAGE_ID_TESTNET,
typeArguments: [QUOTE_COIN_TYPE_TESTNET],
arguments: {
registry: REGISTRY_ID_TESTNET,
accounts: ACCOUNT_REGISTRY_ID,
coin: usdcCoinObjectId, // object ID of a Coin<USDC> object you own
},
})(tx);
tx.setSender(walletAddress);
tx.setGasPayment([gasPaymentRef]);
// sign and executeThe entire coin is deposited. To deposit a specific amount, use tx.splitCoins() first:
const tx = new Transaction();
// Split 50 USDC from your largest USDC coin
const [depositCoin] = tx.splitCoins(tx.object(largeUsdcCoinId), [50_000_000n]);
genUserEntry.depositUsdcV2({
package: PACKAGE_ID_TESTNET,
typeArguments: [QUOTE_COIN_TYPE_TESTNET],
arguments: {
registry: REGISTRY_ID_TESTNET,
accounts: ACCOUNT_REGISTRY_ID,
coin: depositCoin, // TransactionArgument from splitCoins
},
})(tx);Step 3: Place a trade
Use placeTrade to buy or sell an option series. The series is identified by (marketId, strike, isCall).
import {
placeTrade,
intentToSide,
TradeIntent,
OrderType,
PACKAGE_ID_TESTNET,
REGISTRY_ID_TESTNET,
} from '@phasis-lab/sdk';
import { Transaction } from '@mysten/sui/transactions';
const BTC_MARKET_ID = '0xbac3cacd5169a741ab179f34e94011a3653835402732ebbfaad8bc18a5d8d264';
const BTC_SNAPSHOT_ID = '0x513b72314c521cf4d20c31ace3f2e2a13e8813aa0c5a59700c02f5a39fc1f1d4';
const tx = new Transaction();
placeTrade(tx, {
registryId: REGISTRY_ID_TESTNET,
marketId: BTC_MARKET_ID,
accountRegistryId: ACCOUNT_REGISTRY_ID,
snapshotId: BTC_SNAPSHOT_ID,
// Series identity
strike: 70_000_000_000_000n, // $70,000 in UD30x9 (70_000 × 1e9)
isCall: true, // true = call, false = put
// Order parameters
side: intentToSide(TradeIntent.OpenLong, null), // BID (0) — buy to open long
orderType: OrderType.NO_RESTRICTION, // rest if not immediately matched
qty: 2_000_000n, // 2 contracts (2 × 1e6)
limitPrice: 50_000_000n, // $0.05 limit price (0.05 × 1e9)
// Optional (defaults to testnet package & quote type)
packageId: PACKAGE_ID_TESTNET,
});
tx.setSender(walletAddress);
tx.setGasPayment([gasPaymentRef]);
// sign and executePlaceTradeArgs reference
| Field | Type | Description |
|---|---|---|
registryId | string | Shared OptionsRegistry object ID |
marketId | string | Market object ID (e.g. BTC market) |
accountRegistryId | string | Shared AccountRegistry object ID |
snapshotId | string | StressSnapshot object ID for the asset |
strike | bigint | number | Strike in UD30x9 raw (strike × 1e9) |
isCall | boolean | true for call, false for put |
side | 0 | 1 | Book side — DeepBookSide.BID (buy) or DeepBookSide.ASK (sell) |
orderType | 0–3 | Use OrderType constants |
qty | bigint | number | Quantity in raw units (contracts × 1e6) |
limitPrice | bigint | number | Limit price in UD30x9 raw (price × 1e9) |
packageId | string? | Defaults to PACKAGE_ID_TESTNET |
Encoding reminder
// Strike: multiply USD value by 1e9
const strike = BigInt(Math.round(strikeUsd * 1e9)); // e.g. 70_000 USD → 70_000_000_000_000n
// Limit price: multiply USD per contract by 1e9
const limitPrice = BigInt(Math.round(priceUsd * 1e9)); // e.g. $0.05 → 50_000_000n
// Quantity: multiply number of contracts by 1e6
const qty = BigInt(Math.round(contracts * 1e6)); // e.g. 2 contracts → 2_000_000nStep 4: Cancel an order
Cancel a resting limit order by its orderId (a u128 value returned by getAccountOrders).
import { cancelOrder, REGISTRY_ID_TESTNET } from '@phasis-lab/sdk';
import { Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
cancelOrder(tx, {
registryId: REGISTRY_ID_TESTNET,
marketId: BTC_MARKET_ID,
accountRegistryId: ACCOUNT_REGISTRY_ID,
snapshotId: BTC_SNAPSHOT_ID,
strike: 70_000_000_000_000n,
isCall: true,
orderId: pendingOrderId, // bigint — the u128 order_id from getAccountOrders
});
tx.setSender(walletAddress);
tx.setGasPayment([gasPaymentRef]);
// sign and executeCancels work during a protocol halt window — only the !paused check and snapshot-asset gate apply, so users can always recover resting orders.
Step 5: Refresh margin
Call refreshAssetMargin to recompute and apply the margin lock for all of the sender's positions under a given stress snapshot's asset. This is useful if the stress snapshot has been updated by the publisher since the account last traded.
import { refreshAssetMargin, REGISTRY_ID_TESTNET } from '@phasis-lab/sdk';
import { Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
refreshAssetMargin(tx, {
registryId: REGISTRY_ID_TESTNET,
accountRegistryId: ACCOUNT_REGISTRY_ID,
snapshotId: BTC_SNAPSHOT_ID,
});
tx.setSender(walletAddress);
tx.setGasPayment([gasPaymentRef]);
// sign and executeThe snapshot determines which asset's positions are recomputed — passing the BTC snapshot recomputes the BTC margin lock.
Step 6: Withdraw USDC
import { genUserEntry, PACKAGE_ID_TESTNET, QUOTE_COIN_TYPE_TESTNET, REGISTRY_ID_TESTNET } from '@phasis-lab/sdk';
import { Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
genUserEntry.withdrawUsdcV2({
package: PACKAGE_ID_TESTNET,
typeArguments: [QUOTE_COIN_TYPE_TESTNET],
arguments: {
registry: REGISTRY_ID_TESTNET,
accounts: ACCOUNT_REGISTRY_ID,
amount: 10_000_000n, // 10 USDC (6 decimals)
},
})(tx);
tx.setSender(walletAddress);
tx.setGasPayment([gasPaymentRef]);
// sign and execute — an owned Coin<USDC> is returned to the senderThe withdrawal gate enforces:
- Protocol is not paused (full halt includes withdraw — anti-drain protection).
amount <= free balancewhere free =balanceQuote − lockedMargin − quoteLocked.
Checking free balance before withdrawal
import { getAccount, accountFreeQuote } from '@phasis-lab/sdk';
const account = await getAccount(client, accountId);
const free = accountFreeQuote(account); // bigint, USDC 6-decimal
console.log('Withdrawable:', Number(free) / 1e6, 'USDC');