Skip to content

Commit

Permalink
Release 0.17.0 (#8)
Browse files Browse the repository at this point in the history
- Add Jito Restaking token prices (VRT)
  • Loading branch information
oeble authored Oct 29, 2024
1 parent 2fb6740 commit ee83665
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 0 deletions.
243 changes: 243 additions & 0 deletions programs/scope/src/oracles/jito_restaking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
use anchor_lang::prelude::*;
use decimal_wad::decimal::Decimal;

use crate::utils::consts::FULL_BPS;
use crate::utils::{math, zero_copy_deserialize};
use crate::{DatedPrice, Price};

/// Jito restaking price oracle gives the amount of JitoSOL per VRT token on withdrawal
/// WARNING: Assumes both tokens have the same decimals (9)
pub fn get_price(jito_vault: &AccountInfo, clock: &Clock) -> Result<DatedPrice> {
let vault = zero_copy_deserialize::<jito_vault_core::Vault>(jito_vault)?;

let dated_price = DatedPrice {
price: get_price_int(&vault),
last_updated_slot: clock.slot,
unix_timestamp: u64::try_from(clock.unix_timestamp).unwrap(),
..Default::default()
};

Ok(dated_price)
}

fn get_price_int(vault: &jito_vault_core::Vault) -> Price {
let vrt_supply = vault.vrt_supply.get();
if vrt_supply == 0 {
return Price::default();
}

let total_deposits = vault.tokens_deposited.get();

let total_fee_bps = vault.program_fee_bps.get() + vault.withdrawal_fee_bps.get();

let withdrawable_amount = math::mul_bps(total_deposits, FULL_BPS.saturating_sub(total_fee_bps));

let price_dec = Decimal::from(withdrawable_amount) / vrt_supply;
price_dec.into()
}

pub fn validate_account(vault: &Option<AccountInfo>) -> Result<()> {
let Some(vault) = vault else {
msg!("No vault account provided");
return err!(crate::ScopeError::UnexpectedAccount);
};
let _ = zero_copy_deserialize::<jito_vault_core::Vault>(vault)?;
Ok(())
}

pub mod jito_vault_core {
use super::*;
use anchor_lang::Discriminator;
use bytemuck::{Pod, Zeroable};

#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
#[repr(C)]
pub struct DelegationState {
/// The amount of stake that is currently active on the operator
staked_amount: PodU64,

/// Any stake that was deactivated in the current epoch
enqueued_for_cooldown_amount: PodU64,

/// Any stake that was deactivated in the previous epoch,
/// to be available for re-delegation in the current epoch + 1
cooling_down_amount: PodU64,

reserved: [u8; 256],
}

#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
#[repr(C)]
pub struct Vault {
/// The base account of the VRT
pub base: Pubkey,

// ------------------------------------------
// Token information and accounting
// ------------------------------------------
/// Mint of the VRT token
pub vrt_mint: Pubkey,

/// Mint of the token that is supported by the VRT
pub supported_mint: Pubkey,

/// The total number of VRT in circulation
pub vrt_supply: PodU64,

/// The total number of tokens deposited
pub tokens_deposited: PodU64,

/// The maximum deposit capacity allowed in the mint_to instruction.
/// The deposited assets in the vault may exceed the deposit_capacity during other operations, such as vault balance updates.
pub deposit_capacity: PodU64,

/// Rolled-up stake state for all operators in the set
pub delegation_state: DelegationState,

/// The amount of additional assets that need unstaking to fulfill VRT withdrawals
pub additional_assets_need_unstaking: PodU64,

/// The amount of VRT tokens in VaultStakerWithdrawalTickets enqueued for cooldown
pub vrt_enqueued_for_cooldown_amount: PodU64,

/// The amount of VRT tokens cooling down
pub vrt_cooling_down_amount: PodU64,

/// The amount of VRT tokens ready to claim
pub vrt_ready_to_claim_amount: PodU64,

// ------------------------------------------
// Admins
// ------------------------------------------
/// Vault admin
pub admin: Pubkey,

/// The delegation admin responsible for adding and removing delegations from operators.
pub delegation_admin: Pubkey,

/// The operator admin responsible for adding and removing operators.
pub operator_admin: Pubkey,

/// The node consensus network admin responsible for adding and removing support for NCNs.
pub ncn_admin: Pubkey,

/// The admin responsible for adding and removing slashers.
pub slasher_admin: Pubkey,

/// The admin responsible for setting the capacity
pub capacity_admin: Pubkey,

/// The admin responsible for setting the fees
pub fee_admin: Pubkey,

/// The delegate_admin responsible for delegating assets
pub delegate_asset_admin: Pubkey,

/// Fee wallet account
pub fee_wallet: Pubkey,

/// Optional mint signer
pub mint_burn_admin: Pubkey,

/// ( For future use ) Authority to update the vault's metadata
pub metadata_admin: Pubkey,

// ------------------------------------------
// Indexing and counters
// These are helpful when one needs to iterate through all the accounts
// ------------------------------------------
/// The index of the vault in the vault list
pub vault_index: PodU64,

/// Number of VaultNcnTicket accounts tracked by this vault
pub ncn_count: PodU64,

/// Number of VaultOperatorDelegation accounts tracked by this vault
pub operator_count: PodU64,

/// Number of VaultNcnSlasherTicket accounts tracked by this vault
pub slasher_count: PodU64,

/// The slot of the last fee change
pub last_fee_change_slot: PodU64,

/// The slot of the last time the delegations were updated
pub last_full_state_update_slot: PodU64,

/// The deposit fee in basis points
pub deposit_fee_bps: PodU16,

/// The withdrawal fee in basis points
pub withdrawal_fee_bps: PodU16,

/// The next epoch's withdrawal fee in basis points
pub next_withdrawal_fee_bps: PodU16,

/// Fee for each epoch
pub reward_fee_bps: PodU16,

/// (Copied from Config) The program fee in basis points
pub program_fee_bps: PodU16,

/// The bump seed for the PDA
pub bump: u8,

pub is_paused: u8,

/// Reserved space
pub reserved: [u8; 259],
}

impl Discriminator for Vault {
const DISCRIMINATOR: [u8; 8] = [2, 0, 0, 0, 0, 0, 0, 0];
fn discriminator() -> [u8; 8] {
Self::DISCRIMINATOR
}
}

impl Default for Vault {
fn default() -> Self {
Zeroable::zeroed()
}
}

#[derive(Clone, Copy, Default, PartialEq, Pod, Zeroable, Eq)]
#[repr(transparent)]
pub struct PodU64([u8; 8]);

impl PodU64 {
pub fn get(&self) -> u64 {
u64::from_le_bytes(self.0)
}

pub fn set(&mut self, value: u64) {
self.0 = value.to_le_bytes();
}
}

impl From<u64> for PodU64 {
fn from(value: u64) -> Self {
PodU64(value.to_le_bytes())
}
}

#[derive(Clone, Copy, Default, PartialEq, Pod, Zeroable, Eq)]
#[repr(transparent)]
pub struct PodU16([u8; 2]);

impl PodU16 {
pub fn get(&self) -> u16 {
u16::from_le_bytes(self.0)
}

pub fn set(&mut self, value: u16) {
self.0 = value.to_le_bytes();
}
}

impl From<u16> for PodU16 {
fn from(value: u16) -> Self {
PodU16(value.to_le_bytes())
}
}
}
8 changes: 8 additions & 0 deletions programs/scope/src/oracles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod ktokens;
#[cfg(feature = "yvaults")]
pub mod ktokens_token_x;

pub mod jito_restaking;
pub mod jupiter_lp;
pub mod meteora_dlmm;
pub mod msol_stake;
Expand Down Expand Up @@ -104,6 +105,8 @@ pub enum OracleType {
FixedPrice = 23,
/// Switchboard on demand
SwitchboardOnDemand = 24,
/// Jito restaking tokens
JitoRestaking = 25, // TODO adjust if we merge ALP first
}

impl OracleType {
Expand Down Expand Up @@ -134,6 +137,7 @@ impl OracleType {
| OracleType::RaydiumAmmV3BtoA => 25_000,
OracleType::MeteoraDlmmAtoB | OracleType::MeteoraDlmmBtoA => 30_000,
OracleType::JupiterLpCompute | OracleType::JupiterLpScope => 120_000,
OracleType::JitoRestaking => 25_000,
OracleType::DeprecatedPlaceholder1 | OracleType::DeprecatedPlaceholder2 => {
panic!("DeprecatedPlaceholder is not a valid oracle type")
}
Expand Down Expand Up @@ -259,6 +263,9 @@ where
..Default::default()
})
}
OracleType::JitoRestaking => {
jito_restaking::get_price(base_account, clock).map_err(Into::into)
}
OracleType::DeprecatedPlaceholder1 | OracleType::DeprecatedPlaceholder2 => {
panic!("DeprecatedPlaceholder is not a valid oracle type")
}
Expand Down Expand Up @@ -320,6 +327,7 @@ pub fn validate_oracle_cfg(
.map_err(|_| error!(ScopeError::FixedPriceInvalid))?;
Ok(())
}
OracleType::JitoRestaking => jito_restaking::validate_account(price_account),
OracleType::DeprecatedPlaceholder1 | OracleType::DeprecatedPlaceholder2 => {
panic!("DeprecatedPlaceholder is not a valid oracle type")
}
Expand Down
2 changes: 2 additions & 0 deletions programs/scope/src/utils/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ pub const TOKEN_METADATA_SIZE: usize = 86016;
/// Used when calling [`crate::utils::math::check_confidence_interval`]
/// for pyth prices (confidence interval check) and switchboard prices (standard deviation check)
pub const ORACLE_CONFIDENCE_FACTOR: u32 = super::math::confidence_bps_to_factor(200); // 2%

pub const FULL_BPS: u16 = 10_000;
7 changes: 7 additions & 0 deletions programs/scope/src/utils/math.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use decimal_wad::{decimal::U192, rate::U128};
use raydium_amm_v3::libraries::U256;
use yvaults::utils::FULL_BPS;

use crate::{Price, ScopeError, ScopeResult};

Expand Down Expand Up @@ -216,3 +217,9 @@ pub fn check_confidence_interval(

Ok(())
}

pub fn mul_bps(amount: impl Into<u128>, bps: impl Into<u128>) -> u128 {
let a = amount.into();
let b = bps.into();
a * b / u128::from(FULL_BPS)
}

0 comments on commit ee83665

Please sign in to comment.