diff --git a/controller/core/src/internal_utils.rs b/controller/core/src/internal_utils.rs index 9eb5101e..efa95df1 100644 --- a/controller/core/src/internal_utils.rs +++ b/controller/core/src/internal_utils.rs @@ -27,7 +27,7 @@ macro_rules! impl_cast_from_acc_data { } /// # Safety - /// - `acc_data_arr` must have the same align as Self. + /// - `acc_data` must have the same align as Self. #[inline] pub const unsafe fn of_acc_data( acc_data: &[u8], @@ -55,7 +55,7 @@ macro_rules! impl_cast_from_acc_data { } /// # Safety - /// - `acc_data_arr` must have the same align as Self. + /// - `acc_data` must have the same align as Self. #[inline] pub const unsafe fn of_acc_data_mut( acc_data: &mut [u8], diff --git a/ts/sdk/src/accounts.rs b/ts/sdk/src/accounts.rs index ee3ee2e4..d6a64fda 100644 --- a/ts/sdk/src/accounts.rs +++ b/ts/sdk/src/accounts.rs @@ -1,3 +1,7 @@ +use inf1_std::inf1_ctl_core::{ + accounts::lst_state_list::LstStatePackedList, + keys::{LST_STATE_LIST_ID, POOL_STATE_ID}, +}; use wasm_bindgen::prelude::*; use crate::{ @@ -37,12 +41,57 @@ pub fn get_pool_state(inf: &Inf) -> PoolState { } } +/// Sets the `PoolState` account data +#[wasm_bindgen(js_name = setPoolState)] +pub fn set_pool_state( + inf: &mut Inf, + &PoolState { + total_sol_value, + trading_protocol_fee_bps, + lp_protocol_fee_bps, + version, + is_disabled, + is_rebalancing, + admin, + rebalance_authority, + protocol_fee_beneficiary, + pricing_program, + lp_token_mint, + }: &PoolState, +) { + inf.0.pool = inf1_std::inf1_ctl_core::accounts::pool_state::PoolState { + total_sol_value, + trading_protocol_fee_bps, + lp_protocol_fee_bps, + version, + is_disabled, + is_rebalancing, + padding: Default::default(), + admin: admin.0, + rebalance_authority: rebalance_authority.0, + protocol_fee_beneficiary: protocol_fee_beneficiary.0, + pricing_program: pricing_program.0, + lp_token_mint: lp_token_mint.0, + }; +} + /// Returns serialized `PoolState` account data #[wasm_bindgen(js_name = serPoolState)] pub fn ser_pool_state(inf: &Inf) -> Box<[u8]> { Into::into(*inf.0.pool.as_acc_data_arr()) } +/// @throws if `pool_state_data` is invalid +#[wasm_bindgen(js_name = deserPoolState)] +pub fn deser_pool_state(inf: &mut Inf, pool_state_data: Box<[u8]>) -> Result<(), InfError> { + inf.0.pool = inf1_std::inf1_ctl_core::accounts::pool_state::PoolStatePacked::of_acc_data( + &pool_state_data, + ) + .ok_or(inf1_std::err::InfErr::AccDeser { pk: POOL_STATE_ID })? + .into_pool_state(); + Ok(()) +} + /// @throws if stored lst state list account data is invalid #[wasm_bindgen(js_name = getLstStateList)] pub fn get_lst_state_list(inf: &Inf) -> Result, InfError> { @@ -72,8 +121,55 @@ pub fn get_lst_state_list(inf: &Inf) -> Result, InfError> { .collect()) } +/// Sets the `LstStateList` account data +#[wasm_bindgen(js_name = setLstStateList)] +pub fn set_lst_state_list( + inf: &mut Inf, + // Clippy complains, needed for wasm_bindgen + #[allow(clippy::boxed_local)] lst_state_list: Box<[LstState]>, +) { + inf.0.lst_state_list_data = lst_state_list + .iter() + .flat_map( + |&LstState { + is_input_disabled, + mint, + pool_reserves_bump, + protocol_fee_accumulator_bump, + sol_value, + sol_value_calculator, + }| { + *inf1_std::inf1_ctl_core::typedefs::lst_state::LstState { + is_input_disabled, + mint: mint.0, + pool_reserves_bump, + protocol_fee_accumulator_bump, + sol_value, + sol_value_calculator: sol_value_calculator.0, + padding: Default::default(), + } + .as_acc_data_arr() + }, + ) + .collect(); +} + /// Returns serialized `LstStateList` account data #[wasm_bindgen(js_name = serLstStateList)] pub fn ser_lst_state_list(inf: &Inf) -> Box<[u8]> { inf.0.lst_state_list_data.clone() } + +/// @throws if `lst_state_list_data` is invalid +#[wasm_bindgen(js_name = deserLstStateList)] +pub fn deser_lst_state_list(inf: &mut Inf, lst_state_list_data: Box<[u8]>) -> Result<(), InfError> { + LstStatePackedList::of_acc_data(&lst_state_list_data).ok_or( + inf1_std::err::InfErr::AccDeser { + pk: LST_STATE_LIST_ID, + }, + )?; + + inf.0.lst_state_list_data = lst_state_list_data; + + Ok(()) +} diff --git a/ts/tests/test/accounts.test.ts b/ts/tests/test/accounts.test.ts index 334d3deb..52a1bbcd 100644 --- a/ts/tests/test/accounts.test.ts +++ b/ts/tests/test/accounts.test.ts @@ -7,15 +7,13 @@ import { Inf, serPoolState, serLstStateList, + setPoolState, + deserPoolState, + setLstStateList, + deserLstStateList, } from "@sanctumso/inf1"; import { beforeAll, describe, expect, it } from "vitest"; -import { - fetchAccountMap, - localRpc, - LST_STATE_LIST_ID, - POOL_STATE_ID, - SPL_POOL_ACCOUNTS, -} from "../utils"; +import { fetchAccountMap, localRpc, SPL_POOL_ACCOUNTS } from "../utils"; import { type Address, type Rpc, type SolanaRpcApi } from "@solana/kit"; async function splInf(rpc: Rpc): Promise { @@ -51,16 +49,56 @@ describe("accounts test", () => { `); }); - it("round trip serPoolState", async () => { - const data = serPoolState(await splInf(rpc)); - // create a new inf, but overriding fetched account data - const initAccs = await fetchAccountMap(rpc, initPks() as Address[]); - initAccs.set(POOL_STATE_ID, { - ...initAccs.get(POOL_STATE_ID)!, - data, - }); - const rt = serPoolState(init(initAccs, SPL_POOL_ACCOUNTS)); - expect(data).toStrictEqual(rt); + it("round trip setPoolState getPoolState", async () => { + const inf = await splInf(rpc); + const pool = { + admin: "8VE2uJkoheDbJd9rCyKzfXmiMqAS4o1B3XGshEh86BGk", + isDisabled: 1, + isRebalancing: 1, + lpProtocolFeeBps: 100, + lpTokenMint: "5oVNBeEEQvYi1cX3ir8Dx5n1P7pdxydbGF2X4TxVusJm", + pricingProgram: "s1b6NRXj6ygNu1QMKXh2H9LUR2aPApAAm1UQ2DjdhNV", + protocolFeeBeneficiary: "EeQmNqm1RcQnee8LTyx6ccVG9FnR8TezQuw2JXq2LC1T", + rebalanceAuthority: "GFHMc9BegxJXLdHJrABxNVoPRdnmVxXiNeoUCEpgXVHw", + totalSolValue: 74167603073316n, + tradingProtocolFeeBps: 100, + version: 1, + }; + + setPoolState(inf, pool); + + const newPool = getPoolState(inf); + + expect(pool).toStrictEqual(newPool); + }); + + it("round trip setPoolState serPoolState deserPoolState getPoolState", async () => { + const inf = await splInf(rpc); + const pool = { + admin: "8VE2uJkoheDbJd9rCyKzfXmiMqAS4o1B3XGshEh86BGk", + isDisabled: 1, + isRebalancing: 1, + lpProtocolFeeBps: 100, + lpTokenMint: "5oVNBeEEQvYi1cX3ir8Dx5n1P7pdxydbGF2X4TxVusJm", + pricingProgram: "s1b6NRXj6ygNu1QMKXh2H9LUR2aPApAAm1UQ2DjdhNV", + protocolFeeBeneficiary: "EeQmNqm1RcQnee8LTyx6ccVG9FnR8TezQuw2JXq2LC1T", + rebalanceAuthority: "GFHMc9BegxJXLdHJrABxNVoPRdnmVxXiNeoUCEpgXVHw", + totalSolValue: 74167603073316n, + tradingProtocolFeeBps: 100, + version: 1, + }; + + setPoolState(inf, pool); + + const data = serPoolState(inf); + + const newInf = await splInf(rpc); + + deserPoolState(newInf, data); + + const newPool = getPoolState(newInf); + + expect(pool).toStrictEqual(newPool); }); it("happy path getLstStateList", async () => { @@ -106,15 +144,99 @@ describe("accounts test", () => { `); }); - it("round trip serLstStateList", async () => { - const data = serLstStateList(await splInf(rpc)); - // create a new inf, but overriding fetched account data - const initAccs = await fetchAccountMap(rpc, initPks() as Address[]); - initAccs.set(LST_STATE_LIST_ID, { - ...initAccs.get(LST_STATE_LIST_ID)!, - data, - }); - const rt = serLstStateList(init(initAccs, SPL_POOL_ACCOUNTS)); - expect(data).toStrictEqual(rt); + it("round trip setLstStateList getLstStateList", async () => { + const inf = await splInf(rpc); + + const lstStates = [ + { + isInputDisabled: 1, + mint: "7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj", + poolReservesBump: 255, + protocolFeeAccumulatorBump: 252, + solValue: 303444n, + solValueCalculator: "1idUSy4MGGKyKhvjSnGZ6Zc7Q4eKQcibym4BkEEw9KR", + }, + { + isInputDisabled: 1, + mint: "So11111111111111111111111111111111111111112", + poolReservesBump: 250, + protocolFeeAccumulatorBump: 251, + solValue: 1341445067009n, + solValueCalculator: "wsoGmxQLSvwWpuaidCApxN5kEowLe2HLQLJhCQnj4bE", + }, + { + isInputDisabled: 1, + mint: "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So", + poolReservesBump: 255, + protocolFeeAccumulatorBump: 255, + solValue: 146510n, + solValueCalculator: "mare3SCyfZkAndpBRBeonETmkCCB3TJTTrz8ZN2dnhP", + }, + { + isInputDisabled: 1, + mint: "jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v", + poolReservesBump: 255, + protocolFeeAccumulatorBump: 240, + solValue: 9802594257518n, + solValueCalculator: "ssmbu3KZxgonUtjEMCKspZzxvUQCxAFnyh1rcHUeEDo", + }, + ]; + + setLstStateList(inf, lstStates); + + let newLstStates = getLstStateList(inf); + + expect(lstStates).toStrictEqual(newLstStates); + }); + + it("round trip setLstStateList serLstStateList deserLstStateList getLstStateList", async () => { + const inf = await splInf(rpc); + + const lstStates = [ + { + isInputDisabled: 1, + mint: "7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj", + poolReservesBump: 255, + protocolFeeAccumulatorBump: 252, + solValue: 303444n, + solValueCalculator: "1idUSy4MGGKyKhvjSnGZ6Zc7Q4eKQcibym4BkEEw9KR", + }, + { + isInputDisabled: 1, + mint: "So11111111111111111111111111111111111111112", + poolReservesBump: 250, + protocolFeeAccumulatorBump: 251, + solValue: 1341445067009n, + solValueCalculator: "wsoGmxQLSvwWpuaidCApxN5kEowLe2HLQLJhCQnj4bE", + }, + { + isInputDisabled: 1, + mint: "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So", + poolReservesBump: 255, + protocolFeeAccumulatorBump: 255, + solValue: 146510n, + solValueCalculator: "mare3SCyfZkAndpBRBeonETmkCCB3TJTTrz8ZN2dnhP", + }, + { + isInputDisabled: 1, + mint: "jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v", + poolReservesBump: 255, + protocolFeeAccumulatorBump: 240, + solValue: 9802594257518n, + solValueCalculator: "ssmbu3KZxgonUtjEMCKspZzxvUQCxAFnyh1rcHUeEDo", + }, + ]; + + setLstStateList(inf, lstStates); + + const lstStateData = serLstStateList(inf); + + const newInf = await splInf(rpc); + + deserLstStateList(newInf, lstStateData); + + const newLstStates = getLstStateList(newInf); + + expect(lstStates).toStrictEqual(newLstStates); }); });