Skip to main content




This byte specifies the version of the Solido state.

pub const LIDO_VERSION: u8 = 0;


This defines the size of the header of the Solido state.

pub const LIDO_CONSTANT_HEADER_SIZE: usize = 1 + 2 * 32 + 8 + 2;

The header size should be equivalent to the following:

  • the size of the version byte
  • the size of the manager and st_sol_mint public keys
  • the size of the st_sol_total_shares which is an stLamports struct
  • the size of the sol_reserve_authority_bump_seed and the deposit_authority_bump_seed


This defines the size of the fee section of the Lido state.

pub const LIDO_CONSTANT_FEE_SIZE: usize = 2 * 32 + 3 * 4;

This should be equivalent to the following:

  • the size of the FeeDistribution struct
  • the size of the FeeRecipients struct



The validators type uses the AccountMap struct and the Validator struct to create a helper type to assist in dealing with the collection of validators needed for the Soldio state.

pub type Validators = AccountMap<Validator>;


The Maintainers type leverages a type alias, AccountSet, for an AccountMap with a unit type.

pub type Maintainers = AccountSet;



This is the main structure for maintaining the Solido state.

pub struct Lido {
pub lido_version: u8,
pub manager: Pubkey,
pub st_sol_mint: Pubkey,
pub st_sol_total_shares: StLamports,
pub sol_reserve_authority_bump_seed: u8,
pub deposit_authority_bump_seed: u8,
pub fee_distribution: FeeDistribution,
pub fee_recipients: FeeRecipients,
pub validators: Validators,
pub maintainers: Maintainers,

Implemented functions on Lido


This function calculates the size of the Solido state if it had the maximum number of validators and maintainers.

pub fn calculate_size(max_validators: u32, max_maintainers: u32) -> usize {

This function calculates the correct amount of pool tokens, stSOL, for a given stake deposit in SOL.

equivalent pool token (stLamports)=stake equivalent in stLamportsTotal Shares stLamportsTotal Lamports\textnormal{equivalent pool token (stLamports)}=\textnormal{stake equivalent in stLamports}*\frac{\textnormal{Total Shares stLamports}}{\textnormal{Total Lamports}}
pub fn calc_pool_tokens_for_deposit(
stake_lamports: Lamports,
total_lamports: Lamports,
) -> Option<StLamports> {

Checks if the instance of Solido has already been initialized.

pub fn is_initialized(&self) -> ProgramResult {

Confirms that the passed mint account is indeed Solido's expected stSOL mint.

pub fn check_mint_is_st_sol_mint(&self, mint_account_info: &AccountInfo) -> ProgramResult {

Confirms that the given account is an SPL token account with our stSOL mint as mint.

pub fn check_is_st_sol_account(&self, token_account_info: &AccountInfo) -> ProgramResult {

Checks if the passed manager is the same as the one stored in the state

pub fn check_manager(&self, manager: &AccountInfo) -> ProgramResult {

Checks if the passed maintainer belong to the list of maintainers

pub fn check_maintainer(&self, maintainer: &AccountInfo) -> ProgramResult {

This function returns the address of the reserve account, i.e. the account where SOL gets deposited to.

pub fn get_reserve_account(
program_id: &Pubkey,
solido_address: &Pubkey,
) -> Result<Pubkey, ProgramError> {

Confirms that the reserve authority passed does in fact belong to this Solido instance.

pub fn check_reserve_authority(
program_id: &Pubkey,
solido_address: &Pubkey,
reserve_authority_info: &AccountInfo,
) -> Result<Pubkey, ProgramError> {


The validator struct maintains the data regarding each validator that is required to generate staking accounts and maintain the fees due.

pub struct Validator {
pub fee_credit: StLamports,
pub fee_address: Pubkey,
pub stake_accounts_seed_begin: u64,
pub stake_accounts_seed_end: u64,
pub stake_accounts_balance: Lamports,

Implemented functions on Validator


This function finds a stake account associated with a given validator vote account.

pub fn find_stake_account_address(
program_id: &Pubkey,
solido_account: &Pubkey,
validator_vote_account: &Pubkey,
seed: u64,
) -> (Pubkey, u8) {

This function wraps the Solana Pubkey::find_program_address. That function tries to find a valid program address and its corresponding bump seed which must be passed as an additional seed when calling invoke_signed. The processes of finding a valid program address is by trial and error,and even though it is deterministic given a set of inputs it can take a variable amount of time to succeed across different inputs. This means that when called from an on-chain program it may incur a variable amount of the program's compute budget. Programs that are meant to be very performant may not want to use this function because it could take a considerable amount of time. Also, programs that area already at risk of exceeding their compute budget should also call this with care since there is a chance that the program's budget may be occasionally exceeded.

Additional Note: The underlying Solana program function will panic in the very unlikely event that the additional seed could not be found.


The FeeDistribution struct simply maintains the ratios of distribution between the treasury, developer (Lido team), and the validators.

pub struct FeeDistribution {
pub treasury_fee: u32,
pub validation_fee: u32,
pub developer_fee: u32,


The FeeRecipients struct is another simple struct that holds the Pubkey addresses of the treasury and developer (Lido team) accounts that will receive fees.

pub struct FeeRecipients {
pub treasury_account: Pubkey,
pub developer_account: Pubkey,