Skip to content

Commit

Permalink
Merge pull request #1 from Kamino-Finance/release/0.14.0
Browse files Browse the repository at this point in the history
Release 0.14.0
  • Loading branch information
oeble authored May 20, 2024
2 parents d5801f5 + 549a960 commit c065d3a
Show file tree
Hide file tree
Showing 38 changed files with 1,147 additions and 552 deletions.
395 changes: 175 additions & 220 deletions Cargo.lock

Large diffs are not rendered by default.

341 changes: 240 additions & 101 deletions configs/mainnet/hubble.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions programs/scope-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ crate-type = ["cdylib", "lib"]
devnet = []
localnet = []
mainnet = []
staging = []

[dependencies]
anchor-lang = ">= 0.28.0"
Expand Down
1 change: 1 addition & 0 deletions programs/scope-types/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn main() {
println!("cargo:rerun-if-env-changed=CLUSTER");
// Set feature according to current cluster
match cluster.as_str() {
"staging" => println!("cargo:rustc-cfg=feature=\"staging\""),
"localnet" => println!("cargo:rustc-cfg=feature=\"localnet\""),
"devnet" => {
println!("cargo:rustc-cfg=feature=\"devnet\"");
Expand Down
8 changes: 4 additions & 4 deletions programs/scope-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,12 @@ pub struct OracleMappings {
}

impl OracleMappings {
pub fn is_twap_enabled(&self, token: usize) -> bool {
self.twap_enabled[token] > 0
pub fn is_twap_enabled(&self, entry_id: usize) -> bool {
self.twap_enabled[entry_id] > 0
}

pub fn get_twap_source(&self, token: usize) -> usize {
usize::from(self.twap_source[token])
pub fn get_twap_source(&self, entry_id: usize) -> usize {
usize::from(self.twap_source[entry_id])
}
}

Expand Down
17 changes: 16 additions & 1 deletion programs/scope-types/src/program_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,30 @@ compile_error!("'localnet' and 'devnet' features are mutually exclusive");
#[cfg(all(feature = "mainnet", feature = "skip_price_validation"))]
compile_error!("'mainnet' and 'skip_price_validation' features are mutually exclusive");

#[cfg(all(feature = "mainnet", feature = "staging"))]
compile_error!("'mainnet' and 'staging' features are mutually exclusive");

#[cfg(all(feature = "localnet", feature = "staging"))]
compile_error!("'localnet' and 'staging' features are mutually exclusive");

#[cfg(all(feature = "devnet", feature = "staging"))]
compile_error!("'devnet' and 'staging' features are mutually exclusive");

#[cfg(all(feature = "skip_price_validation", feature = "staging"))]
compile_error!("'skip_price_validation' and 'staging' features are mutually exclusive");

cfg_if::cfg_if! {
if #[cfg(feature = "mainnet")] {
pub const PROGRAM_ID:Pubkey = pubkey!("HFn8GnPADiny6XqUoWE8uRPPxb29ikn4yTuPa9MF2fWJ");
}
else if #[cfg(feature = "staging")] {
pub const PROGRAM_ID:Pubkey = pubkey!("scpStzYvKzE7DHwsGMP5XLhcMTuLr3feoiC9mJ3yHr5");
}
else if #[cfg(feature = "localnet")] {
pub const PROGRAM_ID:Pubkey = pubkey!("2fU6YqiA2aj9Ct1tDagA8Tng7otgxHM5KwgnsUWsMFxM");
} else if #[cfg(feature = "devnet")] {
pub const PROGRAM_ID:Pubkey = pubkey!("3Vw8Ngkh1MVJTPHthmUbmU2XKtFEkjYvJzMqrv2rh9yX");
} else {
compile_error!("At least one of 'mainnet', 'localnet' or 'devnet' feature need to be set");
compile_error!("At least one of 'mainnet', 'staging', 'localnet' or 'devnet' feature need to be set");
}
}
7 changes: 5 additions & 2 deletions programs/scope/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ yvaults = ["dep:yvaults"]
devnet = ["skip_price_validation"]
localnet = []
mainnet = []
staging = []
serde = ["dep:serde"]

[dependencies]
anchor-lang = "0.28.0"
anchor-spl = "0.28.0"
solana-program = "~1.16.18"
solana-program = ">1.16.18"
bytemuck = { version = "1.4.0", features = ["min_const_generics", "derive"] }
num_enum = "0.7.0"
cfg-if = "1.0.0"
Expand All @@ -44,7 +45,7 @@ arrayref = "0.3.6"
decimal-wad = "0.1.7"
rust_decimal = "1.18.0"
# Comment out the line below if you do not have access to the yvaults repo
yvaults = { git = "ssh://git@github.com/hubbleprotocol/yvaults.git", features = [
yvaults = { git = "ssh://git@github.com/hubbleprotocol/yvaults.git", branch = "scope-public-compat", features = [
"no-entrypoint",
"cpi",
"mainnet",
Expand All @@ -63,4 +64,6 @@ raydium-amm-v3 = { git = "https://github.com/raydium-io/raydium-clmm", features
jup-perp-itf = { path = "../jup-perp-itf", features = ["cpi"] }
lb-clmm-itf = { path = "../lb-clmm-itf", features = ["no-entrypoint"] }
intbits = "0.2.0"
pyth-solana-receiver-sdk = "0.1.0"
static_assertions = "1.1.0"

1 change: 1 addition & 0 deletions programs/scope/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn main() {
println!("cargo:rerun-if-env-changed=CLUSTER");
// Set feature according to current cluster
match cluster.as_str() {
"staging" => println!("cargo:rustc-cfg=feature=\"staging\""),
"localnet" => println!("cargo:rustc-cfg=feature=\"localnet\""),
"devnet" => {
println!("cargo:rustc-cfg=feature=\"devnet\"");
Expand Down
9 changes: 9 additions & 0 deletions programs/scope/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ pub enum ScopeError {

#[msg("The stake pool fee is higher than the maximum allowed")]
StakeFeeTooHigh,

#[msg("Cannot get a valid price for the tokens composing the Ktoken")]
KTokenUnderlyingPriceNotValid,

#[msg("Error while computing the Ktoken pool holdings")]
KTokenHoldingsCalculationError,

#[msg("Cannot resize the account we only allow it to grow in size")]
CannotResizeAccount,
}

impl<T> From<TryFromPrimitiveError<T>> for ScopeError
Expand Down
62 changes: 62 additions & 0 deletions programs/scope/src/handlers/handler_extend_mapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::ScopeError;
use anchor_lang::prelude::*;

use crate::utils::consts::ORACLE_MAPPING_EXTENDED_SIZE;
use crate::utils::pdas::seeds;

#[derive(Accounts)]
#[instruction(feed_name: String)]
pub struct ExtendMapping<'info> {
/// CHECK: At creation admin can be anyone, this ix can't override an existing feed
#[account(mut)]
pub admin: Signer<'info>,

// Set space to max size here
// The ability to create multiple feeds is mostly useful for tests
#[account(seeds = [seeds::CONFIG, feed_name.as_bytes()], bump, has_one = admin, has_one = oracle_mappings)]
pub configuration: AccountLoader<'info, crate::Configuration>,

/// CHECK: checked above
#[account(mut, owner = crate::ID)]
pub oracle_mappings: AccountInfo<'info>,

pub system_program: Program<'info, System>,
}

pub fn process(ctx: Context<ExtendMapping>, _: String) -> Result<()> {
resize_account_and_make_rent_exempt(
&mut ctx.accounts.oracle_mappings,
&ctx.accounts.admin,
&ctx.accounts.system_program,
ORACLE_MAPPING_EXTENDED_SIZE + 8,
)
}

fn resize_account_and_make_rent_exempt<'a>(
account: &mut AccountInfo<'a>,
payer: &Signer<'a>,
system_program: &Program<'a, System>,
new_size: usize,
) -> Result<()> {
if new_size <= account.data_len() {
// It can be resized even when new_size <= old_size, but we want to exclude
// this use case in order to catch human errors.
return Err(error!(ScopeError::CannotResizeAccount));
}
// Make account rent exempt for the new size by transfering enough lamports.
let rent = Rent::get()?;
let new_minimum_balance = rent.minimum_balance(new_size);

let lamports_diff = new_minimum_balance.saturating_sub(account.lamports());
solana_program::program::invoke(
&solana_program::system_instruction::transfer(payer.key, account.key, lamports_diff),
&[
payer.to_account_info().clone(),
account.clone(),
system_program.to_account_info(),
],
)?;

// No need to zero init as we always grow memory.
account.realloc(new_size, false).map_err(Into::into)
}
23 changes: 11 additions & 12 deletions programs/scope/src/handlers/handler_initialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,38 +27,37 @@ pub struct Initialize<'info> {

// Account is pre-reserved/paid outside the program
#[account(zero)]
pub oracle_mappings: AccountLoader<'info, crate::OracleMappings>,
pub oracle_mappings: AccountLoader<'info, crate::OracleMappingsOld>,
}

pub fn process(ctx: Context<Initialize>, _: String) -> Result<()> {
// Initialize oracle mapping account
let _ = ctx.accounts.oracle_mappings.load_init()?;
let _ = ctx.accounts.token_metadatas.load_init()?;
let mut oracle_prices = ctx.accounts.oracle_prices.load_init()?;
let mut oracle_twaps = ctx.accounts.oracle_twaps.load_init()?;
let mut configuration: std::cell::RefMut<'_, crate::Configuration> =
ctx.accounts.configuration.load_init()?;

// Initialize oracle price account
let admin = ctx.accounts.admin.key();
let oracle_pbk = ctx.accounts.oracle_mappings.key();
let twaps_pbk = ctx.accounts.oracle_twaps.key();
let prices_pbk = ctx.accounts.oracle_prices.key();
let metadata_pbk = ctx.accounts.token_metadatas.key();

let mut oracle_prices = ctx.accounts.oracle_prices.load_init()?;
// Initialize oracle mapping account
oracle_prices.oracle_mappings = oracle_pbk;

// Initialize configuration account
let prices_pbk = ctx.accounts.oracle_prices.key();
let admin = ctx.accounts.admin.key();
let mut configuration: std::cell::RefMut<'_, crate::Configuration> =
ctx.accounts.configuration.load_init()?;
configuration.admin = admin;
configuration.oracle_mappings = oracle_pbk;
configuration.oracle_prices = prices_pbk;
configuration.oracle_twaps = twaps_pbk;
configuration.tokens_metadata = metadata_pbk;
configuration.admin_cached = Pubkey::default();

// Initialize oracle twap account
let mut oracle_twaps = ctx.accounts.oracle_twaps.load_init()?;
oracle_twaps.oracle_prices = prices_pbk;
oracle_twaps.oracle_mappings = oracle_pbk;

let _ = ctx.accounts.token_metadatas.load_init()?;
configuration.tokens_metadata = ctx.accounts.token_metadatas.key();

Ok(())
}
14 changes: 9 additions & 5 deletions programs/scope/src/handlers/handler_refresh_prices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use solana_program::{
},
};

use crate::utils::zero_copy_deserialize;
use crate::OracleMappingsCore;
use crate::{
oracles::{get_price, OracleType},
ScopeError,
Expand All @@ -20,8 +22,9 @@ const COMPUTE_BUDGET_ID: Pubkey = pubkey!("ComputeBudget111111111111111111111111
pub struct RefreshList<'info> {
#[account(mut, has_one = oracle_mappings)]
pub oracle_prices: AccountLoader<'info, crate::OraclePrices>,
#[account()]
pub oracle_mappings: AccountLoader<'info, crate::OracleMappings>,
/// CHECK: Checked above
#[account(owner = crate::ID)]
pub oracle_mappings: AccountInfo<'info>,
#[account(mut, has_one = oracle_prices, has_one = oracle_mappings)]
pub oracle_twaps: AccountLoader<'info, crate::OracleTwaps>,
/// CHECK: Sysvar fixed address
Expand All @@ -36,7 +39,8 @@ pub fn refresh_price_list<'info>(
) -> Result<()> {
check_execution_ctx(&ctx.accounts.instruction_sysvar_account_info)?;

let oracle_mappings = &ctx.accounts.oracle_mappings.load()?;
let oracle_mappings =
&zero_copy_deserialize::<OracleMappingsCore>(&ctx.accounts.oracle_mappings)?;
let mut oracle_twaps = ctx.accounts.oracle_twaps.load_mut()?;

// No token to refresh
Expand Down Expand Up @@ -102,9 +106,9 @@ pub fn refresh_price_list<'info>(
} else {
match price_res {
Ok(price) => price,
Err(e) => {
Err(_) => {
msg!(
"Price skipped as validation failed (token {token_idx}, type {price_type:?}): {e}",
"Price skipped as validation failed (token {token_idx}, type {price_type:?})",
);
continue;
}
Expand Down
12 changes: 6 additions & 6 deletions programs/scope/src/handlers/handler_reset_twap.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anchor_lang::prelude::*;
use solana_program::sysvar::instructions::ID as SYSVAR_INSTRUCTIONS_ID;

use crate::oracles::check_context;
use crate::{oracles::check_context, utils::pdas::seeds};

#[derive(Accounts)]
#[instruction(token:u64, feed_name: String)]
Expand All @@ -10,11 +10,11 @@ pub struct ResetTwap<'info> {

#[account()]
pub oracle_prices: AccountLoader<'info, crate::OraclePrices>,
#[account(seeds = [b"conf", feed_name.as_bytes()], bump,
has_one = admin,
has_one = oracle_prices,
has_one = oracle_twaps,
)]
#[account(seeds = [seeds::CONFIG, feed_name.as_bytes()], bump,
has_one = admin,
has_one = oracle_prices,
has_one = oracle_twaps,
)]
pub configuration: AccountLoader<'info, crate::Configuration>,
#[account(mut, has_one = oracle_prices)]
pub oracle_twaps: AccountLoader<'info, crate::OracleTwaps>,
Expand Down
48 changes: 32 additions & 16 deletions programs/scope/src/handlers/handler_update_mapping.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,59 @@
use anchor_lang::prelude::*;

use crate::utils::pdas::seeds;
use crate::utils::zero_copy_deserialize_mut;
use crate::{
oracles::{check_context, validate_oracle_account, OracleType},
OracleMappings, ScopeError,
OracleMappingsCore, ScopeError,
};
use anchor_lang::prelude::*;

#[derive(Accounts)]
#[instruction(token:u64, price_type: u8, twap_enabled: bool, twap_source: u16, feed_name: String)]
#[instruction(token:u16, price_type: u8, twap_enabled: bool, twap_source: u16, ref_price_index: u16, feed_name: String)]
pub struct UpdateOracleMapping<'info> {
pub admin: Signer<'info>,
#[account(seeds = [b"conf", feed_name.as_bytes()], bump, has_one = admin, has_one = oracle_mappings)]
#[account(seeds = [seeds::CONFIG, feed_name.as_bytes()], bump, has_one = admin, has_one = oracle_mappings)]
pub configuration: AccountLoader<'info, crate::Configuration>,
#[account(mut)]
pub oracle_mappings: AccountLoader<'info, OracleMappings>,

/// CHECK: checked above + on deserialize
#[account(mut, owner = crate::ID)]
pub oracle_mappings: AccountInfo<'info>,
/// CHECK: We trust the admin to provide a trustable account here. Some basic sanity checks are done based on type
pub price_info: Option<AccountInfo<'info>>,
}

pub fn reset_price_ref_mapping(ctx: Context<UpdateOracleMapping>) -> Result<()> {
let mut oracle_mappings =
zero_copy_deserialize_mut::<OracleMappingsCore>(&ctx.accounts.oracle_mappings)?;
for i in 0..oracle_mappings.price_info_accounts.len() {
oracle_mappings.ref_price[i] = u16::MAX;
}
Ok(())
}

pub fn process(
ctx: Context<UpdateOracleMapping>,
token: usize,
entry_id: usize,
price_type: u8,
twap_enabled: bool,
twap_source: u16,
ref_price_index: u16,
_: String,
) -> Result<()> {
check_context(&ctx)?;

msg!(
"UpdateOracleMapping, token: {}, price_type: {}, twap_enabled: {}, twap_source: {}",
token,
"UpdateOracleMapping, token: {}, price_type: {}, twap_enabled: {}, twap_source: {}, ref_price_index: {}",
entry_id,
price_type,
twap_enabled,
twap_source
twap_source,
ref_price_index
);

let mut oracle_mappings = ctx.accounts.oracle_mappings.load_mut()?;
let mut oracle_mappings =
zero_copy_deserialize_mut::<OracleMappingsCore>(&ctx.accounts.oracle_mappings)?;
let ref_price_pubkey = oracle_mappings
.price_info_accounts
.get_mut(token)
.get_mut(entry_id)
.ok_or(ScopeError::BadTokenNb)?;
let price_type: OracleType = price_type
.try_into()
Expand All @@ -61,9 +76,10 @@ pub fn process(
}
}

oracle_mappings.price_types[token] = price_type.into();
oracle_mappings.twap_enabled[token] = u8::from(twap_enabled);
oracle_mappings.twap_source[token] = twap_source;
oracle_mappings.price_types[entry_id] = price_type.into();
oracle_mappings.twap_enabled[entry_id] = u8::from(twap_enabled);
oracle_mappings.twap_source[entry_id] = twap_source;
oracle_mappings.ref_price[entry_id] = ref_price_index;

Ok(())
}
Loading

0 comments on commit c065d3a

Please sign in to comment.