Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions controller/core/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ pub mod generic;
pub mod liquidity;
pub mod protocol_fee;
pub mod rebalance;
pub mod rps;
pub mod swap;
pub mod sync_sol_value;
1 change: 1 addition & 0 deletions controller/core/src/instructions/rps/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod set_rps;
70 changes: 70 additions & 0 deletions controller/core/src/instructions/rps/set_rps.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use generic_array_struct::generic_array_struct;

use crate::instructions::internal_utils::caba;

// Accounts

#[generic_array_struct(builder pub)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct SetRpsIxAccs<T> {
/// The pool's state singleton PDA
pub pool_state: T,

/// The pool's RPS authority
pub rps_auth: T,
}

impl<T: Copy> SetRpsIxAccs<T> {
#[inline]
pub const fn memset(val: T) -> Self {
Self([val; SET_RPS_IX_ACCS_LEN])
}
}

pub type SetRpsIxKeys<'a> = SetRpsIxAccs<&'a [u8; 32]>;

pub type SetRpsIxKeysOwned = SetRpsIxAccs<[u8; 32]>;

pub type SetRpsIxAccFlags = SetRpsIxAccs<bool>;

pub const SET_RPS_IX_IS_WRITER: SetRpsIxAccFlags =
SetRpsIxAccFlags::memset(false).const_with_pool_state(true);

pub const SET_RPS_IX_IS_SIGNER: SetRpsIxAccFlags =
SetRpsIxAccFlags::memset(false).const_with_rps_auth(true);

// Data

pub const SET_RPS_IX_DISCM: u8 = 26;

pub const SET_RPS_IX_DATA_LEN: usize = 9;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct SetRpsIxData([u8; SET_RPS_IX_DATA_LEN]);

