Skip to content

Accounts and transactions ​

Program ID: 9zA7Ctc39vg8tbUezeiVHfNotnt4SLAgY8oX9VXkciCw (current deployment; mainnet: TBD). Integer seeds are little-endian (u64 = 8 bytes, u8 = 1 byte).

Accounts ​

AccountSeedsHoldsCreated by / rent paid byClosed
Config["config"]Parameters, roles, house (LP) balances, payout-queue head and tail, staking poolinitializeNever
Vault["vault"]All USDC (an SPL token account owned by Config)initializeNever
Market["market", index: u8]Market parameters, open interest, OI limits and brake, oracle watermark, settlement pin (settle_pin_ts) and settlement pricePre-launch setup or an AddMarket proposalNever
Trader["trader", owner]Available, locked and queued USDC; $NOTE unstaked/staked; reward accounting; session key; counters (next_request, the next request id; next_position, a count of positions ever opened)create_trader (owner, ≈ 0.0022 SOL)Never
Request["request", owner, id: u64], where id is the trader's next_requestA pending open or close: pin, expiry, escrowed SOL, and the admissibility limits in force when it was made (max_conf_bps, max_ema_dev_bps), which its fill or void uses. Zero means a request made before these were recorded, which uses the live limits.Request signer (≈ 0.0019 SOL + execution fee + escrowed rent)At fill, cancel or void; rent goes to the signer
Position["position", owner, id: u64], where id is the id of the open request that created itSide, entry, margin, leverage, notional, entry publish time, pending-close markerFiller at execute_open, reimbursed from the request's escrowAt close, liquidation or settlement; rent goes to the open request's signer
QueueClaim["claim", owner, position_id: u64], where position_id is the id of the position whose win queuedAn unpaid win: queue index (its FIFO position), owner, position id, amountWhen a win queues: a close filler, reimbursed from the close request's escrow; or a retired-market settler, who is not reimbursed until the claim is fully paidWhen fully paid; rent goes to the close request's signer (or the settler)
Proposal["proposal", id: u64]A timelocked governance actionAdminNever (status changes)

Trader instructions ​

InstructionSignerWhat it doesTypical failures
create_traderOwnerCreates your trading accountAlready exists
deposit(amount)OwnerUSDC from your token account to the vault; credits availableNotLaunched, InvalidParam
withdraw(amount)OwnerUSDC from the vault to any USDC token account (except the vault); debits availableInsufficientBalance, BadDestination
set_session(key, expires_ts)OwnerDelegates trading to key until expires_ts (≤ 30 days ahead); replaces any previous keyInvalidParam
revoke_sessionOwnerRemoves the session key—
request_open(is_long, margin, leverage, max_samples)Owner or session keyLocks margin, reserves OI and notional, creates a request with its pin, escrows execution fee and position rentSee How trading works
request_close(max_samples)Owner or session keyCreates a close request for one position, marks the position as closing, escrows execution fee and claim rentClosePending, MarketSettled, SettlementPending, StaleHeartbeat, ClockLag
cancel_requestAnyone, only after expiryOpen: refunds locked margin and releases reservations. Close: clears the closing marker, and the position stays open. ¼ of the execution fee goes to the caller; the rest of the escrow goes back to the request's signer.RequestNotExpired (at or before expiry)
stake(amount), unstake(amount)Owner or session keyMoves $NOTE between unstaked and staked (instant)InvalidParam
claim_rewardsOwner or session keyCredits accrued USDC rewards to available—

max_samples is the most price samples you accept. Production always uses 1, so any value works.

Permissionless instructions ​

InstructionSignerWhat it does
execute_openAnyoneFills an open at its pinned update and creates the position. The filler pays the position rent and is reimbursed. The execution fee goes to the update's poster.
execute_closeAnyoneFills a close at its pinned update and settles PnL; creates a queue claim if the win queues. A close pinned before its market's settlement pin still fills, even after settlement.
liquidateAnyoneLiquidates a bust position at a fresh verified price (Liquidation). Takes no queue-claim account: liquidations never queue.
void_close(sample)AnyoneCancels a close whose pinned update is authentic but inadmissible
settle_retiredAnyoneCloses a position in a settled market at its final price; refuses a position with a pending close (ClosePending) until that close is filled, voided, or expired and cancelled. The Papersol keeper does this automatically, and the app has a Settle button.
observe_priceAnyone (fee payer only)Records a fresh verified price as the market's oracle watermark (heartbeat)
harvest, harvest_oneAnyonePays the payout queue from the front, fully or in part
sweepAnyone (fee payer only)Moves treasury above staker_cap to stakers and pushes pending staker revenue into streaming
execute_proposal, execute_add_marketAnyone, after eta and within 7 daysApplies a timelocked proposal. SettleMarket: only after the settlement pin + 120 s, with the Pyth update pinned at it; without one (dead feed), only the admin, 24 h after the pin. A SetMarket that re-opens a market with a settlement pin succeeds only while both the Solana clock and the market's last_oracle_ts are more than 1 h before its pin, or after the settlement lapses (more than 7 days after the pin). The Papersol keeper executes due settlements.
migrate_market, migrate_requestAnyone (pays the extra rent)Zero-extends a Market created before settle_pin_ts was added, or a Request (open or close) created before its admissibility limits were added. The new bytes are zero (no settlement pin; live admissibility limits), so behaviour doesn't change.

Role-gated instructions ​

InstructionSignerWhen
initializeProgram upgrade authorityOnce
setup_params, setup_marketAdminBefore launch only
launchAdminOnce; irreversible
proposeAdminAfter launch. SettleMarket records the proposal's eta as the market's settlement pin (one pending settlement per market) and snapshots the admissibility limits.
cancel_proposalAdmin or guardianWhile pending. A settlement proposal can be cancelled (or its market re-opened) only while both the Solana clock and the market's last_oracle_ts are more than 1 h before its pin, or after the settlement lapses (more than 7 days after the pin); cancelling it clears the settlement pin.
set_pausedGuardian or admin to pause; admin only to unpauseAny time
set_oi_brakeAdmin; guardian may only lowerAny time
set_oi_limitsRisk keeperAny time, within [usage, ceiling]
accept_adminPending adminAfter a SetAdmin proposal
claim_dev_fees(amount)AdminUp to accrued dev fees
set_mock_priceAdminTest builds only; production builds reject it

Events ​

EventEmitted by
CashFlow { kind }Deposit (0), withdrawal (1), reward claim (2)
RequestCreatedOpen and close requests (pin, expiry, samples)
RequestCancelledCancel and void
PositionOpenedOpen fills (entry, entry publish time, margin, leverage, notional)
PositionClosed { kind }Loss (0), win (1), liquidation (2), retired settlement (3), with raw PnL, haircut win, returned, queued, fee, $NOTE minted
ClaimHarvestedEach queue payment (paid, payout, remaining)
StakeChangedStake and unstake
LpSettledSweeps and staker revenue distribution
OiLimitsChanged, OiBrakeChanged, ControlsChanged, ProposalEventRisk and governance actions

Papersol is experimental software. Trading with leverage can lose your whole margin.