PAR docs

Architecture

PAR is one Anchor 0.30.1 program (programs/par) and one Token-2022 mint. Everything else is accounts derived from fixed seeds. There is no off-chain component the protocol depends on: oracles are read on chain, liquidations and redemptions are permissionless instructions anyone can send.

Accounts

AccountSeedsCreated byHolds
Protocol["protocol"]initializePAR mint address, launch time (start of the debt ceiling schedule). Written once.
PAR mint["par_mint"]initializeToken-2022 mint, 6 decimals. Mint authority is the Protocol PDA. Freeze authority: none.
Branch["branch", collateral_mint]create_branchOne collateral type: debt totals, interest accumulators, redistribution accumulators, bucket bitmap, redemption base rate, its stability pool.
Collateral vault["collateral_vault", branch]create_branchSPL Token account holding every trove's collateral for that branch.
Pool vault["pool_vault", branch]create_branchToken-2022 account holding the stability pool's PAR.
Bucket["bucket", branch, index_u16_le]first trove at that rateDebt and shares of every trove at one rate, active trove count, last accrual time.
Trove["trove", branch, owner]open_troveOwner, status, bucket, shares of bucket debt, collateral, redistribution stake and snapshots, last rate change.
Deposit["deposit", branch, owner]provide_to_poolCompounded PAR deposit and the snapshots needed to pay its gains.

One owner has at most one trove and one pool deposit per branch, because both PDAs are seeded by the owner's key.

Component view

Diagram
flowchart LR
  subgraph Protocol
    P[Protocol PDA] -->|mint authority| M[PAR mint, Token-2022, no freeze authority]
  end
  subgraph Branch_jitoSOL[Branch: jitoSOL]
    B1[Branch] --> CV1[Collateral vault]
    B1 --> PV1[Pool vault]
    B1 --> K1[246 buckets]
    K1 --> T1[Troves]
  end
  subgraph Branch_mSOL[Branch: mSOL]
    B2[Branch] --> CV2[Collateral vault]
    B2 --> PV2[Pool vault]
    B2 --> K2[246 buckets]
    K2 --> T2[Troves]
  end
  O1[Pyth SOL/USD] --> B1
  O2[Switchboard SOL/USD] --> B1
  O1 --> B2
  O2 --> B2

Branches

Each listed collateral gets its own branch with its own debt ceiling, stability pool and redemption fee. The listing is in code (listed_collateral in oracle.rs):

CollateralMintRate source
jitoSOLJITOSOL_MINTSPL stake pool JITO_STAKE_POOL: SOL per token = total lamports / pool token supply
mSOLMSOL_MINTMarinade state: SOL per token = msol_price / 2^32

create_branch is permissionless: the listing and every parameter are in code, so whoever pays the rent creates the same branch.

Buckets and the bitmap

The branch keeps active_buckets: [u64; 4], a 256-bit bitmap where bit i is set while bucket i has at least one active trove. Redemptions use it to prove they are always draining the lowest non-empty bucket, without a sorted list of troves.

Inside a bucket, troves own shares of the bucket's debt. Interest accrues on the bucket, so a bucket with ten thousand troves costs the same to update as a bucket with one.

Interest without touching buckets

The branch keeps weighted_debt, the sum of each bucket's debt times its rate in basis points. Interest for the whole branch over elapsed seconds is weighted_debt x elapsed / (10,000 x 31,536,000), so the branch can mint interest to the stability pool on every instruction without reading any bucket. Each bucket folds its own share in when it is next touched.

Collateral accounting

The collateral vault balance always equals:

text
active_collateral + default_collateral + pool_collateral + claimable_collateral
  • active_collateral: backing live troves.
  • default_collateral: redistributed from liquidations, not yet folded into the receiving troves.
  • pool_collateral: earned by stability pool depositors, waiting to be claimed.
  • claimable_collateral: left over for owners of liquidated or fully redeemed troves.