Skip to main content

Smart Contracts

Key Concepts

TermDefinition
ProgramA Solana smart contract. Stateless - all state lives in separate accounts passed as inputs to each instruction
InstructionThe equivalent of a function call on a Solana program. Each transaction contains one or more instructions
AccountAn on-chain storage unit. Holds state (data) and is owned by a program
PDA (Program Derived Address)A deterministic account address derived from a program ID and a set of seeds. No private key, only the program can sign for it
SlabA fixed-size pre-allocated memory region used as the backing store for the order book. Enables O(log n) insert, delete, and lookup without dynamic allocation
Critbit treeA binary trie used to implement the order book. Keys are order prices, supports efficient traversal to find best bid/ask
CU (Compute Unit)Solana's unit of computation. Each transaction has a budget. Complex operations like order matching consume many CUs
Rent exemptionAccounts must hold enough SOL to be exempt from rent (storage fees). Accounts below the threshold are garbage collected
Zero-copyAn Anchor pattern that avoids deserialising large accounts into memory. Required for the order book to avoid stack overflow
KeeperA bot that calls permissionless protocol instructions (settle_funding, liquidate_position). Does not participate in order matching - matching runs inline on-chain within place_order
AUM (Assets Under Management)Total value of USDC currently held in a vault. Used to compute NAV per share: AUM / total_shares. Increases when depositors deposit or positions generate profit, decreases on withdrawals or losses
VAA (Verified Action Approval)A signed price attestation produced by the Pyth oracle network and served by Hermes.

1. Summary

Perps requires at least two Anchor programs deployed on Solana:

  • Perps Program - core protocol logic: order book, position management, margin, funding rate, and liquidations.
  • Vault Program - capital management: vault creation, deposits, withdrawals, NAV calculation, and LP share minting.

The Vault Program interacts with the Perps Program to place orders on behalf of vault strategies. Both programs use the Pyth oracle for price validation on every instruction that affects margin or positions.

Neither program implements any oracle logic of its own. Both integrate, via CPI, with the Pyth Receiver Program, a third program already deployed on-chain by Pyth Network.


2. Program Architecture

ProgramResponsibilityWhy separate
Perps ProgramOrder book, matching, positions, funding, liquidationsCore protocol, independent of capital management
Vault ProgramDeposits, withdrawals, LP shares, strategy managementProtocol launches with a liquidity vault and a liquidation vault. External managers can also create their own vaults
Pyth Receiver ProgramVerifies the signed price attestation (VAA) and serves it to callers via CPIOracle logic is a solved problem

3. Perps Program

3.1 Accounts

AccountDescriptionNotes
GlobalConfigProtocol-wide parameters: admin, pending_admin, treasury, fee rates, max leverage, oracle max_age, max_conf_ratio, min_vault_lockup_period, vault_creation_fee, protocol_paused flag, protocol_hard_frozen flagOne per deployment
MarketPer-market config: symbol, tick size, lot size, funding interval, Pyth price feed ID, max position size, initial margin %, maintenance margin %, status (active / paused / deprecated)One per tradeable market (SOL-PERP, BTC-PERP, ETH-PERP)
OrderBookSlab-based critbit tree containing all live orders for a marketZero-copy account, pre-allocated fixed buffer
FundingStatePer market: cumulative funding index, last funding timestamp, last mark/oracle priceUsed to compute funding payments
UserAccountPer user (or vault): owner: Pubkey, index: u8, account_type: AccountType, deposited margin, available margin, open order countPDA derived from [owner, "user_account", index]. Stores account_type: enum { Primary, Secondary, Vault } as the canonical type indicator — any reader (indexer, other programs, RPC clients) determines the type from this field alone, without inspecting index. index is stored solely for PDA re-derivation and verification; it carries no business logic meaning. The program enforces that a Secondary account can only be created if a Primary already exists for the same owner — initialize_user_account receives the primary PDA as an optional account and checks !data_is_empty(). Vault trading accounts (account_type = Vault, owner = Vault PDA) are created by the Vault Program via CPI inside create_vault, never through initialize_user_account
PositionPer user per market: side (long/short), size, entry price, liquidation price, funding index at openPDA derived from [user_pubkey, market_pubkey, "position"]

3.2 Common Validations

Every state-changing instruction checks these conditions before executing any logic. If any check fails, the instruction is rejected.

The protocol has two freeze levels stored in GlobalConfig:

