Security & trust
This page lists what nobody can do to PAR or to your trove, and the line of code that makes it so. It also lists what the design does not protect against.
What nobody can do
| Action | Who could do it in a typical stablecoin | Why nobody can do it here |
|---|---|---|
| Freeze a wallet holding PAR | The issuer, through the mint's freeze authority | par_token::initialize_mint encodes freeze_authority as COption::None. The mint never had a freeze key. |
| Mint PAR out of thin air | The issuer, through the mint authority | The mint authority is the Protocol PDA. Only program logic signs for it: borrowing against collateral and minting interest to the pool. |
| Change the program | Whoever holds the upgrade authority | initialize has the constraint program_data.upgrade_authority_address.is_none(). PAR cannot start while an upgrade key exists. |
| Change a parameter | An admin or a governance vote | Every parameter is a const in constants.rs. Protocol is written once by initialize; no instruction writes it again. |
| Raise your rate | A lending pool's utilisation curve | The rate is the bucket your trove sits in. set_rate requires the trove owner's signature, and the trove PDA is seeded by the owner's key. |
| Change what an oracle reports | Whoever holds the feed's authority | PAR reads its own Switchboard feed, created with the authority handed to the incinerator (1nc1nerator11111111111111111111111111111111), so its recipe can never be changed or the feed closed. Pyth's feed is Pyth's own sponsored account. |
| Stop redemptions with a bad oracle | Pausing logic | Redemptions and liquidations use whichever fresh oracle is available (PriceUse::Exit). Only new borrowing needs both feeds fresh and within 2%. |
| List a new collateral | An admin | The listing is listed_collateral in code: jitoSOL and mSOL only. |
Oracle safety
flowchart TD
F[read Pyth and Switchboard] --> A{both fresh, under 120 s?}
A -- yes --> B{within 2%?}
B -- yes --> BR[Borrow uses the lower price]
B -- no --> P[Borrowing paused: OracleDisagreement]
A -- one --> E[Exit uses the fresh one]
A -- none --> U[OracleUnavailable]
F --> E2[Exit with both fresh uses the higher price]- A source is stale after
ORACLE_MAX_AGE_SECS(120 seconds). - A Pyth price whose confidence interval is wider than 2% (
MAX_PYTH_CONFIDENCE_BPS) is not used. - Opening, borrowing and withdrawing collateral need both sources fresh and within 2% (
MAX_ORACLE_DEVIATION_BPS), and use the lower price. - Liquidating and redeeming use the higher fresh price: a trove is only liquidated when every live source says it is unsafe, and a redeemer never receives more collateral than the highest price allows.
- The LST's own SOL-per-token rate is read from its stake pool's accounting (SPL stake pool for jitoSOL, Marinade state for mSOL), not from a market price.
Rounding
Every rounding goes against the party taking value out. Interest is tracked exactly in PAR base units x bps x seconds and only the total is floored when minted, so nothing is lost between calls. Bucket shares round against the borrower.
Debt ceiling
Each branch starts with a ceiling of 1,000,000 PAR, doubling every 30 days, final after 12 doublings (4,096,000,000 PAR). The schedule starts at Protocol.launch_ts and is pure arithmetic on the clock: nobody can raise or lower it.
Edge cases and what the program does
| Situation | Behaviour |
|---|---|
| SOL falls fast | Troves under 120% become liquidatable by anyone at once. The stability pool absorbs first; whatever it cannot cover is redistributed over the other troves in the branch. |
| The stability pool is empty | Liquidations still run: the whole debt is redistributed. If no other trove exists either, liquidate fails with NothingToAbsorb. |
| One oracle is stale | New borrowing pauses (OracleStale). Redemptions and liquidations continue on the fresh source. |
| Both oracles are stale | OracleUnavailable: borrowing, redeeming and liquidating wait until a source returns. Nothing is mispriced in the meantime. |
| A stake pool's rate changes | The branch reads SOL per token from the pool's own accounting on every priced instruction, so collateral value follows it. |
| The program needs a change | It cannot be changed in place. Any different rules would have to live in a different program. |