diff --git a/Cargo.lock b/Cargo.lock index 3d542da..26fa071 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -572,7 +572,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -862,7 +862,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -1235,7 +1235,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -1257,7 +1257,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -1375,7 +1375,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -1398,7 +1398,7 @@ checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -1492,7 +1492,7 @@ checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -1505,7 +1505,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -1657,7 +1657,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -1672,6 +1672,12 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + [[package]] name = "futures-util" version = "0.3.29" @@ -1743,6 +1749,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "goblin" version = "0.5.4" @@ -2469,7 +2481,7 @@ checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -2551,7 +2563,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -2563,7 +2575,7 @@ dependencies = [ "proc-macro-crate 2.0.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -2592,9 +2604,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" @@ -2744,7 +2756,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -2866,9 +2878,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -2890,7 +2902,7 @@ checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -2943,9 +2955,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -3109,6 +3121,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "relative-path" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" + [[package]] name = "reqwest" version = "0.11.22" @@ -3191,6 +3209,35 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rstest" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.48", + "unicode-ident", +] + [[package]] name = "rtoolbox" version = "0.0.2" @@ -3340,7 +3387,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -3408,7 +3455,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -3453,7 +3500,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -3942,7 +3989,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -4439,7 +4486,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -4761,6 +4808,8 @@ version = "0.1.0" dependencies = [ "anchor-lang", "anchor-spl", + "once_cell", + "rstest", "spl-stake-pool", "sunrise-core", ] @@ -4794,7 +4843,7 @@ checksum = "fadbefec4f3c678215ca72bd71862697bb06b41fd77c0088902dd3203354387b" dependencies = [ "quote", "spl-discriminator-syn 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -4804,7 +4853,7 @@ source = "git+https://github.com/solana-labs/solana-program-library#177a6c94d751 dependencies = [ "quote", "spl-discriminator-syn 0.1.1 (git+https://github.com/solana-labs/solana-program-library)", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -4816,7 +4865,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.39", + "syn 2.0.48", "thiserror", ] @@ -4828,7 +4877,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.39", + "syn 2.0.48", "thiserror", ] @@ -4921,7 +4970,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -4932,7 +4981,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -5227,9 +5276,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -5355,7 +5404,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -5366,7 +5415,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", "test-case-core", ] @@ -5387,22 +5436,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -5505,7 +5554,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -5652,7 +5701,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -5907,7 +5956,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", "wasm-bindgen-shared", ] @@ -5941,7 +5990,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6221,7 +6270,7 @@ checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] @@ -6241,7 +6290,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.48", ] [[package]] diff --git a/programs/spl-beam/Cargo.toml b/programs/spl-beam/Cargo.toml index 4f468aa..2210f9f 100644 --- a/programs/spl-beam/Cargo.toml +++ b/programs/spl-beam/Cargo.toml @@ -20,3 +20,7 @@ anchor-lang = '0.29.0' anchor-spl = '0.29.0' spl-stake-pool = { git = "https://github.com/solana-labs/solana-program-library", features = ["no-entrypoint"] } sunrise-core = { path = "../sunrise-core", features = ["cpi"] } +once_cell = "1.19.0" + +[dev-dependencies] +rstest = "0.18.2" \ No newline at end of file diff --git a/programs/spl-beam/src/cpi_interface/stake_pool.rs b/programs/spl-beam/src/cpi_interface/stake_pool.rs index bed174d..05fec22 100644 --- a/programs/spl-beam/src/cpi_interface/stake_pool.rs +++ b/programs/spl-beam/src/cpi_interface/stake_pool.rs @@ -30,3 +30,29 @@ impl Deref for StakePool { &self.0 } } + +#[cfg(test)] +mod tests { + use anchor_lang::__private::base64; + use anchor_lang::Owner; + use super::*; + + // This is a stake pool account - see packages/tests/fixtures/spl/pool.json + const BASE64_POOL_DATA: &str = "AQi2aQPmj/kyc1PszrLaqtyAYSpJobj5d6Ix+gjkmqjkCLZpA+aP+TJzU+zOstqq3IBhKkmhuPl3ojH6COSaqOR0TlK3ODVp7q8xpWvvF7+QNZz/+Qxc/JYj8YXrjzH2C/wIg0ukdM5I0b2+7xzv1QkIWnhD3KHOW51k82GiSUI9HwiQKTXm+75i8/+yT5wnONmvFKIUMPYZ6vcHRhqy8CAcCNLpcPk8ez1QGR5hGs2TqoClRrReyWXhiwWHFVaZyKy+io330TRBlc2b9EScxWN+/9wvGEkjPl5KMq8RBlm3bgbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCp1QsvlBIAAADIq5cMEgAAALoBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECcAAAAAAAD0AQAAAAAAAAAAABAnAAAAAAAACAAAAAAAAAAQJwAAAAAAAAoAAAAAAAAAAGQAECcAAAAAAAAIAAAAAAAAAGQAECcAAAAAAAADAAAAAAAAAADIq5cMEgAAANULL5QSAAAAAwAAAAAAAAEAAAAAAAAAARAnAAAAAAAAAwAAAAAAAAAJLUF3EAAAANWF8/IQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + // bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1 + const EXPECTED_POOL_MINT: Pubkey = Pubkey::new_from_array([ + 8,210,233,112,249,60,123,61,80,25,30,97,26,205,147,170,128,165,70,180,94,201,101,225,139,5,135,21,86,153,200,172 + ]); + + #[test] + fn test_deserialize_spl_stake_pool() { + let bytes = &base64::decode(BASE64_POOL_DATA).unwrap(); + let stake_pool = StakePool::try_deserialize(&mut &bytes[..]).unwrap(); + assert_eq!(stake_pool.pool_mint, EXPECTED_POOL_MINT); + } + + #[test] + fn test_owner() { + assert_eq!(StakePool::owner(), SPL_STAKE_POOL_PROGRAM_ID); + } +} diff --git a/programs/spl-beam/src/state.rs b/programs/spl-beam/src/state.rs index 65c98f4..e17fd49 100644 --- a/programs/spl-beam/src/state.rs +++ b/programs/spl-beam/src/state.rs @@ -1,6 +1,7 @@ use anchor_lang::prelude::*; #[account] +#[derive(Debug, Default)] pub struct State { /// The update authority of the state. pub update_authority: Pubkey, diff --git a/programs/spl-beam/src/utils.rs b/programs/spl-beam/src/utils.rs index 6f4dae6..f1a5ba3 100644 --- a/programs/spl-beam/src/utils.rs +++ b/programs/spl-beam/src/utils.rs @@ -40,16 +40,18 @@ pub fn get_delegated_stake_amount(stake_account: &AccountInfo) -> Result { /// This is calculated as: /// The value of the pool tokens minus the amount of SOL staked in the beam pub fn calculate_extractable_yield( - sunrise_state: &Account, + sunrise_state: &sunrise_core::State, beam_state: &Account, - stake_pool: &Account, - pool_token_vault: &Account, + stake_pool: &StakePool, + pool_token_vault: &TokenAccount, ) -> Result { // Calculate the beam's ownership of the stake pool state - let total_lamports = stake_pool.total_lamports; - let token_supply = stake_pool.pool_token_supply; - let balance = pool_token_vault.amount; - let owned_pool_value = proportional(balance, token_supply, total_lamports)?; + let total_lamports = stake_pool.total_lamports; // the total number of lamports staked in the pool + let token_supply = stake_pool.pool_token_supply; // the total number of pool tokens in existence + let balance = pool_token_vault.amount; // how many pool tokens the beam owns + let owned_pool_value = proportional(balance, total_lamports, token_supply)?; // the value in lamports of the pool tokens owned by the beam + + msg!("owned_pool_value: {}, total_lamports: {}, token_supply: {}, balance: {}", owned_pool_value, total_lamports, token_supply, balance); // Calculate the amount of SOL staked in the beam let details = sunrise_state @@ -57,5 +59,150 @@ pub fn calculate_extractable_yield( .ok_or(BeamError::UnidentifiedBeam)?; let staked_sol = details.partial_gsol_supply; + msg!("staked_sol: {}", staked_sol); + Ok(owned_pool_value.saturating_sub(staked_sol)) } + +#[cfg(test)] +mod utils_tests { + use std::cell::RefCell; + use std::rc::Rc; + use anchor_lang::__private::base64; + use anchor_lang::solana_program::program_pack::Pack; + use anchor_spl::token::spl_token; + use anchor_spl::token::spl_token::state::AccountState; + use sunrise_core::BeamDetails; + use rstest::rstest; + use super::*; + + static mut LAMPORTS_STORAGE: u64 = 0; + + fn clone_token_account_with_amount(token_account: &TokenAccount, new_amount: u64) -> Result { + let new_spl_account = spl_token::state::Account { + mint: token_account.mint, + owner: token_account.owner, + amount: new_amount, + delegate: token_account.delegate, + state: AccountState::Initialized, + is_native: token_account.is_native, + delegated_amount: token_account.delegated_amount, + close_authority: token_account.close_authority, + }; + + let mut dst = [0u8; spl_token::state::Account::LEN]; + spl_token::state::Account::pack(new_spl_account, &mut dst).unwrap(); + TokenAccount::try_deserialize_unchecked(&mut &dst[..]) + } + + pub fn create_stake_pool() -> StakePool { + // This is a stake pool account - see packages/tests/fixtures/spl/pool.json + const BASE64_POOL_DATA: &str = "AQi2aQPmj/kyc1PszrLaqtyAYSpJobj5d6Ix+gjkmqjkCLZpA+aP+TJzU+zOstqq3IBhKkmhuPl3ojH6COSaqOR0TlK3ODVp7q8xpWvvF7+QNZz/+Qxc/JYj8YXrjzH2C/wIg0ukdM5I0b2+7xzv1QkIWnhD3KHOW51k82GiSUI9HwiQKTXm+75i8/+yT5wnONmvFKIUMPYZ6vcHRhqy8CAcCNLpcPk8ez1QGR5hGs2TqoClRrReyWXhiwWHFVaZyKy+io330TRBlc2b9EScxWN+/9wvGEkjPl5KMq8RBlm3bgbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCp1QsvlBIAAADIq5cMEgAAALoBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECcAAAAAAAD0AQAAAAAAAAAAABAnAAAAAAAACAAAAAAAAAAQJwAAAAAAAAoAAAAAAAAAAGQAECcAAAAAAAAIAAAAAAAAAGQAECcAAAAAAAADAAAAAAAAAADIq5cMEgAAANULL5QSAAAAAwAAAAAAAAEAAAAAAAAAARAnAAAAAAAAAwAAAAAAAAAJLUF3EAAAANWF8/IQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + let bytes = &base64::decode(BASE64_POOL_DATA).unwrap(); + StakePool::try_deserialize(&mut &bytes[..]).unwrap() + } + + pub fn create_sunrise_state() -> sunrise_core::State { + const BASE64_SUNRISE_DATA: &str = "2JJrXmhLtrHJi086QhFaVlGIukKID4tkZHDOjVITr4RH7vx5aT2Xqc2hpwNXs05dP6XFw8EiAxG+oWnkP5J1x/Y3KJwMAngLAAAAAAAAAAD//XTkcY2+Bb9ZFikxQyz/hY58xeSkY6ATmfCxiGP0DyhBDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; + let bytes = &base64::decode(BASE64_SUNRISE_DATA).unwrap(); + sunrise_core::State::try_deserialize(&mut &bytes[..]).unwrap() + } + + pub fn create_mock_account_info<'info, T: AccountSerialize + AccountDeserialize + Clone>(data: &'info T, owner: &'info Pubkey, key: &'info Pubkey) -> AccountInfo<'info> { + // Serialize T into a byte vector + let mut data_vec = Vec::new(); + data.try_serialize(&mut data_vec).unwrap(); + // let mut data_storage = Box::new(data_vec.clone()); + // Get a mutable reference to the data (with static lifetime so that it can be returned from this function) + // (NOTE: Only do this in test code) + let static_ref: &'static mut [u8] = Box::leak(data_vec.into_boxed_slice()); + + // Create a reference counted, mutable, interior-mutable cell to the data to match the AccountInfo `data` property + let data_ref = Rc::new(RefCell::new(static_ref)); + + // Get a mutable reference to the lamports storage (with a fixed dummy value) + let lamports = unsafe { + Rc::new(RefCell::new(&mut LAMPORTS_STORAGE)) + }; + + // Create the AccountInfo + AccountInfo { + key: &key, + is_signer: false, + is_writable: false, + lamports, + data: data_ref, + owner, + executable: false, + rent_epoch: 0, + } + } + + fn create_and_register_beam_state(sunrise_state: &mut sunrise_core::State, gsol_supply: u64 ) -> Result<(State, Pubkey)> { + let beam_key = Pubkey::new_unique(); + + // add the beam to the core state + let beam_details = BeamDetails { + key: beam_key, + partial_gsol_supply: gsol_supply, + ..Default::default() + }; + sunrise_state + .allocations + .extend(std::iter::repeat(BeamDetails::default()).take(1)); + sunrise_state.add_beam(beam_details)?; + + + let beam_state = State::default(); + + Ok((beam_state, beam_key)) + } + + #[test] + fn test_proportional() { + assert_eq!(proportional(100, 1, 1).unwrap(), 100); + assert_eq!(proportional(100, 1, 2).unwrap(), 50); + assert_eq!(proportional(100, 2, 1).unwrap(), 200); + assert_eq!(proportional(100, 2, 2).unwrap(), 100); + assert_eq!(proportional(100, 0, 1).unwrap(), 0); + assert_eq!(proportional(100, 1, 0).unwrap(), 100); + assert_eq!(proportional(100, 0, 0).unwrap(), 100); + } + + #[test] + fn test_pool_tokens_from_lamports() { + let stake_pool = create_stake_pool(); + assert_eq!(pool_tokens_from_lamports(&stake_pool, 100).unwrap(), 97); + assert_eq!(pool_tokens_from_lamports(&stake_pool, 0).unwrap(), 0); + assert_eq!(pool_tokens_from_lamports(&stake_pool, 1000).unwrap(), 971); + } + + #[rstest] + // total supply is 77520677832, total lamports is 79795522517, + // so the value of one pool token is 79795522517 / 77520677832 = 1.029345 + // 0 lamports are in the beam, so there is no extractable yield + #[case::all_zeroes(0, 0, 0)] + // 48 pool tokens are worth 48 * 1.029345 = 49.4 lamports + // 50 lamports are in the beam, so the extractable yield is 0. + #[case::no_accrued_value(48, 50, 0)] + // 60 pool tokens are worth 60 * 1.029345 = 61.7607 lamports + // 50 lamports are in the beam, so the extractable yield is 61.7607 - 50 = 11.7607, rounded down to 11. + #[case::accrued_value(60, 50, 11)] + fn test_calculate_extractable_yield(#[case] pool_value: u64, #[case] issued_gsol: u64, #[case] expected_extractable_yield: u64) -> Result<()> { + let mut sunrise_state = create_sunrise_state(); + let stake_pool = create_stake_pool(); + + // create a beam and register it against the sunrise state with the given issued_gsol (the amount of sol staked in the beam) + let (beam_state, beam_key) = create_and_register_beam_state(&mut sunrise_state, issued_gsol)?; + let beam_state_account_info = create_mock_account_info(&beam_state, &crate::ID, &beam_key); + let beam_state_account = Account::try_from(&beam_state_account_info)?; + + // create a token account for the stake pool token vault with the given pool_value (the amount of pool tokens owned by the beam) + let pool_token_vault = clone_token_account_with_amount(&TokenAccount::default(), pool_value)?; + + let extractable_yield = calculate_extractable_yield(&sunrise_state, &beam_state_account, &stake_pool, &pool_token_vault).unwrap(); + assert_eq!(extractable_yield, expected_extractable_yield); + + Ok(()) + } +} \ No newline at end of file diff --git a/programs/sunrise-core/Cargo.toml b/programs/sunrise-core/Cargo.toml index 6ed4ebb..a550764 100644 --- a/programs/sunrise-core/Cargo.toml +++ b/programs/sunrise-core/Cargo.toml @@ -25,7 +25,7 @@ anchor-spl = '0.29.0' solana-program-test = { git = "https://github.com/dankelleher/solana.git", branch = "program-test-hack" } #solana-program-test = "1.17.12" solana-sdk = "1.17.12" -thiserror = "1.0.43" +thiserror = "1.0.56" [[test]] name = "sunrise-beam-integration" diff --git a/programs/sunrise-core/src/lib.rs b/programs/sunrise-core/src/lib.rs index bc3e6a6..cdbc165 100644 --- a/programs/sunrise-core/src/lib.rs +++ b/programs/sunrise-core/src/lib.rs @@ -12,7 +12,8 @@ use anchor_lang::solana_program::sysvar; use anchor_spl::token::{Mint, Token, TokenAccount}; use instructions::*; use seeds::*; -pub use state::{AllocationUpdate, EpochReport, RegisterStateInput, State, UpdateStateInput}; + +pub use state::{AllocationUpdate, EpochReport, RegisterStateInput, State, UpdateStateInput, BeamDetails}; declare_id!("suncPB4RR39bMwnRhCym6ZLKqMfnFG83vjzVVuXNhCq"); diff --git a/programs/sunrise-core/src/state.rs b/programs/sunrise-core/src/state.rs index 60269a8..2ce2f83 100644 --- a/programs/sunrise-core/src/state.rs +++ b/programs/sunrise-core/src/state.rs @@ -174,6 +174,8 @@ impl State { /// Get a shared reference to a [BeamDetails] given its key. pub fn get_beam_details(&self, key: &Pubkey) -> Option<&BeamDetails> { + msg!("get_beam_details"); + msg!("{:?}", self.allocations); self.allocations.iter().find(|x| x.key == *key) }