FlagSet byMeaning
protocol_pausedpause_protocol or hard_freeze_protocolBlocks all activity except withdraw_margin. Hard freeze always sets this flag too
protocol_hard_frozenhard_freeze_protocol onlyAdditionally blocks withdraw_margin. Last resort only

Most instructions only need to check protocol_paused. Withdrawals need the additional protocol_hard_frozen check.

CheckConditionApplies to
Freeze checkGlobalConfig.protocol_paused == falseAll instructions
Hard freeze checkGlobalConfig.protocol_hard_frozen == falsewithdraw_margin and vault_withdraw only
Market is activeMarket.status == ActiveAll market instructions (place_order, cancel_order, close_position, settle_funding, liquidate_position)
Oracle price is freshpublish_time + max_age > clock::get()All instructions that require a VAA
Oracle confidence within rangeconf / price < max_conf_ratioAll instructions that require a VAA

hard_freeze_protocol sets both flags. pause_protocol sets only protocol_paused. unfreeze_protocol clears both. unpause_protocol clears only protocol_paused.

3.3 User Instructions

InstructionCallerVAA requiredDescription
initialize_user_accountUserNoCreate a UserAccount PDA for a given index, sets account_type = Primary if index == 0, else Secondary. If index > 0, the primary account (index == 0) must already be initialized — the instruction receives it as an optional account and rejects with PrimaryAccountRequired if absent or uninitialised. Vault trading accounts are never created through this instruction
deposit_marginUserNoTransfer USDC from user wallet to UserAccount
withdraw_marginUserYesWithdraw available margin, VAA validates that remaining margin covers open positions
adjust_position_marginUserYesAdd or remove margin from a specific open position. Add: moves USDC from available margin in UserAccount to the position, raising the liquidation price. Remove: moves USDC from the position back to available margin, lowering the liquidation price. VAA validates that the result stays within margin requirements
place_orderUserYesPlace an order and attempt inline matching against the opposite side of the slab (price-time priority). Any unfilled remainder enters the slab as a resting limit order. Validates margin at mark price. Order type is passed as a parameter (see below)
cancel_orderUserNoRemove an order from the slab, release reserved margin
close_positionUserYesClose an open position by placing an opposing market order. Fills at the best available book price, settles realized PnL to UserAccount. VAA is required to validate remaining margin on any other open positions after settlement

Order types supported by place_order:

TypeBehaviourPrototype
LimitEnters the slab at the specified price, waits for a matchYes
MarketExecutes immediately at the best available price, any unfilled remainder is canceledYes
IOC (Immediate-or-Cancel)Like market but with a price cap/floor, canceled if the full size cannot be filled within the price limitPost-prototype

3.4 Permissionless Instructions

InstructionVAA requiredDescription
settle_fundingYesApply cumulative funding payments to all open positions for a market
liquidate_positionYesLiquidate a position whose margin ratio is below maintenance threshold, pay liquidation fee to caller

3.5 Admin Instructions

Note: For the prototype, all admin instructions are immediate. A timelock/governance mechanism (propose + delay + execute) will be added in a future version before mainnet.

Admin address: The admin field in GlobalConfig is a plain Pubkey. The program only checks that the signer of an admin instruction matches that address - it has no knowledge of what controls it. The address can be a regular wallet (useful for development), a multi-sig program (e.g. Squads), or any future governance system. Membership management, approval thresholds, and proposal flow are all external to the protocol. To change the admin address, use transfer_admin.

InstructionDescription
initialize_marketCreate a new market with full config and an empty pre-allocated order book
update_marketUpdate any market parameter: tick size, lot size, funding interval, max position size, margin requirements, price feed ID
pause_marketHalt new orders and position opens for a market, existing positions and cancels still allowed
unpause_marketResume normal operation for a paused market
deprecate_marketMark a market as deprecated, only close/cancel allowed, no new orders or deposits
update_global_configUpdate protocol-wide parameters: fee rates, max leverage, oracle staleness limit, confidence ratio limit
pause_protocolSoft freeze, suspend all new activity across all markets and vaults, withdrawals still allowed
unpause_protocolLift soft freeze, resume normal operation
hard_freeze_protocolHard freeze, suspend all instructions including withdrawals. Last resort for active exploits
unfreeze_protocolLift hard freeze, resume normal operation
transfer_adminInitiate a two-step transfer of the admin address by setting pending_admin, used to migrate to a new wallet, multi-sig, or governance system
accept_adminpending_admin signs and accepts authority, completing the transfer

4. Vault Program

