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
| Account | Seeds | Created by | Holds |
|---|---|---|---|
Protocol | ["protocol"] | initialize | PAR mint address, launch time (start of the debt ceiling schedule). Written once. |
| PAR mint | ["par_mint"] | initialize | Token-2022 mint, 6 decimals. Mint authority is the Protocol PDA. Freeze authority: none. |
Branch | ["branch", collateral_mint] | create_branch | One collateral type: debt totals, interest accumulators, redistribution accumulators, bucket bitmap, redemption base rate, its stability pool. |
| Collateral vault | ["collateral_vault", branch] | create_branch | SPL Token account holding every trove's collateral for that branch. |
| Pool vault | ["pool_vault", branch] | create_branch | Token-2022 account holding the stability pool's PAR. |
Bucket | ["bucket", branch, index_u16_le] | first trove at that rate | Debt and shares of every trove at one rate, active trove count, last accrual time. |
Trove | ["trove", branch, owner] | open_trove | Owner, status, bucket, shares of bucket debt, collateral, redistribution stake and snapshots, last rate change. |
Deposit | ["deposit", branch, owner] | provide_to_pool | Compounded 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
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 --> B2Branches
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):
| Collateral | Mint | Rate source |
|---|---|---|
| jitoSOL | JITOSOL_MINT | SPL stake pool JITO_STAKE_POOL: SOL per token = total lamports / pool token supply |
| mSOL | MSOL_MINT | Marinade 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:
active_collateral + default_collateral + pool_collateral + claimable_collateralactive_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.