Assessment reports>Pye>Design>Solo validator bonds

Solo validator bonds

Description

A user can deposit SOL or staked SOL into a solo validator bond, and the Pye program delegates these funds to a validator using the SPL stake program. The Pye program has one main stake account and one transient stake account for each solo validator bond, both of which may be active with staked funds.

If a user deposits staked SOL, their stake is directly merged into the bond's stake account.

Since unstaked SOL cannot be directly deposited into a stake account, if a user deposits unstaked SOL, a more complicated process occurs:

  1. If the bond's stake account does not exist, it is created with the user's deposit.

  2. If the bond's stake account does exist but is activating, an uninitialized stake account owned by the user is merged into the stake account instead.

  3. If the bond's stake account is fully active, the transient stake account is used.

When using the transient stake account,

  1. If the transient stake account is empty, it is created with the user's deposit.

  2. If the transient stake account does exist but is uninitialized, it is initialized with the user's deposit.

  3. If the transient stake account does exist but is activating, an uninitialized stake account owned by the user is merged into the transient account instead.

  4. If the transient stake account is fully activated, it is merged into the main stake account, and then the transient stake account is reinitialized with the user's deposit.

PTs and YTs are then minted and distributed to the user for their deposit, to be later burned when the bond has matured to be redeemed for SOL or staked SOL.

Invariants

  • Funds cannot be redeemed until the bond has matured. This is enforced by using the maturity_ts timestamp, set when the bond is created. The handle_maturity instruction that marks a bond as mature can only be called when the current timestamp is greater than maturity_ts.

  • Only authorized entities should be able to redeem from the bond. When depositing, PTs and YTs are minted and distributed to the user and are required to redeem from the bond.

  • Users cannot withdraw more than they are entitled to. Redemption amount is directly related to the amount of PTs and YTs redeemed. Users entitled to more rewards are given more tokens. Once the tokens are redeemed, they are burned so they cannot be used twice.

  • Users depositing early should have higher yields than users depositing closer to the bond's maturity. The amount of YTs minted for a deposit linearly decays with the time until the bond's maturity.

  • The amount per YT should be correlated with the rewards from staking. Staking rewards are automatically added to the stake account's staked balance, which is used in the yield calculations.

  • Total rewards from the bond must be less than or equal to the lamport balance of the bond. When redeeming, the lamports per YT is calculated as a proportion of the total lamport amount of the bond (including the transient stake account).

Test coverage

Cases covered

  • Initializing of solo validator bonds

  • Depositing SOL / staked SOL into a solo validator bond

  • Calling the handle_maturity instruction after the bond has matured

  • Redeeming PT/YT for SOL / staked SOL

Cases not covered

  • Calling the handle_maturity instruction before the bond has matured

  • Attempting to redeem more PTs/YTs than the user has access to

  • Checking whether different users can deposit / redeem from a solo validator bond

  • Redeeming multiple times / at different times

Attack surface

  • Solo validator bond. A SoloValidatorBond account can only be created via the initialize_solo_validator_bond instruction, which validates the bond's settings (for example, maturity timestamp, token mint, and so on).

  • PTs/YTs. These tokens can only be minted by the program, and they have to match the bond's token mint.

  • Stake accounts. The main stake account and transient stake account must match the accounts stored in the SoloValidatorBond account, which are PDAs derived from the bond's address.

  • Fee wallet / global counter party. These are checked by matching with global settings.

  • Fee-wallet token accounts. Fee-wallet accounts must have the fee wallet as their authority. The token mint is validated when fee tokens are distributed to the token account.

Zellic © 2025Back to top ↑