impl SetRpsIxData {
#[inline]
pub const fn new(rps: u64) -> Self {
const A: usize = SET_RPS_IX_DATA_LEN;

let mut d = [0u8; A];

d = caba::<A, 0, 1>(d, &[SET_RPS_IX_DISCM]);
d = caba::<A, 1, 8>(d, &rps.to_le_bytes());

Self(d)
}

#[inline]
pub const fn as_buf(&self) -> &[u8; SET_RPS_IX_DATA_LEN] {
&self.0
}

/// Returns `rps` arg, the new RPS to set to
#[inline]
pub const fn parse_no_discm(data: &[u8; 8]) -> u64 {
u64::from_le_bytes(*data)
}
}
1 change: 1 addition & 0 deletions controller/program/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ pub mod admin;
pub mod disable_pool;
pub mod protocol_fee;
pub mod rebalance;
pub mod rps;
pub mod swap;
pub mod sync_sol_value;
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use inf1_ctl_jiminy::{
};
use jiminy_cpi::{
account::{Abr, AccountHandle},
program_error::{ProgramError, NOT_ENOUGH_ACCOUNT_KEYS},
program_error::ProgramError,
Cpi,
};
use jiminy_sysvar_clock::Clock;
Expand All @@ -24,6 +24,7 @@ use sanctum_spl_token_jiminy::{

use crate::{
token::checked_mint_of,
utils::accs_split_first_chunk,
verify::{verify_not_rebalancing_and_not_disabled, verify_pks, verify_signers},
};

Expand All @@ -34,8 +35,8 @@ pub fn withdraw_protocol_fees_v2_checked<'acc>(
abr: &Abr,
accs: &[AccountHandle<'acc>],
) -> Result<WithdrawProtocolFeesV2IxAccounts<'acc>, ProgramError> {
let accs = accs.first_chunk().ok_or(NOT_ENOUGH_ACCOUNT_KEYS)?;
let accs = WithdrawProtocolFeesV2IxAccs(*accs);
let (ix_prefix, _) = accs_split_first_chunk(accs)?;
let accs = WithdrawProtocolFeesV2IxAccs(*ix_prefix);

let pool = pool_state_v2_checked(abr.get(*accs.pool_state()))?;
let mint_acc = abr.get(*accs.inf_mint());
Expand All @@ -62,7 +63,7 @@ pub fn withdraw_protocol_fees_v2_checked<'acc>(
pub fn process_withdraw_protocol_fees_v2(
abr: &mut Abr,
cpi: &mut Cpi,
accs: WithdrawProtocolFeesV2IxAccounts,
accs: &WithdrawProtocolFeesV2IxAccounts,
clock: &Clock,
) -> Result<(), ProgramError> {
let pool = pool_state_v2_checked_mut(abr.get_mut(*accs.pool_state()))?;
Expand Down
1 change: 1 addition & 0 deletions controller/program/src/instructions/rps/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod set_rps;
67 changes: 67 additions & 0 deletions controller/program/src/instructions/rps/set_rps.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use inf1_ctl_jiminy::{
account_utils::{pool_state_v2_checked, pool_state_v2_checked_mut},
instructions::rps::set_rps::{
NewSetRpsIxAccsBuilder, SetRpsIxAccs, SetRpsIxData, SET_RPS_IX_IS_SIGNER,
},
keys::POOL_STATE_ID,
program_err::Inf1CtlCustomProgErr,
typedefs::{rps::Rps, uq0f63::UQ0F63},
};
use jiminy_cpi::{
account::{Abr, AccountHandle},
program_error::{ProgramError, INVALID_INSTRUCTION_DATA},
};
use jiminy_sysvar_clock::Clock;

use crate::{
utils::{accs_split_first_chunk, ix_data_as_arr},
verify::{verify_not_rebalancing_and_not_disabled, verify_pks, verify_signers},
};

type SetRpsIxAccounts<'acc> = SetRpsIxAccs<AccountHandle<'acc>>;

#[inline]
pub fn set_rps_checked<'acc>(
abr: &mut Abr,
accs: &[AccountHandle<'acc>],
ix_data_no_discm: &[u8],
) -> Result<(SetRpsIxAccounts<'acc>, Rps), ProgramError> {
let (ix_prefix, _) = accs_split_first_chunk(accs)?;
let accs = SetRpsIxAccs(*ix_prefix);

let new_rps_raw = SetRpsIxData::parse_no_discm(ix_data_as_arr(ix_data_no_discm)?);

// Validate raw RPS value
let uq_rps = UQ0F63::new(new_rps_raw).map_err(|_| INVALID_INSTRUCTION_DATA)?;
let new_rps = Rps::new(uq_rps).map_err(|_| INVALID_INSTRUCTION_DATA)?;

let pool = pool_state_v2_checked(abr.get(*accs.pool_state()))?;

let expected_pks = NewSetRpsIxAccsBuilder::start()
.with_pool_state(&POOL_STATE_ID)
.with_rps_auth(&pool.rps_authority)
.build();
verify_pks(abr, &accs.0, &expected_pks.0)?;

verify_signers(abr, &accs.0, &SET_RPS_IX_IS_SIGNER.0)?;

verify_not_rebalancing_and_not_disabled(pool)?;

Ok((accs, new_rps))
}

#[inline]
pub fn process_set_rps(
abr: &mut Abr,
accs: &SetRpsIxAccounts,
new_rps: Rps,
clock: &Clock,
) -> Result<(), ProgramError> {
let pool = pool_state_v2_checked_mut(abr.get_mut(*accs.pool_state()))?;
pool.release_yield(clock.slot)
.map_err(Inf1CtlCustomProgErr)?;

pool.rps = *new_rps.as_raw();

Ok(())
}
23 changes: 16 additions & 7 deletions controller/program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use inf1_ctl_jiminy::instructions::{
set_rebal_auth::SET_REBAL_AUTH_IX_DISCM,
start::{StartRebalanceIxData, START_REBALANCE_IX_DISCM},
},
rps::set_rps::SET_RPS_IX_DISCM,
swap::{
parse_swap_ix_args,
v1::{exact_in::SWAP_EXACT_IN_IX_DISCM, exact_out::SWAP_EXACT_OUT_IX_DISCM},
Expand Down Expand Up @@ -88,6 +89,7 @@ use crate::{
set_rebal_auth::{process_set_rebal_auth, set_rebal_auth_accs_checked},
start::process_start_rebalance,
},
rps::set_rps::{process_set_rps, set_rps_checked},
swap::{
v1::{
add_liq_split_v1_accs_into_v2, conv_add_liq_args, conv_rem_liq_args,
Expand Down Expand Up @@ -281,13 +283,6 @@ fn process_ix(
let accs = set_rebal_auth_accs_checked(abr, accounts)?;
process_set_rebal_auth(abr, accs)
}
// v2 withdraw protocol fees
(&WITHDRAW_PROTOCOL_FEES_V2_IX_DISCM, _) => {
sol_log("WithdrawProtocolFeesV2");
let accs = withdraw_protocol_fees_v2_checked(abr, accounts)?;
let clock = Clock::write_to(&mut clock)?;
process_withdraw_protocol_fees_v2(abr, cpi, accs, clock)
}
// v2 swap
(&SWAP_EXACT_IN_V2_IX_DISCM, data) => {
sol_log("SwapExactInV2");
Expand All @@ -305,6 +300,20 @@ fn process_ix(
verify_swap_v2(abr, &accs, &args, clock)?;
process_swap_exact_out_v2(abr, cpi, &accs, &args, clock)
}
// v2 withdraw protocol fees
(&WITHDRAW_PROTOCOL_FEES_V2_IX_DISCM, _) => {
sol_log("WithdrawProtocolFeesV2");
let accs = withdraw_protocol_fees_v2_checked(abr, accounts)?;
let clock = Clock::write_to(&mut clock)?;
process_withdraw_protocol_fees_v2(abr, cpi, &accs, clock)
}
// v2 set rps
(&SET_RPS_IX_DISCM, data) => {
sol_log("SetRps");
let (accs, rps) = set_rps_checked(abr, accounts, data)?;
let clock = Clock::write_to(&mut clock)?;
process_set_rps(abr, &accs, rps, clock)
}
_ => Err(INVALID_INSTRUCTION_DATA.into()),
}
}
6 changes: 6 additions & 0 deletions controller/program/tests/common/pool_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::borrow::Borrow;
use inf1_ctl_jiminy::{
accounts::pool_state::PoolStateV2, sync_sol_val::SyncSolVal, typedefs::snap::NewSnapBuilder,
};
use inf1_svc_ag_core::calc::SvcCalcAg;
use inf1_svc_jiminy::traits::SolValCalc;

/// Calc, balance, sol value
Expand Down Expand Up @@ -44,3 +45,8 @@ where
pub fn assert_lp_solvent_invar(ps: &PoolStateV2) {
assert!(ps.total_sol_value >= ps.withheld_lamports + ps.protocol_fee_lamports);
}

/// Lookahead to after release_yield with no LST updates
pub fn header_lookahead_no_lsts(ps: PoolStateV2, curr_slot: u64) -> PoolStateV2 {
header_lookahead(ps, &[] as &[Cbs<SvcCalcAg>], curr_slot)
}
4 changes: 2 additions & 2 deletions controller/program/tests/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ mod admin;
mod disable_pool;
mod protocol_fee;
mod rebalance;
mod sync_sol_value;

mod rps;
mod swap;
mod sync_sol_value;
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use inf1_ctl_jiminy::{
svc::InfCalc,
typedefs::pool_sv::PoolSvLamports,
};
use inf1_svc_ag_core::{calc::SvcCalcAg, inf1_svc_lido_core::solido_legacy_core::TOKENKEG_PROGRAM};
use inf1_svc_ag_core::inf1_svc_lido_core::solido_legacy_core::TOKENKEG_PROGRAM;
use inf1_test_utils::{
acc_bef_aft, any_normal_pk, any_pool_state_v2, assert_diffs_pool_state_v2,
assert_jiminy_prog_err, assert_token_acc_diffs, keys_signer_writable_to_metas,
Expand All @@ -35,7 +35,7 @@ use sanctum_u64_ratio::Ratio;
use solana_instruction::Instruction;
use solana_pubkey::Pubkey;

use crate::common::{header_lookahead, Cbs, SVM};
use crate::common::{header_lookahead_no_lsts, SVM};
use jiminy_cpi::program_error::{ProgramError, INVALID_ARGUMENT, MISSING_REQUIRED_SIGNATURE};

const INF_MINT_ID: [u8; 32] = INF_MINT.to_bytes();
Expand All @@ -44,11 +44,6 @@ const INF_MINT_ID: [u8; 32] = INF_MINT.to_bytes();
/// when protocol_fee_lamports * inf_mint_supply
const SAFE_MUL_U64_MAX: u64 = u32::MAX as u64;

/// Lookahead to after release_yield with no LST updates
fn pool_state_header_lookahead(ps: PoolStateV2, curr_slot: u64) -> PoolStateV2 {
header_lookahead(ps, &[] as &[Cbs<SvcCalcAg>], curr_slot)
}

fn withdraw_protocol_fees_v2_ix(keys: &WithdrawProtocolFeesV2IxKeysOwned) -> Instruction {
let accounts = keys_signer_writable_to_metas(
keys.0.iter(),
Expand Down Expand Up @@ -120,8 +115,7 @@ fn withdraw_protocol_fees_v2_test(
})
};

let pool_state_bef =
pool_state_header_lookahead(pool_state_bef, svm.sysvars.clock.slot);
let pool_state_bef = header_lookahead_no_lsts(pool_state_bef, svm.sysvars.clock.slot);

let [withdraw_to_bef, withdraw_to_aft] = {
acc_bef_aft(&withdraw_to_pk, bef, &aft)
Expand Down
1 change: 1 addition & 0 deletions controller/program/tests/tests/rps/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod set_rps;
Loading