4.1 Accounts

AccountDescription
VaultConfig: manager address, strategy type, performance_fee % (set at creation, immutable), lockup_period (must be >= min_vault_lockup_period), min_deposit, min_owner_share (minimum % of total shares the manager must hold), total AUM, total shares outstanding, status (active / frozen / deprecated)
VaultShareSPL token mint - LP token representing a proportional claim on vault assets. NAV per share = AUM / total shares
VaultUserDepositPer depositor: shares held, last_deposit_time. Lock is checked at withdraw: now >= last_deposit_time + vault.lockup_period. PDA derived from [vault_pubkey, user_pubkey]

4.2 Common Validations

CheckConditionApplies to
Protocol not pausedGlobalConfig.protocol_paused == falseAll vault instructions except vault_withdraw
Vault is activeVault.status == Activevault_deposit, place_order_as_vault
Vault not deprecatedVault.status != DeprecatedAll except vault_withdraw

vault_withdraw is always allowed regardless of protocol pause or vault status. Depositors must always be able to exit.

4.3 Public Instructions

InstructionCallerDescription
create_vaultAnyone (pays vault_creation_fee)Create a new vault - manager sets performance fee, lockup period (>= min_vault_lockup_period), and min deposit. All fixed at creation

4.4 User Instructions

InstructionCallerDescription
vault_depositUserPull USDC from the user's UserAccount (available margin) into the vault. Mints LP shares proportional to current NAV per share, updates last_deposit_time. Rejected if vault is frozen, amount < min_deposit, or if the deposit would dilute the manager's share below min_owner_share
vault_withdrawUserBurn LP shares and return USDC proportional to current NAV per share back to the user's UserAccount (available margin). Performance fee deducted from profit. Rejected if now < last_deposit_time + vault.lockup_period. If the caller is the manager, also rejected if the resulting share would fall below min_owner_share

4.5 Manager Instructions

InstructionCallerDescription
place_order_as_vaultVault managerCPI into Perps Program to place an order on behalf of the vault
freeze_vaultVault managerHalt new deposits, existing depositors can still withdraw
unfreeze_vaultVault managerResume normal vault operation

4.6 Admin Instructions (Vault Program)

Note: For the prototype, all admin instructions are immediate. A timelock/governance mechanism will be added in a future version before mainnet.

InstructionDescription
force_freeze_vaultImmediately freeze any vault regardless of manager (emergency only)
force_unfreeze_vaultLift an admin-imposed freeze
deprecate_vaultMark vault as deprecated, only withdrawals allowed, no new deposits
update_creation_feeUpdate the protocol fee charged on vault creation
collect_feesCollect accrued protocol fees to the treasury account defined in GlobalConfig

5. Order Book Design

The order book is implemented as a critbit tree backed by a slab allocator. All order data lives in a single fixed-size account allocated at market creation - no dynamic resizing, no heap allocation, minimal compute unit cost.

PropertyValueReason
Data structureCritbit tree (binary trie on price)O(log n) insert, delete, best-bid/ask
StorageZero-copy pre-allocated slabAvoids stack overflow on large accounts
Order slots~10,000 per market (configurable at init)Fixed cost, slab never grows after creation
Order fieldsorder_id, owner, side, price, size, timestampMinimum needed for price-time priority
Matching priorityBest price first, FIFO within same price levelStandard CLOB behaviour
Matching modelInline within place_order, matching runs on-chain in the same transaction, no off-chain keeper neededNo centralised matcher, fully on-chain execution

6. Oracle Integration

Artifi does not implement any oracle logic itself. Both the Perps Program and the Vault Program integrate, via CPI, with the Pyth Receiver Program, a program already deployed on-chain by Pyth Network.

Every instruction that affects margin or positions requires a valid Pyth price account as input. The program validates the price before using it.

ValidationRuleIf violated
Stalenesspublish_time + max_age > clock::get()Instruction rejected
Confidenceconf / price < max_conf_ratio (e.g. 5%)Instruction rejected
InstructionOracle use
place_orderValidate margin at mark price + compute PnL and margin impact for each inline fill
adjust_position_marginValidate that margin ratio stays within bounds after the adjustment
withdraw_marginEnsure margin ratio remains above minimum after withdrawal
close_positionValidate remaining margin on other open positions after settlement
settle_fundingCompute funding payment from (mark - index) / index
liquidate_positionVerify position is actually below maintenance margin at current mark price

For the full transaction flow, see technical-documents.md.