From 91acc9e66fd41546a31449746f4c29b097aa8c73 Mon Sep 17 00:00:00 2001 From: rain Date: Thu, 24 Oct 2024 18:16:24 +0800 Subject: [PATCH] Test: add layout test check Accounts with [repr(packed)] Due to the changes in Rust 1.80.0, where the fields in struct marked with [repr(packed)] might be reordered. There is necessary to add tests to confirm whether this change will impact the layout of these accounts. More details about the Rust changes at: https://github.com/rust-lang/rust/pull/125360. --- Cargo.lock | 34 +- Cargo.toml | 6 +- programs/amm/Cargo.toml | 6 +- programs/amm/src/states/operation_account.rs | 51 +++ programs/amm/src/states/oracle.rs | 114 +++++++ programs/amm/src/states/pool.rs | 318 ++++++++++++++++++ programs/amm/src/states/tick_array.rs | 123 +++++++ .../src/states/tickarray_bitmap_extension.rs | 69 ++++ 8 files changed, 706 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c38ff69..c1c03411 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -890,9 +890,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" dependencies = [ "bytemuck_derive", ] @@ -1399,9 +1399,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] [[package]] name = "derivation-path" @@ -2614,6 +2617,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.3.3" @@ -2986,6 +2995,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -5528,12 +5543,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.29" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "num-conv", + "powerfmt", "serde", "time-core", "time-macros", @@ -5547,10 +5564,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] diff --git a/Cargo.toml b/Cargo.toml index 1205bf3a..73403210 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,6 @@ [workspace] -members = [ - "programs/*", - "client", -] +resolver = "2" +members = ["programs/*", "client"] [profile.test] opt-level = 0 diff --git a/programs/amm/Cargo.toml b/programs/amm/Cargo.toml index ae346b5d..c9b17fa4 100644 --- a/programs/amm/Cargo.toml +++ b/programs/amm/Cargo.toml @@ -24,12 +24,12 @@ paramset = [] [dependencies] anchor-lang = { version = "0.29.0", features = ["init-if-needed"] } -anchor-spl = {version = "0.29.0", features = ["metadata"]} +anchor-spl = { version = "0.29.0", features = ["metadata"] } solana-program = "<1.17.0" spl-memo = "4.0.0" uint = { git = "https://github.com/raydium-io/parity-common", package = "uint" } mpl-token-metadata = { version = "^1.11.0", features = ["no-entrypoint"] } -bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"] } +bytemuck = { version = "1.19.0", features = ["derive", "min_const_generics"] } arrayref = { version = "0.3.6" } solana-security-txt = "1.1.1" @@ -46,4 +46,4 @@ overflow-checks = true [profile.release.build-override] opt-level = 3 incremental = false -codegen-units = 1 \ No newline at end of file +codegen-units = 1 diff --git a/programs/amm/src/states/operation_account.rs b/programs/amm/src/states/operation_account.rs index 9947dc76..5dfe2361 100644 --- a/programs/amm/src/states/operation_account.rs +++ b/programs/amm/src/states/operation_account.rs @@ -212,4 +212,55 @@ mod test { operation_state.remove_operation_owner(keys.clone()); println!("{:?}", operation_state.operation_owners); } + + #[test] + fn operation_layout_test() { + use anchor_lang::Discriminator; + + let bump: u8 = 0x12; + let operation_owners: [Pubkey; OPERATION_SIZE_USIZE] = + std::array::from_fn(|_| Pubkey::new_unique()); + let whitelist_mints: [Pubkey; WHITE_MINT_SIZE_USIZE] = + std::array::from_fn(|_| Pubkey::new_unique()); + + // serialize original data + let mut operation_data = + [0u8; 8 + 1 + 32 * OPERATION_SIZE_USIZE + 32 * WHITE_MINT_SIZE_USIZE]; + let mut offset = 0; + operation_data[offset..offset + 8].copy_from_slice(&OperationState::discriminator()); + offset += 8; + operation_data[offset..offset + 1].copy_from_slice(&bump.to_le_bytes()); + offset += 1; + for i in 0..OPERATION_SIZE_USIZE { + operation_data[offset..offset + 32].copy_from_slice(&operation_owners[i].to_bytes()); + offset += 32; + } + for i in 0..WHITE_MINT_SIZE_USIZE { + operation_data[offset..offset + 32].copy_from_slice(&whitelist_mints[i].to_bytes()); + offset += 32; + } + + // len check + assert_eq!(offset, operation_data.len()); + assert_eq!( + operation_data.len(), + core::mem::size_of::() + 8 + ); + + // deserialize original data + let unpack_data: &OperationState = + bytemuck::from_bytes(&operation_data[8..core::mem::size_of::() + 8]); + + // data check + let unpack_bump = unpack_data.bump; + assert_eq!(unpack_bump, bump); + for i in 0..OPERATION_SIZE_USIZE { + let unpack_operation_owners = unpack_data.operation_owners[i]; + assert_eq!(unpack_operation_owners, operation_owners[i]); + } + for i in 0..WHITE_MINT_SIZE_USIZE { + let unpack_whitelist_mints = unpack_data.whitelist_mints[i]; + assert_eq!(unpack_whitelist_mints, whitelist_mints[i]); + } + } } diff --git a/programs/amm/src/states/oracle.rs b/programs/amm/src/states/oracle.rs index 3198e57a..03aea8ba 100644 --- a/programs/amm/src/states/oracle.rs +++ b/programs/amm/src/states/oracle.rs @@ -121,3 +121,117 @@ pub fn block_timestamp_mock() -> u64 { .unwrap() .as_secs() } + +#[cfg(test)] +pub mod oracle_layout_test { + use super::*; + use anchor_lang::Discriminator; + #[test] + fn test_observation_layout() { + let initialized = true; + let recent_epoch: u64 = 0x123456789abcdef0; + let observation_index: u16 = 0x1122; + let pool_id: Pubkey = Pubkey::new_unique(); + let padding: [u64; 4] = [ + 0x123456789abcde0f, + 0x123456789abcd0ef, + 0x123456789abc0def, + 0x123456789ab0cdef, + ]; + + let mut observation_datas = [0u8; Observation::LEN * OBSERVATION_NUM]; + let mut observations = [Observation::default(); OBSERVATION_NUM]; + let mut offset = 0; + for i in 0..OBSERVATION_NUM { + let index = i + 1; + let block_timestamp: u32 = u32::MAX - 3 * index as u32; + let tick_cumulative: i64 = i64::MAX - 3 * index as i64; + let padding: [u64; 4] = [ + u64::MAX - index as u64, + u64::MAX - 2 * index as u64, + u64::MAX - 3 * index as u64, + u64::MAX - 4 * index as u64, + ]; + observations[i].block_timestamp = block_timestamp; + observations[i].tick_cumulative = tick_cumulative; + observations[i].padding = padding; + observation_datas[offset..offset + 4].copy_from_slice(&block_timestamp.to_le_bytes()); + offset += 4; + observation_datas[offset..offset + 8].copy_from_slice(&tick_cumulative.to_le_bytes()); + offset += 8; + observation_datas[offset..offset + 8].copy_from_slice(&padding[0].to_le_bytes()); + offset += 8; + observation_datas[offset..offset + 8].copy_from_slice(&padding[1].to_le_bytes()); + offset += 8; + observation_datas[offset..offset + 8].copy_from_slice(&padding[2].to_le_bytes()); + offset += 8; + observation_datas[offset..offset + 8].copy_from_slice(&padding[3].to_le_bytes()); + offset += 8; + } + + // serialize original data + let mut observation_state_data = [0u8; ObservationState::LEN]; + let mut offset = 0; + observation_state_data[offset..offset + 8] + .copy_from_slice(&ObservationState::discriminator()); + offset += 8; + observation_state_data[offset..offset + 1] + .copy_from_slice(&(initialized as u8).to_le_bytes()); + offset += 1; + observation_state_data[offset..offset + 8].copy_from_slice(&recent_epoch.to_le_bytes()); + offset += 8; + observation_state_data[offset..offset + 2] + .copy_from_slice(&observation_index.to_le_bytes()); + offset += 2; + observation_state_data[offset..offset + 32].copy_from_slice(&pool_id.to_bytes()); + offset += 32; + observation_state_data[offset..offset + Observation::LEN * OBSERVATION_NUM] + .copy_from_slice(&observation_datas); + offset += Observation::LEN * OBSERVATION_NUM; + observation_state_data[offset..offset + 8].copy_from_slice(&padding[0].to_le_bytes()); + offset += 8; + observation_state_data[offset..offset + 8].copy_from_slice(&padding[1].to_le_bytes()); + offset += 8; + observation_state_data[offset..offset + 8].copy_from_slice(&padding[2].to_le_bytes()); + offset += 8; + observation_state_data[offset..offset + 8].copy_from_slice(&padding[3].to_le_bytes()); + offset += 8; + // len check + assert_eq!(offset, observation_state_data.len()); + assert_eq!( + observation_state_data.len(), + core::mem::size_of::() + 8 + ); + + // deserialize original data + let unpack_data: &ObservationState = bytemuck::from_bytes( + &observation_state_data[8..core::mem::size_of::() + 8], + ); + + // data check + let unpack_initialized = unpack_data.initialized; + assert_eq!(unpack_initialized, initialized); + let unpack_recent_epoch = unpack_data.recent_epoch; + assert_eq!(unpack_recent_epoch, recent_epoch); + let unpack_observation_index = unpack_data.observation_index; + assert_eq!(unpack_observation_index, observation_index); + let unpack_pool_id = unpack_data.pool_id; + assert_eq!(unpack_pool_id, pool_id); + let unpack_padding = unpack_data.padding; + assert_eq!(unpack_padding, padding); + for (observation, unpack_observation) in + observations.iter().zip(unpack_data.observations.iter()) + { + let block_timestamp = observation.block_timestamp; + let tick_cumulative = observation.tick_cumulative; + let padding = observation.padding; + + let unpack_block_timestamp = unpack_observation.block_timestamp; + let unpack_tick_cumulative = unpack_observation.tick_cumulative; + let unpack_padding = unpack_observation.padding; + assert_eq!(block_timestamp, unpack_block_timestamp); + assert_eq!(tick_cumulative, unpack_tick_cumulative); + assert_eq!(padding, unpack_padding); + } + } +} diff --git a/programs/amm/src/states/pool.rs b/programs/amm/src/states/pool.rs index c63e43b6..181d3670 100644 --- a/programs/amm/src/states/pool.rs +++ b/programs/amm/src/states/pool.rs @@ -1561,4 +1561,322 @@ pub mod pool_test { } } } + + mod pool_layout_test { + use super::*; + use anchor_lang::Discriminator; + #[test] + fn test_pool_layout() { + let bump: u8 = 0x12; + let amm_config = Pubkey::new_unique(); + let owner = Pubkey::new_unique(); + let token_mint_0 = Pubkey::new_unique(); + let token_mint_1 = Pubkey::new_unique(); + let token_vault_0 = Pubkey::new_unique(); + let token_vault_1 = Pubkey::new_unique(); + let observation_key = Pubkey::new_unique(); + let mint_decimals_0: u8 = 0x13; + let mint_decimals_1: u8 = 0x14; + let tick_spacing: u16 = 0x1516; + let liquidity: u128 = 0x11002233445566778899aabbccddeeff; + let sqrt_price_x64: u128 = 0x11220033445566778899aabbccddeeff; + let tick_current: i32 = 0x12345678; + let padding3: u16 = 0x1718; + let padding4: u16 = 0x191a; + let fee_growth_global_0_x64: u128 = 0x11223300445566778899aabbccddeeff; + let fee_growth_global_1_x64: u128 = 0x11223344005566778899aabbccddeeff; + let protocol_fees_token_0: u64 = 0x123456789abcdef0; + let protocol_fees_token_1: u64 = 0x123456789abcde0f; + let swap_in_amount_token_0: u128 = 0x11223344550066778899aabbccddeeff; + let swap_out_amount_token_1: u128 = 0x11223344556600778899aabbccddeeff; + let swap_in_amount_token_1: u128 = 0x11223344556677008899aabbccddeeff; + let swap_out_amount_token_0: u128 = 0x11223344556677880099aabbccddeeff; + let status: u8 = 0x1b; + let padding: [u8; 7] = [0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]; + // RewardInfo + let reward_state: u8 = 0x1c; + let open_time: u64 = 0x123456789abc0def; + let end_time: u64 = 0x123456789ab0cdef; + let last_update_time: u64 = 0x123456789a0bcdef; + let emissions_per_second_x64: u128 = 0x11223344556677889900aabbccddeeff; + let reward_total_emissioned: u64 = 0x1234567890abcdef; + let reward_claimed: u64 = 0x1234567809abcdef; + let token_mint = Pubkey::new_unique(); + let token_vault = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let reward_growth_global_x64: u128 = 0x112233445566778899aa00bbccddeeff; + let mut reward_info_data = [0u8; RewardInfo::LEN]; + + let mut offset = 0; + reward_info_data[offset..offset + 1].copy_from_slice(&reward_state.to_le_bytes()); + offset += 1; + reward_info_data[offset..offset + 8].copy_from_slice(&open_time.to_le_bytes()); + offset += 8; + reward_info_data[offset..offset + 8].copy_from_slice(&end_time.to_le_bytes()); + offset += 8; + reward_info_data[offset..offset + 8].copy_from_slice(&last_update_time.to_le_bytes()); + offset += 8; + reward_info_data[offset..offset + 16] + .copy_from_slice(&emissions_per_second_x64.to_le_bytes()); + offset += 16; + reward_info_data[offset..offset + 8] + .copy_from_slice(&reward_total_emissioned.to_le_bytes()); + offset += 8; + reward_info_data[offset..offset + 8].copy_from_slice(&reward_claimed.to_le_bytes()); + offset += 8; + reward_info_data[offset..offset + 32].copy_from_slice(&token_mint.to_bytes()); + offset += 32; + reward_info_data[offset..offset + 32].copy_from_slice(&token_vault.to_bytes()); + offset += 32; + reward_info_data[offset..offset + 32].copy_from_slice(&authority.to_bytes()); + offset += 32; + reward_info_data[offset..offset + 16] + .copy_from_slice(&reward_growth_global_x64.to_le_bytes()); + let mut reward_info_datas = [0u8; RewardInfo::LEN * REWARD_NUM]; + let mut offset = 0; + for _ in 0..REWARD_NUM { + reward_info_datas[offset..offset + RewardInfo::LEN] + .copy_from_slice(&reward_info_data); + offset += RewardInfo::LEN; + } + assert_eq!(offset, reward_info_datas.len()); + assert_eq!( + reward_info_datas.len(), + core::mem::size_of::() * 3 + ); + + // tick_array_bitmap + let mut tick_array_bitmap: [u64; 16] = [0u64; 16]; + let mut tick_array_bitmap_data = [0u8; 8 * 16]; + let mut offset = 0; + for i in 0..16 { + tick_array_bitmap[i] = u64::MAX << i; + tick_array_bitmap_data[offset..offset + 8] + .copy_from_slice(&tick_array_bitmap[i].to_le_bytes()); + offset += 8; + } + let total_fees_token_0: u64 = 0x1234567809abcdef; + let total_fees_token_1: u64 = 0x1234567089abcdef; + let total_fees_claimed_token_0: u64 = 0x1234560789abcdef; + let total_fees_claimed_token_1: u64 = 0x1234506789abcdef; + let fund_fees_token_0: u64 = 0x1234056789abcdef; + let fund_fees_token_1: u64 = 0x1230456789abcdef; + let pool_open_time: u64 = 0x1203456789abcdef; + let recent_epoch: u64 = 0x1023456789abcdef; + let mut padding1: [u64; 24] = [0u64; 24]; + let mut padding1_data = [0u8; 8 * 24]; + let mut offset = 0; + for i in 0..24 { + padding1[i] = u64::MAX - i as u64; + padding1_data[offset..offset + 8].copy_from_slice(&padding1[i].to_le_bytes()); + offset += 8; + } + let mut padding2: [u64; 32] = [0u64; 32]; + let mut padding2_data = [0u8; 8 * 32]; + let mut offset = 0; + for i in 24..(24 + 32) { + padding2[i - 24] = u64::MAX - i as u64; + padding2_data[offset..offset + 8].copy_from_slice(&padding2[i - 24].to_le_bytes()); + offset += 8; + } + // serialize original data + let mut pool_data = [0u8; PoolState::LEN]; + let mut offset = 0; + pool_data[offset..offset + 8].copy_from_slice(&PoolState::discriminator()); + offset += 8; + pool_data[offset..offset + 1].copy_from_slice(&bump.to_le_bytes()); + offset += 1; + pool_data[offset..offset + 32].copy_from_slice(&amm_config.to_bytes()); + offset += 32; + pool_data[offset..offset + 32].copy_from_slice(&owner.to_bytes()); + offset += 32; + pool_data[offset..offset + 32].copy_from_slice(&token_mint_0.to_bytes()); + offset += 32; + pool_data[offset..offset + 32].copy_from_slice(&token_mint_1.to_bytes()); + offset += 32; + pool_data[offset..offset + 32].copy_from_slice(&token_vault_0.to_bytes()); + offset += 32; + pool_data[offset..offset + 32].copy_from_slice(&token_vault_1.to_bytes()); + offset += 32; + pool_data[offset..offset + 32].copy_from_slice(&observation_key.to_bytes()); + offset += 32; + pool_data[offset..offset + 1].copy_from_slice(&mint_decimals_0.to_le_bytes()); + offset += 1; + pool_data[offset..offset + 1].copy_from_slice(&mint_decimals_1.to_le_bytes()); + offset += 1; + pool_data[offset..offset + 2].copy_from_slice(&tick_spacing.to_le_bytes()); + offset += 2; + pool_data[offset..offset + 16].copy_from_slice(&liquidity.to_le_bytes()); + offset += 16; + pool_data[offset..offset + 16].copy_from_slice(&sqrt_price_x64.to_le_bytes()); + offset += 16; + pool_data[offset..offset + 4].copy_from_slice(&tick_current.to_le_bytes()); + offset += 4; + pool_data[offset..offset + 2].copy_from_slice(&padding3.to_le_bytes()); + offset += 2; + pool_data[offset..offset + 2].copy_from_slice(&padding4.to_le_bytes()); + offset += 2; + pool_data[offset..offset + 16].copy_from_slice(&fee_growth_global_0_x64.to_le_bytes()); + offset += 16; + pool_data[offset..offset + 16].copy_from_slice(&fee_growth_global_1_x64.to_le_bytes()); + offset += 16; + pool_data[offset..offset + 8].copy_from_slice(&protocol_fees_token_0.to_le_bytes()); + offset += 8; + pool_data[offset..offset + 8].copy_from_slice(&protocol_fees_token_1.to_le_bytes()); + offset += 8; + pool_data[offset..offset + 16].copy_from_slice(&swap_in_amount_token_0.to_le_bytes()); + offset += 16; + pool_data[offset..offset + 16].copy_from_slice(&swap_out_amount_token_1.to_le_bytes()); + offset += 16; + pool_data[offset..offset + 16].copy_from_slice(&swap_in_amount_token_1.to_le_bytes()); + offset += 16; + pool_data[offset..offset + 16].copy_from_slice(&swap_out_amount_token_0.to_le_bytes()); + offset += 16; + pool_data[offset..offset + 1].copy_from_slice(&status.to_le_bytes()); + offset += 1; + pool_data[offset..offset + 7].copy_from_slice(&padding); + offset += 7; + pool_data[offset..offset + RewardInfo::LEN * REWARD_NUM] + .copy_from_slice(&reward_info_datas); + offset += RewardInfo::LEN * REWARD_NUM; + pool_data[offset..offset + 8 * 16].copy_from_slice(&tick_array_bitmap_data); + offset += 8 * 16; + pool_data[offset..offset + 8].copy_from_slice(&total_fees_token_0.to_le_bytes()); + offset += 8; + pool_data[offset..offset + 8] + .copy_from_slice(&total_fees_claimed_token_0.to_le_bytes()); + offset += 8; + pool_data[offset..offset + 8].copy_from_slice(&total_fees_token_1.to_le_bytes()); + offset += 8; + pool_data[offset..offset + 8] + .copy_from_slice(&total_fees_claimed_token_1.to_le_bytes()); + offset += 8; + pool_data[offset..offset + 8].copy_from_slice(&fund_fees_token_0.to_le_bytes()); + offset += 8; + pool_data[offset..offset + 8].copy_from_slice(&fund_fees_token_1.to_le_bytes()); + offset += 8; + pool_data[offset..offset + 8].copy_from_slice(&pool_open_time.to_le_bytes()); + offset += 8; + pool_data[offset..offset + 8].copy_from_slice(&recent_epoch.to_le_bytes()); + offset += 8; + pool_data[offset..offset + 8 * 24].copy_from_slice(&padding1_data); + offset += 8 * 24; + pool_data[offset..offset + 8 * 32].copy_from_slice(&padding2_data); + offset += 8 * 32; + + // len check + assert_eq!(offset, pool_data.len()); + assert_eq!(pool_data.len(), core::mem::size_of::() + 8); + + // deserialize original data + let unpack_data: &PoolState = + bytemuck::from_bytes(&pool_data[8..core::mem::size_of::() + 8]); + + // data check + let unpack_bump = unpack_data.bump[0]; + assert_eq!(unpack_bump, bump); + let unpack_amm_config = unpack_data.amm_config; + assert_eq!(unpack_amm_config, amm_config); + let unpack_owner = unpack_data.owner; + assert_eq!(unpack_owner, owner); + let unpack_token_mint_0 = unpack_data.token_mint_0; + assert_eq!(unpack_token_mint_0, token_mint_0); + let unpack_token_mint_1 = unpack_data.token_mint_1; + assert_eq!(unpack_token_mint_1, token_mint_1); + let unpack_token_vault_0 = unpack_data.token_vault_0; + assert_eq!(unpack_token_vault_0, token_vault_0); + let unpack_token_vault_1 = unpack_data.token_vault_1; + assert_eq!(unpack_token_vault_1, token_vault_1); + let unpack_observation_key = unpack_data.observation_key; + assert_eq!(unpack_observation_key, observation_key); + let unpack_mint_decimals_0 = unpack_data.mint_decimals_0; + assert_eq!(unpack_mint_decimals_0, mint_decimals_0); + let unpack_mint_decimals_1 = unpack_data.mint_decimals_1; + assert_eq!(unpack_mint_decimals_1, mint_decimals_1); + let unpack_tick_spacing = unpack_data.tick_spacing; + assert_eq!(unpack_tick_spacing, tick_spacing); + let unpack_liquidity = unpack_data.liquidity; + assert_eq!(unpack_liquidity, liquidity); + let unpack_sqrt_price_x64 = unpack_data.sqrt_price_x64; + assert_eq!(unpack_sqrt_price_x64, sqrt_price_x64); + let unpack_tick_current = unpack_data.tick_current; + assert_eq!(unpack_tick_current, tick_current); + let unpack_padding3 = unpack_data.padding3; + assert_eq!(unpack_padding3, padding3); + let unpack_padding4 = unpack_data.padding4; + assert_eq!(unpack_padding4, padding4); + let unpack_fee_growth_global_0_x64 = unpack_data.fee_growth_global_0_x64; + assert_eq!(unpack_fee_growth_global_0_x64, fee_growth_global_0_x64); + let unpack_fee_growth_global_1_x64 = unpack_data.fee_growth_global_1_x64; + assert_eq!(unpack_fee_growth_global_1_x64, fee_growth_global_1_x64); + let unpack_protocol_fees_token_0 = unpack_data.protocol_fees_token_0; + assert_eq!(unpack_protocol_fees_token_0, protocol_fees_token_0); + let unpack_protocol_fees_token_1 = unpack_data.protocol_fees_token_1; + assert_eq!(unpack_protocol_fees_token_1, protocol_fees_token_1); + let unpack_swap_in_amount_token_0 = unpack_data.swap_in_amount_token_0; + assert_eq!(unpack_swap_in_amount_token_0, swap_in_amount_token_0); + let unpack_swap_out_amount_token_1 = unpack_data.swap_out_amount_token_1; + assert_eq!(unpack_swap_out_amount_token_1, swap_out_amount_token_1); + let unpack_swap_in_amount_token_1 = unpack_data.swap_in_amount_token_1; + assert_eq!(unpack_swap_in_amount_token_1, swap_in_amount_token_1); + let unpack_swap_out_amount_token_0 = unpack_data.swap_out_amount_token_0; + assert_eq!(unpack_swap_out_amount_token_0, swap_out_amount_token_0); + let unpack_status = unpack_data.status; + assert_eq!(unpack_status, status); + let unpack_padding = unpack_data.padding; + assert_eq!(unpack_padding, padding); + + for reward in unpack_data.reward_infos { + let unpack_reward_state = reward.reward_state; + assert_eq!(unpack_reward_state, reward_state); + let unpack_open_time = reward.open_time; + assert_eq!(unpack_open_time, open_time); + let unpack_end_time = reward.end_time; + assert_eq!(unpack_end_time, end_time); + let unpack_last_update_time = reward.last_update_time; + assert_eq!(unpack_last_update_time, last_update_time); + let unpack_emissions_per_second_x64 = reward.emissions_per_second_x64; + assert_eq!(unpack_emissions_per_second_x64, emissions_per_second_x64); + let unpack_reward_total_emissioned = reward.reward_total_emissioned; + assert_eq!(unpack_reward_total_emissioned, reward_total_emissioned); + let unpack_reward_claimed = reward.reward_claimed; + assert_eq!(unpack_reward_claimed, reward_claimed); + let unpack_token_mint = reward.token_mint; + assert_eq!(unpack_token_mint, token_mint); + let unpack_token_vault = reward.token_vault; + assert_eq!(unpack_token_vault, token_vault); + let unpack_authority = reward.authority; + assert_eq!(unpack_authority, authority); + let unpack_reward_growth_global_x64 = reward.reward_growth_global_x64; + assert_eq!(unpack_reward_growth_global_x64, reward_growth_global_x64); + } + + let unpack_tick_array_bitmap = unpack_data.tick_array_bitmap; + assert_eq!(unpack_tick_array_bitmap, tick_array_bitmap); + let unpack_total_fees_token_0 = unpack_data.total_fees_token_0; + assert_eq!(unpack_total_fees_token_0, total_fees_token_0); + let unpack_total_fees_claimed_token_0 = unpack_data.total_fees_claimed_token_0; + assert_eq!( + unpack_total_fees_claimed_token_0, + total_fees_claimed_token_0 + ); + let unpack_total_fees_claimed_token_1 = unpack_data.total_fees_claimed_token_1; + assert_eq!( + unpack_total_fees_claimed_token_1, + total_fees_claimed_token_1 + ); + let unpack_fund_fees_token_0 = unpack_data.fund_fees_token_0; + assert_eq!(unpack_fund_fees_token_0, fund_fees_token_0); + let unpack_fund_fees_token_1 = unpack_data.fund_fees_token_1; + assert_eq!(unpack_fund_fees_token_1, fund_fees_token_1); + let unpack_open_time = unpack_data.open_time; + assert_eq!(unpack_open_time, pool_open_time); + let unpack_recent_epoch = unpack_data.recent_epoch; + assert_eq!(unpack_recent_epoch, recent_epoch); + let unpack_padding1 = unpack_data.padding1; + assert_eq!(unpack_padding1, padding1); + let unpack_padding2 = unpack_data.padding2; + assert_eq!(unpack_padding2, padding2); + } + } } diff --git a/programs/amm/src/states/tick_array.rs b/programs/amm/src/states/tick_array.rs index ebc9a28c..18495cbf 100644 --- a/programs/amm/src/states/tick_array.rs +++ b/programs/amm/src/states/tick_array.rs @@ -1307,4 +1307,127 @@ pub mod tick_array_test { assert_eq!(reward_frowth_inside_delta, 500); } } + mod tick_array_layout_test { + use super::*; + use anchor_lang::Discriminator; + #[test] + fn test_tick_array_layout() { + let pool_id = Pubkey::new_unique(); + let start_tick_index: i32 = 0x12345678; + let initialized_tick_count: u8 = 0x12; + let recent_epoch: u64 = 0x123456789abcdef0; + let mut padding: [u8; 107] = [0u8; 107]; + let mut padding_data = [0u8; 107]; + for i in 0..107 { + padding[i] = i as u8; + padding_data[i] = i as u8; + } + + let tick: i32 = 0x12345678; + let liquidity_net: i128 = 0x11002233445566778899aabbccddeeff; + let liquidity_gross: u128 = 0x11220033445566778899aabbccddeeff; + let fee_growth_outside_0_x64: u128 = 0x11223300445566778899aabbccddeeff; + let fee_growth_outside_1_x64: u128 = 0x11223344005566778899aabbccddeeff; + let reward_growths_outside_x64: [u128; REWARD_NUM] = [ + 0x11223344550066778899aabbccddeeff, + 0x11223344556600778899aabbccddeeff, + 0x11223344556677008899aabbccddeeff, + ]; + let mut tick_padding: [u32; 13] = [0u32; 13]; + let mut tick_padding_data = [0u8; 4 * 13]; + let mut offset = 0; + for i in 0..13 { + tick_padding[i] = u32::MAX - 3 * i as u32; + tick_padding_data[offset..offset + 4] + .copy_from_slice(&tick_padding[i].to_le_bytes()); + offset += 4; + } + + let mut tick_data = [0u8; TickState::LEN]; + let mut offset = 0; + tick_data[offset..offset + 4].copy_from_slice(&tick.to_le_bytes()); + offset += 4; + tick_data[offset..offset + 16].copy_from_slice(&liquidity_net.to_le_bytes()); + offset += 16; + tick_data[offset..offset + 16].copy_from_slice(&liquidity_gross.to_le_bytes()); + offset += 16; + tick_data[offset..offset + 16].copy_from_slice(&fee_growth_outside_0_x64.to_le_bytes()); + offset += 16; + tick_data[offset..offset + 16].copy_from_slice(&fee_growth_outside_1_x64.to_le_bytes()); + offset += 16; + for i in 0..REWARD_NUM { + tick_data[offset..offset + 16] + .copy_from_slice(&reward_growths_outside_x64[i].to_le_bytes()); + offset += 16; + } + tick_data[offset..offset + 4 * 13].copy_from_slice(&tick_padding_data); + offset += 4 * 13; + assert_eq!(offset, tick_data.len()); + assert_eq!(tick_data.len(), core::mem::size_of::()); + + // serialize original data + let mut tick_array_data = [0u8; TickArrayState::LEN]; + let mut offset = 0; + tick_array_data[offset..offset + 8].copy_from_slice(&TickArrayState::discriminator()); + offset += 8; + tick_array_data[offset..offset + 32].copy_from_slice(&pool_id.to_bytes()); + offset += 32; + tick_array_data[offset..offset + 4].copy_from_slice(&start_tick_index.to_le_bytes()); + offset += 4; + for _ in 0..TICK_ARRAY_SIZE_USIZE { + tick_array_data[offset..offset + TickState::LEN].copy_from_slice(&tick_data); + offset += TickState::LEN; + } + tick_array_data[offset..offset + 1] + .copy_from_slice(&initialized_tick_count.to_le_bytes()); + offset += 1; + tick_array_data[offset..offset + 8].copy_from_slice(&recent_epoch.to_le_bytes()); + offset += 8; + tick_array_data[offset..offset + 107].copy_from_slice(&padding); + offset += 107; + + // len check + assert_eq!(offset, tick_array_data.len()); + assert_eq!( + tick_array_data.len(), + core::mem::size_of::() + 8 + ); + + // deserialize original data + let unpack_data: &TickArrayState = bytemuck::from_bytes( + &tick_array_data[8..core::mem::size_of::() + 8], + ); + + // data check + let unpack_pool_id = unpack_data.pool_id; + assert_eq!(unpack_pool_id, pool_id); + let unpack_start_tick_index = unpack_data.start_tick_index; + assert_eq!(unpack_start_tick_index, start_tick_index); + for tick_item in unpack_data.ticks { + let unpack_tick = tick_item.tick; + assert_eq!(unpack_tick, tick); + let unpack_liquidity_net = tick_item.liquidity_net; + assert_eq!(unpack_liquidity_net, liquidity_net); + let unpack_liquidity_gross = tick_item.liquidity_gross; + assert_eq!(unpack_liquidity_gross, liquidity_gross); + let unpack_fee_growth_outside_0_x64 = tick_item.fee_growth_outside_0_x64; + assert_eq!(unpack_fee_growth_outside_0_x64, fee_growth_outside_0_x64); + let unpack_fee_growth_outside_1_x64 = tick_item.fee_growth_outside_1_x64; + assert_eq!(unpack_fee_growth_outside_1_x64, fee_growth_outside_1_x64); + let unpack_reward_growths_outside_x64 = tick_item.reward_growths_outside_x64; + assert_eq!( + unpack_reward_growths_outside_x64, + reward_growths_outside_x64 + ); + let unpack_tick_padding = tick_item.padding; + assert_eq!(unpack_tick_padding, tick_padding); + } + let unpack_initialized_tick_count = unpack_data.initialized_tick_count; + assert_eq!(unpack_initialized_tick_count, initialized_tick_count); + let unpack_recent_epoch = unpack_data.recent_epoch; + assert_eq!(unpack_recent_epoch, recent_epoch); + let unpack_padding = unpack_data.padding; + assert_eq!(padding, unpack_padding); + } + } } diff --git a/programs/amm/src/states/tickarray_bitmap_extension.rs b/programs/amm/src/states/tickarray_bitmap_extension.rs index 79408ac0..28879f07 100644 --- a/programs/amm/src/states/tickarray_bitmap_extension.rs +++ b/programs/amm/src/states/tickarray_bitmap_extension.rs @@ -594,4 +594,73 @@ pub mod tick_array_bitmap_extension_test { .unwrap(); assert!(next.0 == false); } + + #[test] + fn bitmap_extension_layout_test() { + use anchor_lang::Discriminator; + + let pool_id = Pubkey::new_unique(); + let mut positive_tick_array_bitmap = [[0u64; 8]; EXTENSION_TICKARRAY_BITMAP_SIZE]; + let mut negative_tick_array_bitmap = [[0u64; 8]; EXTENSION_TICKARRAY_BITMAP_SIZE]; + + // serialize original data + let mut bitmap_extension_data = [0u8; 8 + 32 + 64 * EXTENSION_TICKARRAY_BITMAP_SIZE * 2]; + let mut offset = 0; + bitmap_extension_data[offset..offset + 8] + .copy_from_slice(&TickArrayBitmapExtension::discriminator()); + offset += 8; + bitmap_extension_data[offset..offset + 32].copy_from_slice(&pool_id.to_bytes()); + offset += 32; + + let mut init_data = u64::MAX; + for i in 0..EXTENSION_TICKARRAY_BITMAP_SIZE { + for j in 0..8 { + init_data -= 1; + positive_tick_array_bitmap[i][j] = init_data; + bitmap_extension_data[offset..offset + 8].copy_from_slice(&init_data.to_le_bytes()); + offset += 8; + } + } + for i in 0..EXTENSION_TICKARRAY_BITMAP_SIZE { + for j in 0..8 { + init_data -= 1; + negative_tick_array_bitmap[i][j] = init_data; + bitmap_extension_data[offset..offset + 8].copy_from_slice(&init_data.to_le_bytes()); + offset += 8; + } + } + + // len check + assert_eq!(offset, bitmap_extension_data.len()); + assert_eq!( + bitmap_extension_data.len(), + core::mem::size_of::() + 8 + ); + + // deserialize original data + let unpack_data: &TickArrayBitmapExtension = bytemuck::from_bytes( + &bitmap_extension_data[8..core::mem::size_of::() + 8], + ); + + // data check + let unpack_pool_id = unpack_data.pool_id; + assert_eq!(unpack_pool_id, pool_id); + for i in 0..EXTENSION_TICKARRAY_BITMAP_SIZE { + for j in 0..8 { + let unpack_positive_tick_array_bitmap = + unpack_data.positive_tick_array_bitmap[i][j]; + assert_eq!( + unpack_positive_tick_array_bitmap, + positive_tick_array_bitmap[i][j] + ); + + let unpack_negative_tick_array_bitmap = + unpack_data.negative_tick_array_bitmap[i][j]; + assert_eq!( + unpack_negative_tick_array_bitmap, + negative_tick_array_bitmap[i][j] + ); + } + } + } }