Skip to content

Commit

Permalink
Introduce "group IDs" to token metadata. (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
jakrawcz authored Nov 14, 2024
1 parent ee83665 commit f20bb11
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 84 deletions.
313 changes: 248 additions & 65 deletions configs/mainnet/hubble.json

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions programs/scope-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,17 @@ pub struct TokensMetadata {
#[derive(Debug, PartialEq, Eq, Default)]
pub struct TokenMetadata {
pub name: [u8; 32],
pub max_age_price_seconds: u64,
pub _reserved: [u64; 16],
pub max_age_price_slots: u64,
pub group_ids_bitset: u64, // a bitset of group IDs in range [0, 64).
pub _reserved: [u64; 15],
}

#[derive(TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
#[repr(u64)]
pub enum UpdateTokenMetadataMode {
Name = 0,
MaxPriceAgeSeconds = 1,
MaxPriceAgeSlots = 1,
GroupIds = 2,
}

#[error_code]
Expand Down
6 changes: 3 additions & 3 deletions programs/scope/src/handlers/handler_refresh_prices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use solana_program::{
};

use crate::{
oracles::{get_price, OracleType},
oracles::{get_non_zero_price, OracleType},
utils::{price_impl::check_ref_price_difference, zero_copy_deserialize},
OracleMappings, ScopeError,
};
Expand Down Expand Up @@ -89,15 +89,15 @@ pub fn refresh_price_list<'info>(
return err!(ScopeError::UnexpectedAccount);
}
let clock = Clock::get()?;
let price_res = get_price(
let price_res = get_non_zero_price(
price_type,
received_account,
&mut accounts_iter,
&clock,
&oracle_twaps,
oracle_mappings,
&ctx.accounts.oracle_prices,
token_nb.into(),
token_idx,
);
let price = if fail_tx_on_error {
price_res?
Expand Down
28 changes: 28 additions & 0 deletions programs/scope/src/handlers/handler_update_token_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{utils::pdas::seeds, ScopeError};
pub enum UpdateTokenMetadataMode {
Name = 0,
MaxPriceAgeSlots = 1,
GroupIds = 2,
}

impl UpdateTokenMetadataMode {
Expand All @@ -18,6 +19,7 @@ impl UpdateTokenMetadataMode {
match self {
UpdateTokenMetadataMode::Name => 0,
UpdateTokenMetadataMode::MaxPriceAgeSlots => 1,
UpdateTokenMetadataMode::GroupIds => 2,
}
}
}
Expand Down Expand Up @@ -70,7 +72,33 @@ pub fn process(
let str_name = std::str::from_utf8(&token_metadata.name).unwrap();
msg!("Setting token name for index {} to {}", index, str_name);
}
UpdateTokenMetadataMode::GroupIds => {
let value = u64::from_le_bytes(value[..8].try_into().unwrap());
msg!(
"Setting token group IDs for index {} to: raw {} == binary {:#b} == positions {:?}",
index,
value,
value,
list_set_bit_positions(value),
);
token_metadata.group_ids_bitset = value;
}
}

Ok(())
}

/// Lists the bit positions (where LSB == 0) of all the set bits (i.e. `1`s) in the given number's
/// binary representation.
/// NOTE: This is a non-critical helper used only for logging of the update operation; should *not*
/// be needed by business logic. The implementation is a compressed version of a crate
/// https://docs.rs/bit-iter/1.2.0/src/bit_iter/lib.rs.html.
fn list_set_bit_positions(mut bits: u64) -> Vec<u8> {
let mut positions = Vec::with_capacity(usize::try_from(bits.count_ones()).unwrap());
while bits != 0 {
let position = u8::try_from(bits.trailing_zeros()).unwrap();
positions.push(position);
bits &= bits.wrapping_sub(1);
}
positions
}
10 changes: 6 additions & 4 deletions programs/scope/src/oracles/jito_restaking.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
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};
use crate::{
utils::{consts::FULL_BPS, math, zero_copy_deserialize},
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)
Expand Down Expand Up @@ -46,10 +47,11 @@ pub fn validate_account(vault: &Option<AccountInfo>) -> Result<()> {
}

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

use super::*;

#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
#[repr(C)]
pub struct DelegationState {
Expand Down
2 changes: 1 addition & 1 deletion programs/scope/src/oracles/meteora_dlmm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ where
let q64x64_price = if a_to_b {
U192::from(q64x64_price)
} else {
// Invert price
// Invert price - safe, since `lb_clmm::get_x64_price_from_id` never returns 0.
(U192::one() << 128) / q64x64_price
};

Expand Down
11 changes: 9 additions & 2 deletions programs/scope/src/oracles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl OracleType {
/// If needed the `extra_accounts` will be extracted from the provided iterator and checked
/// with the data contained in the `base_account`
#[allow(clippy::too_many_arguments)]
pub fn get_price<'a, 'b>(
pub fn get_non_zero_price<'a, 'b>(
price_type: OracleType,
base_account: &AccountInfo<'a>,
extra_accounts: &mut impl Iterator<Item = &'b AccountInfo<'a>>,
Expand All @@ -164,7 +164,7 @@ pub fn get_price<'a, 'b>(
where
'a: 'b,
{
match price_type {
let price = match price_type {
OracleType::Pyth => pyth::get_price(base_account, clock),
OracleType::PythPullBased => pyth_pull_based::get_price(base_account, clock),
OracleType::PythPullBasedEMA => pyth_pull_based_ema::get_price(base_account, clock),
Expand Down Expand Up @@ -269,7 +269,14 @@ where
OracleType::DeprecatedPlaceholder1 | OracleType::DeprecatedPlaceholder2 => {
panic!("DeprecatedPlaceholder is not a valid oracle type")
}
}?;
// The price providers above are performing their type-specific validations, but are still free
// to return 0, which we can only tolerate in case of explicit fixed price:
if price.price.value == 0 && price_type != OracleType::FixedPrice {
msg!("Price is 0 (token {index}, type {price_type:?}): {price:?}",);
return err!(ScopeError::PriceNotValid);
}
Ok(price)
}

/// Validate the given account as being an appropriate price account for the
Expand Down
5 changes: 0 additions & 5 deletions programs/scope/src/oracles/pyth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,6 @@ pub fn validate_valid_price(
});
}

if price == 0 {
msg!("Pyth price is 0");
return Err(ScopeError::PriceNotValid);
}

let conf: u128 = pyth_price.conf.into();
check_confidence_interval(
price.into(),
Expand Down
3 changes: 2 additions & 1 deletion programs/scope/src/states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ pub struct TokenMetadatas {
pub struct TokenMetadata {
pub name: [u8; 32],
pub max_age_price_slots: u64,
pub _reserved: [u64; 16],
pub group_ids_bitset: u64, // a bitset of group IDs in range [0, 64).
pub _reserved: [u64; 15],
}

static_assertions::const_assert_eq!(CONFIGURATION_SIZE, std::mem::size_of::<Configuration>());
Expand Down

0 comments on commit f20bb11

Please sign in to comment.