diff --git a/Cargo.lock b/Cargo.lock index 5d418c0b..e1edcace 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1103,8 +1103,7 @@ dependencies = [ "bincode", "clap 4.4.7", "commons", - "lb_clmm", - "mpl-token-metadata", + "dlmm_interface", "proptest", "rand 0.8.5", "rust_decimal", @@ -1140,13 +1139,14 @@ name = "commons" version = "0.3.0" dependencies = [ "anchor-client", - "anchor-lang", "anchor-spl", "anyhow", "assert_matches", - "async-trait", "bincode", - "lb_clmm", + "dlmm_interface", + "num-integer", + "num-traits", + "ruint", "solana-program", "solana-program-test", "solana-sdk", @@ -2286,31 +2286,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lb_clmm" -version = "0.8.2" -dependencies = [ - "anchor-lang", - "anchor-spl", - "assert_matches", - "async-trait", - "bincode", - "bytemuck", - "commons", - "mpl-token-metadata", - "num-integer", - "num-traits", - "num_enum 0.7.1", - "proptest", - "rand 0.7.3", - "ruint", - "solana-program", - "solana-program-test", - "solana-sdk", - "spl-associated-token-account", - "uint", -] - [[package]] name = "libc" version = "0.2.147" @@ -2433,9 +2408,9 @@ dependencies = [ "bs58 0.5.0", "chrono", "clap 4.4.7", + "dlmm_interface", "env_logger", "hyper", - "lb_clmm", "log", "routerify", "serde", @@ -2556,19 +2531,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "mpl-token-metadata" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "651388b8ccae4805f240ebd495172eb44ea10153e018623b628df7034e042a96" -dependencies = [ - "borsh 0.10.3", - "num-derive", - "num-traits", - "solana-program", - "thiserror", -] - [[package]] name = "nix" version = "0.26.4" @@ -2729,15 +2691,6 @@ dependencies = [ "num_enum_derive 0.6.1", ] -[[package]] -name = "num_enum" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" -dependencies = [ - "num_enum_derive 0.7.1", -] - [[package]] name = "num_enum_derive" version = "0.5.11" @@ -2762,18 +2715,6 @@ dependencies = [ "syn 2.0.23", ] -[[package]] -name = "num_enum_derive" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2 1.0.63", - "quote 1.0.29", - "syn 2.0.23", -] - [[package]] name = "number_prefix" version = "0.4.0" @@ -3575,9 +3516,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rust_decimal" @@ -3607,12 +3548,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - [[package]] name = "rustc_version" version = "0.4.0" @@ -5645,18 +5580,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" -[[package]] -name = "uint" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9db035e67dfaf7edd9aebfe8676afcd63eed53c8a4044fed514c8cccf1835177" -dependencies = [ - "byteorder", - "crunchy", - "rustc-hex", - "static_assertions", -] - [[package]] name = "unarray" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index 1798f3e4..ffd89985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,25 +6,40 @@ resolver = "2" anchor-lang = "0.28.0" anchor-spl = "0.28.0" anchor-client = "0.28.0" -clap = "4.3.3" -anyhow = "1.0.71" -shellexpand = "3.1.0" -rust_decimal = "1.31.0" + +solana-sdk = "1.16.0" spl-associated-token-account = "1" -mpl-token-metadata = "3.0.1" -rand = "0.8.5" -tokio = "~1.14.1" +solana-transaction-status = "1.16.0" + serde_json = "1.0.48" serde = "1.0.104" -solana-transaction-status = "1.16.12" +bincode = "1.3.3" +bs58 = "0.5.0" + +clap = "4.3.3" +shellexpand = "3.1.0" + env_logger = "0.9.0" log = "0.4.17" -bs58 = "0.5.0" -chrono = "0.4.31" + +rust_decimal = "1.31.0" +ruint = "1.3.0" +num-integer = "0.1.45" +num-traits = "0.2.16" + hyper = "0.14.17" routerify = "3" + +tokio = "~1.14.1" + +anyhow = "1.0.71" + +rand = "0.8.5" + +chrono = "0.4.31" + ureq = "2.0.0" -bincode = "1.3.3" + commons = { path = "./commons" } [profile.release] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index da7ea249..e1f9c795 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -16,9 +16,8 @@ clap = { workspace = true, features = ["derive"] } anyhow = { workspace = true } shellexpand = { workspace = true } rust_decimal = { workspace = true, features = ["maths"] } -lb_clmm = { path = "../programs/lb_clmm", features = ["cpi"] } +dlmm_interface = { path = "../dlmm_interface" } spl-associated-token-account = { workspace = true } -mpl-token-metadata = { workspace = true } rand = { workspace = true } tokio = { workspace = true, features = ["full", "parking_lot"] } bincode = { workspace = true } diff --git a/commons/Cargo.toml b/commons/Cargo.toml index c86bdfed..8f282f6c 100644 --- a/commons/Cargo.toml +++ b/commons/Cargo.toml @@ -10,16 +10,17 @@ authors = ["tian "] [dependencies] anchor-client = { workspace = true, features = ["async"] } anyhow = { workspace = true } -lb_clmm = { path = "../programs/lb_clmm", features = ["cpi"] } +dlmm_interface = { path = "../dlmm_interface" } tokio = { workspace = true, features = ["full", "parking_lot"] } -bincode = "1.3.3" +bincode = { workspace = true } +solana-sdk = { workspace = true } +ruint = { workspace = true } +num-traits = { workspace = true } +num-integer = { workspace = true } [dev-dependencies] -anchor-lang = { workspace = true } anchor-spl = { workspace = true } +spl-associated-token-account = { workspace = true } solana-program-test = "1.16.0" -solana-sdk = "1.16.0" -async-trait = "0.1.52" assert_matches = "1.5.0" -spl-associated-token-account = { workspace = true } solana-program = "1.16.0" diff --git a/commons/src/constants.rs b/commons/src/constants.rs new file mode 100644 index 00000000..fef21506 --- /dev/null +++ b/commons/src/constants.rs @@ -0,0 +1,76 @@ +pub const BASIS_POINT_MAX: i32 = 10000; + +/// Maximum number of bin a bin array able to contains. +pub const MAX_BIN_PER_ARRAY: usize = 70; + +/// Default number of bin per position contains. +pub const DEFAULT_BIN_PER_POSITION: usize = 70; + +/// Max resize length allowed +pub const MAX_RESIZE_LENGTH: usize = 70; + +/// Maximum number of bin per position contains. +pub const POSITION_MAX_LENGTH: usize = 1400; + +/// Minimum bin ID supported. Computed based on 1 bps. +pub const MIN_BIN_ID: i32 = -443636; + +/// Maximum bin ID supported. Computed based on 1 bps. +pub const MAX_BIN_ID: i32 = 443636; + +/// Maximum fee rate. 10% +pub const MAX_FEE_RATE: u64 = 100_000_000; + +pub const FEE_PRECISION: u64 = 1_000_000_000; + +/// Maximum protocol share of the fee. 25% +pub const MAX_PROTOCOL_SHARE: u16 = 2_500; + +/// Host fee. 20% +pub const HOST_FEE_BPS: u16 = 2_000; + +// Number of rewards supported by pool +pub const NUM_REWARDS: usize = 2; + +// Minimum reward duration +pub const MIN_REWARD_DURATION: u64 = 1; + +pub const MAX_REWARD_DURATION: u64 = 31536000; // 1 year = 365 * 24 * 3600 + +pub const DEFAULT_OBSERVATION_LENGTH: u64 = 100; + +pub const SAMPLE_LIFETIME: u64 = 120; // 2 + +pub const EXTENSION_BINARRAY_BITMAP_SIZE: usize = 12; + +pub const BIN_ARRAY_BITMAP_SIZE: i32 = 512; + +pub const MAX_BASE_FACTOR_STEP: u16 = 100; // 100 bps, 1% + +pub const MAX_FEE_UPDATE_WINDOW: i64 = 0; + +pub const MAX_REWARD_BIN_SPLIT: usize = 15; + +pub const SLOT_BUFFER: u64 = 9000; + +pub const TIME_BUFFER: u64 = 3600; + +pub const MAX_ACTIVATION_SLOT_DURATION: u64 = SLOT_BUFFER * 24 * 31; // 31 days + +pub const MAX_ACTIVATION_TIME_DURATION: u64 = TIME_BUFFER * 24 * 31; // 31 days + +pub const FIVE_MINUTES_SLOT_BUFFER: u64 = SLOT_BUFFER / 12; // 5 minutes + +pub const FIVE_MINUTES_TIME_BUFFER: u64 = TIME_BUFFER / 12; // 5 minutes + +// ILM token launch protocol fee +pub const ILM_PROTOCOL_SHARE: u16 = 2000; // 20% + +/// Maximum bin step +pub const MAX_BIN_STEP: u16 = 400; + +/// Maximum base fee, base_fee / 10^9 = fee_in_percentage +pub const MAX_BASE_FEE: u128 = 100_000_000; // 10% (10^9 * 10 / 100) + +/// Minimum base fee +pub const MIN_BASE_FEE: u128 = 100_000; // 0.01% (10^9 * 0.01 / 100) diff --git a/commons/src/conversions/activation_type.rs b/commons/src/conversions/activation_type.rs new file mode 100644 index 00000000..c51784a3 --- /dev/null +++ b/commons/src/conversions/activation_type.rs @@ -0,0 +1,24 @@ +use dlmm_interface::ActivationType; +use std::ops::Deref; + +pub struct ActivationTypeWrapper(ActivationType); + +impl Deref for ActivationTypeWrapper { + type Target = ActivationType; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom for ActivationTypeWrapper { + type Error = anyhow::Error; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(ActivationTypeWrapper(ActivationType::Slot)), + 1 => Ok(ActivationTypeWrapper(ActivationType::Timestamp)), + _ => Err(anyhow::anyhow!("Invalid ActivationType value: {}", value)), + } + } +} diff --git a/commons/src/conversions/mod.rs b/commons/src/conversions/mod.rs new file mode 100644 index 00000000..ed179a9f --- /dev/null +++ b/commons/src/conversions/mod.rs @@ -0,0 +1,8 @@ +pub mod status; +pub use status::*; + +pub mod pair_type; +pub use pair_type::*; + +pub mod activation_type; +pub use activation_type::*; diff --git a/commons/src/conversions/pair_type.rs b/commons/src/conversions/pair_type.rs new file mode 100644 index 00000000..d11feae0 --- /dev/null +++ b/commons/src/conversions/pair_type.rs @@ -0,0 +1,25 @@ +use dlmm_interface::PairType; +use std::ops::Deref; + +pub struct PairTypeWrapper(PairType); + +impl Deref for PairTypeWrapper { + type Target = PairType; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom for PairTypeWrapper { + type Error = anyhow::Error; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(PairTypeWrapper(PairType::Permissionless)), + 1 => Ok(PairTypeWrapper(PairType::Permission)), + 2 => Ok(PairTypeWrapper(PairType::CustomizablePermissionless)), + _ => Err(anyhow::anyhow!("Invalid PairType value: {}", value)), + } + } +} diff --git a/commons/src/conversions/status.rs b/commons/src/conversions/status.rs new file mode 100644 index 00000000..5ae47d2e --- /dev/null +++ b/commons/src/conversions/status.rs @@ -0,0 +1,24 @@ +use dlmm_interface::PairStatus; +use std::ops::Deref; + +pub struct PairStatusWrapper(PairStatus); + +impl Deref for PairStatusWrapper { + type Target = PairStatus; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom for PairStatusWrapper { + type Error = anyhow::Error; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(PairStatusWrapper(PairStatus::Enabled)), + 1 => Ok(PairStatusWrapper(PairStatus::Disabled)), + _ => Err(anyhow::anyhow!("Invalid PairStatus value: {}", value)), + } + } +} diff --git a/commons/src/extensions/bin.rs b/commons/src/extensions/bin.rs new file mode 100644 index 00000000..9a2943f2 --- /dev/null +++ b/commons/src/extensions/bin.rs @@ -0,0 +1,140 @@ +use crate::*; + +pub trait BinExtension { + fn get_or_store_bin_price(&mut self, id: i32, bin_step: u16) -> Result; + fn is_empty(&self, is_x: bool) -> bool; + fn get_max_amount_out(&self, swap_for_y: bool) -> u64; + fn get_max_amount_in(&self, price: u128, swap_for_y: bool) -> Result; + + fn swap( + &mut self, + amount_in: u64, + price: u128, + swap_for_y: bool, + lb_pair: &LbPair, + host_fee_bps: Option, + ) -> Result; + + fn get_amount_out(amount_in: u64, price: u128, swap_for_y: bool) -> Result; + fn get_amount_in(amount_out: u64, price: u128, swap_for_y: bool) -> Result; +} + +impl BinExtension for Bin { + fn get_or_store_bin_price(&mut self, id: i32, bin_step: u16) -> Result { + if self.price == 0 { + self.price = get_price_from_id(id, bin_step)?; + } + + Ok(self.price) + } + + fn is_empty(&self, is_x: bool) -> bool { + if is_x { + self.amount_x == 0 + } else { + self.amount_y == 0 + } + } + + fn get_max_amount_out(&self, swap_for_y: bool) -> u64 { + if swap_for_y { + self.amount_y + } else { + self.amount_x + } + } + + fn get_max_amount_in(&self, price: u128, swap_for_y: bool) -> Result { + if swap_for_y { + safe_shl_div_cast(self.amount_y.into(), price, SCALE_OFFSET, Rounding::Up) + } else { + safe_mul_shr_cast(self.amount_x.into(), price, SCALE_OFFSET, Rounding::Up) + } + } + + fn get_amount_in(amount_out: u64, price: u128, swap_for_y: bool) -> Result { + if swap_for_y { + safe_shl_div_cast(amount_out.into(), price, SCALE_OFFSET, Rounding::Up) + } else { + safe_mul_shr_cast(amount_out.into(), price, SCALE_OFFSET, Rounding::Up) + } + } + + fn get_amount_out(amount_in: u64, price: u128, swap_for_y: bool) -> Result { + if swap_for_y { + safe_mul_shr_cast(price, amount_in.into(), SCALE_OFFSET, Rounding::Down) + } else { + safe_shl_div_cast(amount_in.into(), price, SCALE_OFFSET, Rounding::Down) + } + } + + fn swap( + &mut self, + amount_in: u64, + price: u128, + swap_for_y: bool, + lb_pair: &LbPair, + host_fee_bps: Option, + ) -> Result { + let max_amount_out = self.get_max_amount_out(swap_for_y); + let mut max_amount_in = self.get_max_amount_in(price, swap_for_y)?; + + let max_fee = lb_pair.compute_fee(max_amount_in)?; + max_amount_in = max_amount_in.checked_add(max_fee).context("overflow")?; + + let (amount_in_with_fees, amount_out, fee, protocol_fee) = if amount_in > max_amount_in { + ( + max_amount_in, + max_amount_out, + max_fee, + lb_pair.compute_protocol_fee(max_fee)?, + ) + } else { + let fee = lb_pair.compute_fee_from_amount(amount_in)?; + let amount_in_after_fee = amount_in.checked_sub(fee).context("overflow")?; + let amount_out = Bin::get_amount_out(amount_in_after_fee, price, swap_for_y)?; + ( + amount_in, + std::cmp::min(amount_out, max_amount_out), + fee, + lb_pair.compute_protocol_fee(fee)?, + ) + }; + + let host_fee = match host_fee_bps { + Some(bps) => protocol_fee + .checked_mul(bps.into()) + .context("overflow")? + .checked_div(BASIS_POINT_MAX as u64) + .context("overflow")?, + None => 0, + }; + + let protocol_fee_after_host_fee = protocol_fee.checked_sub(host_fee).context("overflow")?; + + let amount_into_bin = amount_in_with_fees.checked_sub(fee).context("overflow")?; + + if swap_for_y { + self.amount_x = self + .amount_x + .checked_add(amount_into_bin) + .context("overflow")?; + self.amount_y = self.amount_y.checked_sub(amount_out).context("overflow")?; + } else { + self.amount_y = self + .amount_y + .checked_add(amount_into_bin) + .context("overflow")?; + self.amount_x = self.amount_x.checked_sub(amount_out).context("overflow")?; + } + + Ok(SwapResult { + amount_in_with_fees, + amount_out, + fee, + protocol_fee_after_host_fee, + host_fee, + is_exact_out_amount: false, + }) + } +} diff --git a/commons/src/extensions/bin_array.rs b/commons/src/extensions/bin_array.rs new file mode 100644 index 00000000..fab56c0b --- /dev/null +++ b/commons/src/extensions/bin_array.rs @@ -0,0 +1,57 @@ +use num_integer::Integer; + +use crate::*; + +pub trait BinArrayExtension { + fn is_bin_id_within_range(&self, bin_id: i32) -> Result; + fn get_bin_index_in_array(&self, bin_id: i32) -> Result; + + fn get_bin_array_lower_upper_bin_id(index: i32) -> Result<(i32, i32)>; + fn bin_id_to_bin_array_index(bin_id: i32) -> Result; + + fn get_bin_mut<'a>(&'a mut self, bin_id: i32) -> Result<&'a mut Bin>; +} + +impl BinArrayExtension for BinArray { + fn get_bin_array_lower_upper_bin_id(index: i32) -> Result<(i32, i32)> { + let lower_bin_id = index + .checked_mul(MAX_BIN_PER_ARRAY as i32) + .context("overflow")?; + + let upper_bin_id = lower_bin_id + .checked_add(MAX_BIN_PER_ARRAY as i32) + .context("overflow")? + .checked_sub(1) + .context("overflow")?; + + Ok((lower_bin_id, upper_bin_id)) + } + + fn is_bin_id_within_range(&self, bin_id: i32) -> Result { + let (lower_bin_id, upper_bin_id) = + BinArray::get_bin_array_lower_upper_bin_id(self.index as i32)?; + + Ok(bin_id >= lower_bin_id && bin_id <= upper_bin_id) + } + + fn get_bin_mut<'a>(&'a mut self, bin_id: i32) -> Result<&'a mut Bin> { + Ok(&mut self.bins[self.get_bin_index_in_array(bin_id)?]) + } + + fn get_bin_index_in_array(&self, bin_id: i32) -> Result { + ensure!(self.is_bin_id_within_range(bin_id)?, "Bin id out of range"); + let (lower_bin_id, _) = BinArray::get_bin_array_lower_upper_bin_id(self.index as i32)?; + let index = bin_id.checked_sub(lower_bin_id).context("overflow")?; + Ok(index as usize) + } + + fn bin_id_to_bin_array_index(bin_id: i32) -> Result { + let (idx, rem) = bin_id.div_rem(&(MAX_BIN_PER_ARRAY as i32)); + + if bin_id.is_negative() && rem != 0 { + Ok(idx.checked_sub(1).context("overflow")?) + } else { + Ok(idx) + } + } +} diff --git a/commons/src/extensions/bin_array_bitmap.rs b/commons/src/extensions/bin_array_bitmap.rs new file mode 100644 index 00000000..2a322081 --- /dev/null +++ b/commons/src/extensions/bin_array_bitmap.rs @@ -0,0 +1,254 @@ +use ruint::aliases::U512; + +use crate::*; + +pub trait BinArrayBitmapExtExtension { + fn bitmap_range() -> (i32, i32); + fn get_bitmap_offset(bin_array_index: i32) -> Result; + fn bin_array_offset_in_bitmap(bin_array_index: i32) -> Result; + fn to_bin_array_index(offset: usize, bin_array_offset: usize, is_positive: bool) + -> Result; + + fn get_bitmap(&self, bin_array_index: i32) -> Result<(usize, [u64; 8])>; + fn bit(&self, bin_array_index: i32) -> Result; + fn iter_bitmap(&self, start_index: i32, end_index: i32) -> Result>; + fn next_bin_array_index_with_liquidity( + &self, + swap_for_y: bool, + start_index: i32, + ) -> Result<(i32, bool)>; +} + +impl BinArrayBitmapExtExtension for BinArrayBitmapExtension { + fn bitmap_range() -> (i32, i32) { + return ( + -BIN_ARRAY_BITMAP_SIZE * (EXTENSION_BINARRAY_BITMAP_SIZE as i32 + 1), + BIN_ARRAY_BITMAP_SIZE * (EXTENSION_BINARRAY_BITMAP_SIZE as i32 + 1) - 1, + ); + } + + fn next_bin_array_index_with_liquidity( + &self, + swap_for_y: bool, + start_index: i32, + ) -> Result<(i32, bool)> { + let (min_bitmap_id, max_bit_map_id) = BinArrayBitmapExtension::bitmap_range(); + if start_index > 0 { + if swap_for_y { + match self.iter_bitmap(start_index, BIN_ARRAY_BITMAP_SIZE)? { + Some(value) => return Ok((value, true)), + None => return Ok((BIN_ARRAY_BITMAP_SIZE - 1, false)), + } + } else { + match self.iter_bitmap(start_index, max_bit_map_id)? { + Some(value) => return Ok((value, true)), + None => return Err(LbClmmError::CannotFindNonZeroLiquidityBinArrayId.into()), + } + } + } else { + if swap_for_y { + match self.iter_bitmap(start_index, min_bitmap_id)? { + Some(value) => return Ok((value, true)), + None => return Err(LbClmmError::CannotFindNonZeroLiquidityBinArrayId.into()), + } + } else { + match self.iter_bitmap(start_index, -BIN_ARRAY_BITMAP_SIZE - 1)? { + Some(value) => return Ok((value, true)), + None => return Ok((-BIN_ARRAY_BITMAP_SIZE, false)), + } + } + } + } + + fn bit(&self, bin_array_index: i32) -> Result { + let (_, bin_array_bitmap) = self.get_bitmap(bin_array_index)?; + let bin_array_offset_in_bitmap = Self::bin_array_offset_in_bitmap(bin_array_index)?; + let bin_array_bitmap = U512::from_limbs(bin_array_bitmap); + Ok(bin_array_bitmap.bit(bin_array_offset_in_bitmap as usize)) + } + + fn get_bitmap(&self, bin_array_index: i32) -> Result<(usize, [u64; 8])> { + let offset = Self::get_bitmap_offset(bin_array_index)?; + if bin_array_index < 0 { + Ok((offset, self.negative_bin_array_bitmap[offset])) + } else { + Ok((offset, self.positive_bin_array_bitmap[offset])) + } + } + + fn to_bin_array_index( + offset: usize, + bin_array_offset: usize, + is_positive: bool, + ) -> Result { + let offset = offset as i32; + let bin_array_offset = bin_array_offset as i32; + if is_positive { + Ok((offset + 1) * BIN_ARRAY_BITMAP_SIZE + bin_array_offset) + } else { + Ok(-((offset + 1) * BIN_ARRAY_BITMAP_SIZE + bin_array_offset) - 1) + } + } + + fn bin_array_offset_in_bitmap(bin_array_index: i32) -> Result { + if bin_array_index > 0 { + Ok(bin_array_index + .checked_rem(BIN_ARRAY_BITMAP_SIZE) + .context("overflow")? as usize) + } else { + Ok((-(bin_array_index + 1)) + .checked_rem(BIN_ARRAY_BITMAP_SIZE) + .context("overflow")? as usize) + } + } + + fn get_bitmap_offset(bin_array_index: i32) -> Result { + let offset = if bin_array_index > 0 { + bin_array_index / BIN_ARRAY_BITMAP_SIZE - 1 + } else { + -(bin_array_index + 1) / BIN_ARRAY_BITMAP_SIZE - 1 + }; + Ok(offset as usize) + } + + fn iter_bitmap(&self, start_index: i32, end_index: i32) -> Result> { + if start_index == end_index { + if self.bit(start_index)? { + return Ok(Some(start_index)); + } else { + return Ok(None); + } + } + let offset: usize = Self::get_bitmap_offset(start_index)?; + let bin_array_offset = Self::bin_array_offset_in_bitmap(start_index)?; + if start_index < 0 { + // iter in negative_bin_array_bitmap + if start_index < end_index { + for i in (0..=offset).rev() { + let mut bin_array_bitmap = U512::from_limbs(self.negative_bin_array_bitmap[i]); + + if i == offset { + bin_array_bitmap = bin_array_bitmap + << BIN_ARRAY_BITMAP_SIZE as usize - bin_array_offset - 1; + if bin_array_bitmap.eq(&U512::ZERO) { + continue; + } + + let bin_array_offset_in_bitmap = + bin_array_offset - bin_array_bitmap.leading_zeros(); + + return Ok(Some(BinArrayBitmapExtension::to_bin_array_index( + i, + bin_array_offset_in_bitmap, + false, + )?)); + } + if bin_array_bitmap.eq(&U512::ZERO) { + continue; + } + let bin_array_offset_in_bitmap = + BIN_ARRAY_BITMAP_SIZE as usize - bin_array_bitmap.leading_zeros() - 1; + return Ok(Some(BinArrayBitmapExtension::to_bin_array_index( + i, + bin_array_offset_in_bitmap, + false, + )?)); + } + } else { + for i in offset..EXTENSION_BINARRAY_BITMAP_SIZE { + let mut bin_array_bitmap = U512::from_limbs(self.negative_bin_array_bitmap[i]); + if i == offset { + bin_array_bitmap = bin_array_bitmap >> bin_array_offset; + if bin_array_bitmap.eq(&U512::ZERO) { + continue; + } + + let bin_array_offset_in_bitmap = + bin_array_offset + bin_array_bitmap.trailing_zeros(); + + return Ok(Some(BinArrayBitmapExtension::to_bin_array_index( + i, + bin_array_offset_in_bitmap, + false, + )?)); + } + + if bin_array_bitmap.eq(&U512::ZERO) { + continue; + } + let bin_array_offset_in_bitmap = bin_array_bitmap.trailing_zeros(); + + return Ok(Some(BinArrayBitmapExtension::to_bin_array_index( + i, + bin_array_offset_in_bitmap, + false, + )?)); + } + } + } else { + // iter in possitive_bin_array_bitmap + if start_index < end_index { + for i in offset..EXTENSION_BINARRAY_BITMAP_SIZE { + let mut bin_array_bitmap = U512::from_limbs(self.positive_bin_array_bitmap[i]); + if i == offset { + bin_array_bitmap = bin_array_bitmap >> bin_array_offset; + if bin_array_bitmap.eq(&U512::ZERO) { + continue; + } + + let bin_array_offset_in_bitmap = + bin_array_offset + bin_array_bitmap.trailing_zeros(); + return Ok(Some(BinArrayBitmapExtension::to_bin_array_index( + i, + bin_array_offset_in_bitmap, + true, + )?)); + } + + if bin_array_bitmap.eq(&U512::ZERO) { + continue; + } + + let bin_array_offset_in_bitmap = bin_array_bitmap.trailing_zeros(); + return Ok(Some(BinArrayBitmapExtension::to_bin_array_index( + i, + bin_array_offset_in_bitmap, + true, + )?)); + } + } else { + for i in (0..=offset).rev() { + let mut bin_array_bitmap = U512::from_limbs(self.positive_bin_array_bitmap[i]); + + if i == offset { + bin_array_bitmap = bin_array_bitmap + << BIN_ARRAY_BITMAP_SIZE as usize - bin_array_offset - 1; + + if bin_array_bitmap.eq(&U512::ZERO) { + continue; + } + let bin_array_offset_in_bitmap = + bin_array_offset - bin_array_bitmap.leading_zeros(); + return Ok(Some(BinArrayBitmapExtension::to_bin_array_index( + i, + bin_array_offset_in_bitmap, + true, + )?)); + } + + if bin_array_bitmap.eq(&U512::ZERO) { + continue; + } + let bin_array_offset_in_bitmap = + BIN_ARRAY_BITMAP_SIZE as usize - bin_array_bitmap.leading_zeros() - 1; + return Ok(Some(BinArrayBitmapExtension::to_bin_array_index( + i, + bin_array_offset_in_bitmap, + true, + )?)); + } + } + } + Ok(None) + } +} diff --git a/commons/src/extensions/lb_pair.rs b/commons/src/extensions/lb_pair.rs new file mode 100644 index 00000000..74c56329 --- /dev/null +++ b/commons/src/extensions/lb_pair.rs @@ -0,0 +1,275 @@ +use crate::*; +use ruint::aliases::U1024; +use std::ops::Shl; +use std::ops::Shr; + +pub trait LbPairExtension { + fn bitmap_range() -> (i32, i32); + fn get_bin_array_offset(bin_array_index: i32) -> usize; + + fn status(&self) -> Result; + fn pair_type(&self) -> Result; + fn activation_type(&self) -> Result; + fn compute_fee(&self, amount: u64) -> Result; + fn get_total_fee(&self) -> Result; + fn get_base_fee(&self) -> Result; + fn get_variable_fee(&self) -> Result; + fn compute_variable_fee(&self, volatility_accumulator: u32) -> Result; + fn compute_protocol_fee(&self, fee_amount: u64) -> Result; + fn compute_fee_from_amount(&self, amount_with_fees: u64) -> Result; + fn is_overflow_default_bin_array_bitmap(&self, bin_array_index: i32) -> bool; + fn next_bin_array_index_with_liquidity_internal( + &self, + swap_for_y: bool, + start_array_index: i32, + ) -> Result<(i32, bool)>; + + fn update_references(&mut self, current_timestamp: i64) -> Result<()>; + fn update_volatility_accumulator(&mut self) -> Result<()>; + fn advance_active_bin(&mut self, swap_for_y: bool) -> Result<()>; +} + +impl LbPairExtension for LbPair { + fn status(&self) -> Result { + Ok(self.status.try_into()?) + } + + fn pair_type(&self) -> Result { + Ok(self.pair_type.try_into()?) + } + + fn activation_type(&self) -> Result { + Ok(self.activation_type.try_into()?) + } + + fn update_references(&mut self, current_timestamp: i64) -> Result<()> { + let v_params = &mut self.v_parameters; + let s_params = &self.parameters; + + let elapsed = current_timestamp + .checked_sub(v_params.last_update_timestamp) + .context("overflow")?; + + // Not high frequency trade + if elapsed >= s_params.filter_period as i64 { + // Update active id of last transaction + v_params.index_reference = self.active_id; + // filter period < t < decay_period. Decay time window. + if elapsed < s_params.decay_period as i64 { + let volatility_reference = v_params + .volatility_accumulator + .checked_sub(s_params.reduction_factor as u32) + .context("overflow")? + .checked_div(BASIS_POINT_MAX as u32) + .context("overflow")?; + + v_params.volatility_reference = volatility_reference; + } + // Out of decay time window + else { + v_params.volatility_reference = 0; + } + } + + Ok(()) + } + + fn update_volatility_accumulator(&mut self) -> Result<()> { + let v_params = &mut self.v_parameters; + let s_params = &self.parameters; + + let delta_id = i64::from(v_params.index_reference) + .checked_sub(self.active_id.into()) + .context("overflow")? + .unsigned_abs(); + + let volatility_accumulator = u64::from(v_params.volatility_reference) + .checked_add( + delta_id + .checked_mul(BASIS_POINT_MAX as u64) + .context("overflow")?, + ) + .context("overflow")?; + + v_params.volatility_accumulator = std::cmp::min( + volatility_accumulator, + s_params.max_volatility_accumulator.into(), + ) + .try_into() + .context("overflow")?; + + Ok(()) + } + + fn get_base_fee(&self) -> Result { + Ok(u128::from(self.parameters.base_factor) + .checked_mul(self.bin_step.into()) + .context("overflow")? + .checked_mul(10u128) + .context("overflow")? + .checked_mul(10u128.pow(self.parameters.base_fee_power_factor.into())) + .context("overflow")?) + } + + fn get_variable_fee(&self) -> Result { + self.compute_variable_fee(self.v_parameters.volatility_accumulator) + } + + fn compute_variable_fee(&self, volatility_accumulator: u32) -> Result { + if self.parameters.variable_fee_control > 0 { + let volatility_accumulator: u128 = volatility_accumulator.into(); + let bin_step: u128 = self.bin_step.into(); + let variable_fee_control: u128 = self.parameters.variable_fee_control.into(); + + let square_vfa_bin = volatility_accumulator + .checked_mul(bin_step) + .context("overflow")? + .checked_pow(2) + .context("overflow")?; + + let v_fee = variable_fee_control + .checked_mul(square_vfa_bin) + .context("overflow")?; + + let scaled_v_fee = v_fee + .checked_add(99_999_999_999) + .context("overflow")? + .checked_div(100_000_000_000) + .context("overflow")?; + + return Ok(scaled_v_fee); + } + + Ok(0) + } + + fn get_total_fee(&self) -> Result { + let total_fee_rate = self + .get_base_fee()? + .checked_add(self.get_variable_fee()?) + .context("overflow")?; + let total_fee_rate_cap = std::cmp::min(total_fee_rate, MAX_FEE_RATE.into()); + Ok(total_fee_rate_cap) + } + + fn compute_fee(&self, amount: u64) -> Result { + let total_fee_rate = self.get_total_fee()?; + let denominator = u128::from(FEE_PRECISION) + .checked_sub(total_fee_rate) + .context("overflow")?; + + // Ceil division + let fee = u128::from(amount) + .checked_mul(total_fee_rate) + .context("overflow")? + .checked_add(denominator) + .context("overflow")? + .checked_sub(1) + .context("overflow")?; + + let scaled_down_fee = fee.checked_div(denominator).context("overflow")?; + + Ok(scaled_down_fee.try_into().context("overflow")?) + } + + fn advance_active_bin(&mut self, swap_for_y: bool) -> Result<()> { + let next_active_bin_id = if swap_for_y { + self.active_id.checked_sub(1) + } else { + self.active_id.checked_add(1) + } + .context("overflow")?; + + ensure!( + next_active_bin_id >= MIN_BIN_ID && next_active_bin_id <= MAX_BIN_ID, + "Insufficient liquidity" + ); + + self.active_id = next_active_bin_id; + + Ok(()) + } + + fn compute_protocol_fee(&self, fee_amount: u64) -> Result { + let protocol_fee = u128::from(fee_amount) + .checked_mul(self.parameters.protocol_share.into()) + .context("overflow")? + .checked_div(BASIS_POINT_MAX as u128) + .context("overflow")?; + + Ok(protocol_fee.try_into().context("overflow")?) + } + + fn compute_fee_from_amount(&self, amount_with_fees: u64) -> Result { + let total_fee_rate = self.get_total_fee()?; + + let fee_amount = u128::from(amount_with_fees) + .checked_mul(total_fee_rate) + .context("overflow")? + .checked_add((FEE_PRECISION - 1).into()) + .context("overflow")?; + + let scaled_down_fee = fee_amount + .checked_div(FEE_PRECISION.into()) + .context("overflow")?; + + Ok(scaled_down_fee.try_into().context("overflow")?) + } + + fn bitmap_range() -> (i32, i32) { + (-BIN_ARRAY_BITMAP_SIZE, BIN_ARRAY_BITMAP_SIZE - 1) + } + + fn is_overflow_default_bin_array_bitmap(&self, bin_array_index: i32) -> bool { + let (min_bitmap_id, max_bitmap_id) = Self::bitmap_range(); + bin_array_index > max_bitmap_id || bin_array_index < min_bitmap_id + } + + fn get_bin_array_offset(bin_array_index: i32) -> usize { + (bin_array_index + BIN_ARRAY_BITMAP_SIZE) as usize + } + + fn next_bin_array_index_with_liquidity_internal( + &self, + swap_for_y: bool, + start_array_index: i32, + ) -> Result<(i32, bool)> { + let bin_array_bitmap = U1024::from_limbs(self.bin_array_bitmap); + let array_offset: usize = Self::get_bin_array_offset(start_array_index); + let (min_bitmap_id, max_bitmap_id) = LbPair::bitmap_range(); + if swap_for_y { + let bitmap_range: usize = max_bitmap_id + .checked_sub(min_bitmap_id) + .context("overflow")? + .try_into() + .context("overflow")?; + let offset_bit_map = + bin_array_bitmap.shl(bitmap_range.checked_sub(array_offset).context("overflow")?); + + if offset_bit_map.eq(&U1024::ZERO) { + return Ok((min_bitmap_id.checked_sub(1).context("overflow")?, false)); + } else { + let next_bit = offset_bit_map.leading_zeros(); + return Ok(( + start_array_index + .checked_sub(next_bit as i32) + .context("overflow")?, + true, + )); + } + } else { + let offset_bit_map = bin_array_bitmap.shr(array_offset); + if offset_bit_map.eq(&U1024::ZERO) { + return Ok((max_bitmap_id.checked_add(1).context("overflow")?, false)); + } else { + let next_bit = offset_bit_map.trailing_zeros(); + return Ok(( + start_array_index + .checked_add(next_bit as i32) + .context("overflow")?, + true, + )); + }; + } + } +} diff --git a/commons/src/extensions/mod.rs b/commons/src/extensions/mod.rs new file mode 100644 index 00000000..ba1c6d9e --- /dev/null +++ b/commons/src/extensions/mod.rs @@ -0,0 +1,11 @@ +pub mod lb_pair; +pub use lb_pair::*; + +pub mod bin_array; +pub use bin_array::*; + +pub mod bin; +pub use bin::*; + +pub mod bin_array_bitmap; +pub use bin_array_bitmap::*; diff --git a/commons/src/lib.rs b/commons/src/lib.rs index 9e1a6d43..4ed777c6 100644 --- a/commons/src/lib.rs +++ b/commons/src/lib.rs @@ -1 +1,26 @@ +use anyhow::*; +use dlmm_interface::*; + +pub mod constants; +pub use constants::*; + +pub mod conversions; +pub use conversions::*; + +pub mod extensions; +pub use extensions::*; + +pub mod pda; +pub use pda::*; + pub mod quote; +pub use quote::*; + +pub mod seeds; +pub use seeds::*; + +pub mod math; +pub use math::*; + +pub mod typedefs; +pub use typedefs::*; diff --git a/commons/src/math/mod.rs b/commons/src/math/mod.rs new file mode 100644 index 00000000..741ba816 --- /dev/null +++ b/commons/src/math/mod.rs @@ -0,0 +1,11 @@ +pub mod price_math; +pub use price_math::*; + +pub mod u64x64_math; +pub use u64x64_math::*; + +pub mod u128x128_math; +pub use u128x128_math::*; + +pub mod utils; +pub use utils::*; diff --git a/commons/src/math/price_math.rs b/commons/src/math/price_math.rs new file mode 100644 index 00000000..38b8a243 --- /dev/null +++ b/commons/src/math/price_math.rs @@ -0,0 +1,13 @@ +use crate::*; + +pub fn get_price_from_id(active_id: i32, bin_step: u16) -> Result { + let bps = u128::from(bin_step) + .checked_shl(SCALE_OFFSET.into()) + .context("overflow")? + .checked_div(BASIS_POINT_MAX as u128) + .context("overflow")?; + + let base = ONE.checked_add(bps).context("overflow")?; + + pow(base, active_id).context("overflow") +} diff --git a/commons/src/math/u128x128_math.rs b/commons/src/math/u128x128_math.rs new file mode 100644 index 00000000..cef0e8af --- /dev/null +++ b/commons/src/math/u128x128_math.rs @@ -0,0 +1,37 @@ +use dlmm_interface::Rounding; +use ruint::aliases::U256; + +/// (x * y) / denominator +pub fn mul_div(x: u128, y: u128, denominator: u128, rounding: Rounding) -> Option { + if denominator == 0 { + return None; + } + + let x = U256::from(x); + let y = U256::from(y); + let denominator = U256::from(denominator); + + let prod = x.checked_mul(y)?; + + match rounding { + Rounding::Up => prod.div_ceil(denominator).try_into().ok(), + Rounding::Down => { + let (quotient, _) = prod.div_rem(denominator); + quotient.try_into().ok() + } + } +} + +/// (x * y) >> offset +#[inline] +pub fn mul_shr(x: u128, y: u128, offset: u8, rounding: Rounding) -> Option { + let denominator = 1u128.checked_shl(offset.into())?; + mul_div(x, y, denominator, rounding) +} + +/// (x << offset) / y +#[inline] +pub fn shl_div(x: u128, y: u128, offset: u8, rounding: Rounding) -> Option { + let scale = 1u128.checked_shl(offset.into())?; + mul_div(x, scale, y, rounding) +} diff --git a/commons/src/math/u64x64_math.rs b/commons/src/math/u64x64_math.rs new file mode 100644 index 00000000..e101a472 --- /dev/null +++ b/commons/src/math/u64x64_math.rs @@ -0,0 +1,180 @@ +// Precision when converting from decimal to fixed point. Or the other way around. 10^12 +pub const PRECISION: u128 = 1_000_000_000_000; + +// Number of bits to scale. This will decide the position of the radix point. +pub const SCALE_OFFSET: u8 = 64; + +// Where does this value come from ? +// When smallest bin is used (1 bps), the maximum of bin limit is 887272 (Check: https://docs.traderjoexyz.com/concepts/bin-math). +// But in solana, the token amount is represented in 64 bits, therefore, it will be (1 + 0.0001)^n < 2 ** 64, solve for n, n ~= 443636 +// Then we calculate bits needed to represent 443636 exponential, 2^n >= 443636, ~= 19 +// If we convert 443636 to binary form, it will be 1101100010011110100 (19 bits). +// Which, the 19 bits are the bits the binary exponential will loop through. +// The 20th bit will be 0x80000, which the exponential already > the maximum number of bin Q64.64 can support +const MAX_EXPONENTIAL: u32 = 0x80000; // 1048576 + +// 1.0000... representation of 64x64 +pub const ONE: u128 = 1u128 << SCALE_OFFSET; + +pub fn pow(base: u128, exp: i32) -> Option { + // If exponent is negative. We will invert the result later by 1 / base^exp.abs() + let mut invert = exp.is_negative(); + + // When exponential is 0, result will always be 1 + if exp == 0 { + return Some(1u128 << 64); + } + + // Make the exponential positive. Which will compute the result later by 1 / base^exp + let exp: u32 = if invert { exp.abs() as u32 } else { exp as u32 }; + + // No point to continue the calculation as it will overflow the maximum value Q64.64 can support + if exp >= MAX_EXPONENTIAL { + return None; + } + + let mut squared_base = base; + let mut result = ONE; + + // When multiply the base twice, the number of bits double from 128 -> 256, which overflow. + // The trick here is to inverse the calculation, which make the upper 64 bits (number bits) to be 0s. + // For example: + // let base = 1.001, exp = 5 + // let neg = 1 / (1.001 ^ 5) + // Inverse the neg: 1 / neg + // By using a calculator, you will find out that 1.001^5 == 1 / (1 / 1.001^5) + if squared_base >= result { + // This inverse the base: 1 / base + squared_base = u128::MAX.checked_div(squared_base)?; + // If exponent is negative, the above already inverted the result. Therefore, at the end of the function, we do not need to invert again. + invert = !invert; + } + + // The following code is equivalent to looping through each binary value of the exponential. + // As explained in MAX_EXPONENTIAL, 19 exponential bits are enough to covert the full bin price. + // Therefore, there will be 19 if statements, which similar to the following pseudo code. + /* + let mut result = 1; + while exponential > 0 { + if exponential & 1 > 0 { + result *= base; + } + base *= base; + exponential >>= 1; + } + */ + + // From right to left + // squared_base = 1 * base^1 + // 1st bit is 1 + if exp & 0x1 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + // squared_base = base^2 + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + // 2nd bit is 1 + if exp & 0x2 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + // Example: + // If the base is 1.001, exponential is 3. Binary form of 3 is ..0011. The last 2 1's bit fulfill the above 2 bitwise condition. + // The result will be 1 * base^1 * base^2 == base^3. The process continues until reach the 20th bit + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x4 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x8 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x10 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x20 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x40 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x80 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x100 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x200 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x400 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x800 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x1000 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x2000 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x4000 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x8000 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x10000 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x20000 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET; + if exp & 0x40000 > 0 { + result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET + } + + // Stop here as the next is 20th bit, which > MAX_EXPONENTIAL + if result == 0 { + return None; + } + + if invert { + result = u128::MAX.checked_div(result)?; + } + + Some(result) +} diff --git a/commons/src/math/utils.rs b/commons/src/math/utils.rs new file mode 100644 index 00000000..7decad42 --- /dev/null +++ b/commons/src/math/utils.rs @@ -0,0 +1,23 @@ +use crate::*; +use dlmm_interface::Rounding; +use num_traits::FromPrimitive; + +#[inline] +pub fn safe_mul_shr_cast( + x: u128, + y: u128, + offset: u8, + rounding: Rounding, +) -> Result { + T::from_u128(mul_shr(x, y, offset, rounding).context("overflow")?).context("overflow") +} + +#[inline] +pub fn safe_shl_div_cast( + x: u128, + y: u128, + offset: u8, + rounding: Rounding, +) -> Result { + T::from_u128(shl_div(x, y, offset, rounding).context("overflow")?).context("overflow") +} diff --git a/commons/src/pda.rs b/commons/src/pda.rs new file mode 100644 index 00000000..10f8cc2b --- /dev/null +++ b/commons/src/pda.rs @@ -0,0 +1,142 @@ +use super::seeds::{ + self, BIN_ARRAY, BIN_ARRAY_BITMAP_SEED, ILM_BASE_KEY, ORACLE, PRESET_PARAMETER, +}; +use anchor_client::solana_sdk::pubkey::Pubkey; +use std::{cmp::max, cmp::min}; + +pub fn derive_lb_pair_pda2( + token_x_mint: Pubkey, + token_y_mint: Pubkey, + bin_step: u16, + base_factor: u16, +) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[ + min(token_x_mint, token_y_mint).as_ref(), + max(token_x_mint, token_y_mint).as_ref(), + &bin_step.to_le_bytes(), + &base_factor.to_le_bytes(), + ], + &dlmm_interface::ID, + ) +} + +pub fn derive_customizable_permissionless_lb_pair( + token_x_mint: Pubkey, + token_y_mint: Pubkey, +) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[ + ILM_BASE_KEY.as_ref(), + min(token_x_mint, token_y_mint).as_ref(), + max(token_x_mint, token_y_mint).as_ref(), + ], + &dlmm_interface::ID, + ) +} + +pub fn derive_permission_lb_pair_pda( + base: Pubkey, + token_x_mint: Pubkey, + token_y_mint: Pubkey, + bin_step: u16, +) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[ + base.as_ref(), + min(token_x_mint, token_y_mint).as_ref(), + max(token_x_mint, token_y_mint).as_ref(), + &bin_step.to_le_bytes(), + ], + &dlmm_interface::ID, + ) +} + +#[deprecated] +pub fn derive_lb_pair_pda( + token_x_mint: Pubkey, + token_y_mint: Pubkey, + bin_step: u16, +) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[ + min(token_x_mint, token_y_mint).as_ref(), + max(token_x_mint, token_y_mint).as_ref(), + &bin_step.to_le_bytes(), + ], + &dlmm_interface::ID, + ) +} + +pub fn derive_position_pda( + lb_pair: Pubkey, + base: Pubkey, + lower_bin_id: i32, + width: i32, +) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[ + seeds::POSITION.as_ref(), + lb_pair.as_ref(), + base.as_ref(), + lower_bin_id.to_le_bytes().as_ref(), + width.to_le_bytes().as_ref(), + ], + &dlmm_interface::ID, + ) +} + +pub fn derive_oracle_pda(lb_pair: Pubkey) -> (Pubkey, u8) { + Pubkey::find_program_address(&[ORACLE, lb_pair.as_ref()], &dlmm_interface::ID) +} + +pub fn derive_bin_array_pda(lb_pair: Pubkey, bin_array_index: i64) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[BIN_ARRAY, lb_pair.as_ref(), &bin_array_index.to_le_bytes()], + &dlmm_interface::ID, + ) +} + +pub fn derive_bin_array_bitmap_extension(lb_pair: Pubkey) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[BIN_ARRAY_BITMAP_SEED, lb_pair.as_ref()], + &dlmm_interface::ID, + ) +} + +pub fn derive_reserve_pda(token_mint: Pubkey, lb_pair: Pubkey) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[lb_pair.as_ref(), token_mint.as_ref()], + &dlmm_interface::ID, + ) +} + +pub fn derive_reward_vault_pda(lb_pair: Pubkey, reward_index: u64) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[lb_pair.as_ref(), reward_index.to_le_bytes().as_ref()], + &dlmm_interface::ID, + ) +} + +pub fn derive_event_authority_pda() -> (Pubkey, u8) { + Pubkey::find_program_address(&[b"__event_authority"], &dlmm_interface::ID) +} + +#[deprecated] +pub fn derive_preset_parameter_pda(bin_step: u16) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[PRESET_PARAMETER, &bin_step.to_le_bytes()], + &dlmm_interface::ID, + ) +} + +pub fn derive_preset_parameter_pda2(bin_step: u16, base_factor: u16) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[ + PRESET_PARAMETER, + &bin_step.to_le_bytes(), + &base_factor.to_le_bytes(), + ], + &dlmm_interface::ID, + ) +} diff --git a/commons/src/quote.rs b/commons/src/quote.rs index 7c38547d..11aef859 100644 --- a/commons/src/quote.rs +++ b/commons/src/quote.rs @@ -1,15 +1,7 @@ +use crate::*; use anchor_client::solana_sdk::pubkey::Pubkey; -use anyhow::{ensure, Context, Result}; -use lb_clmm::{ - pair_action_access::ActivationType, - state::{ - bin::{Bin, BinArray, SwapResult}, - bin_array_bitmap_extension::BinArrayBitmapExtension, - lb_pair::{LbPair, PairStatus, PairType}, - }, - utils::pda::derive_bin_array_pda, -}; -use std::collections::HashMap; +use core::result::Result::Ok; +use std::{collections::HashMap, ops::Deref}; #[derive(Debug)] pub struct SwapExactInQuote { @@ -35,8 +27,8 @@ fn validate_swap_activation( let pair_type = lb_pair.pair_type()?; if pair_type.eq(&PairType::Permission) { - let activation_type = ActivationType::try_from(lb_pair.activation_type)?; - let current_point = match activation_type { + let activation_type = lb_pair.activation_type()?; + let current_point = match activation_type.deref() { ActivationType::Slot => current_slot, ActivationType::Timestamp => current_timestamp, }; @@ -277,7 +269,6 @@ pub fn get_bin_array_pubkeys_for_swap( #[cfg(test)] mod tests { use super::*; - use anchor_client::anchor_lang::AccountDeserialize; use anchor_client::solana_sdk::clock::Clock; use anchor_client::{ solana_client::nonblocking::rpc_client::RpcClient, @@ -302,16 +293,11 @@ mod tests { // RPC client. No gPA is required. let rpc_client = RpcClient::new(Cluster::Mainnet.url().to_string()); - let client = Client::new( - Cluster::Custom(rpc_client.url(), rpc_client.url()), - Rc::new(Keypair::new()), - ); - - let program = client.program(lb_clmm::ID).unwrap(); - let SOL_USDC = Pubkey::from_str("HTvjzsfX3yU6BUodCjZ5vZkUrAxMDTrBs3CJaq43ashR").unwrap(); - let lb_pair = program.account::(SOL_USDC).await.unwrap(); + let lb_pair_account = rpc_client.get_account(&SOL_USDC).await.unwrap(); + + let lb_pair = LbPairAccount::deserialize(&lb_pair_account.data).unwrap().0; // 3 bin arrays to left, and right is enough to cover most of the swap, and stay under 1.4m CU constraint. // Get 3 bin arrays to the left from the active bin @@ -339,7 +325,9 @@ mod tests { .map(|(account, key)| { ( key, - BinArray::try_deserialize(&mut account.unwrap().data.as_ref()).unwrap(), + BinArrayAccount::deserialize(&account.unwrap().data) + .unwrap() + .0, ) }) .collect::>(); @@ -437,11 +425,13 @@ mod tests { Rc::new(Keypair::new()), ); - let program = client.program(lb_clmm::ID).unwrap(); + let program = client.program(dlmm_interface::ID).unwrap(); let SOL_USDC = Pubkey::from_str("HTvjzsfX3yU6BUodCjZ5vZkUrAxMDTrBs3CJaq43ashR").unwrap(); - let lb_pair = program.account::(SOL_USDC).await.unwrap(); + let lb_pair_account = rpc_client.get_account(&SOL_USDC).await.unwrap(); + + let lb_pair = LbPairAccount::deserialize(&lb_pair_account.data).unwrap().0; // 3 bin arrays to left, and right is enough to cover most of the swap, and stay under 1.4m CU constraint. // Get 3 bin arrays to the left from the active bin @@ -469,7 +459,9 @@ mod tests { .map(|(account, key)| { ( key, - BinArray::try_deserialize(&mut account.unwrap().data.as_ref()).unwrap(), + BinArrayAccount::deserialize(&account.unwrap().data) + .unwrap() + .0, ) }) .collect::>(); diff --git a/commons/src/seeds.rs b/commons/src/seeds.rs new file mode 100644 index 00000000..4b56ef6e --- /dev/null +++ b/commons/src/seeds.rs @@ -0,0 +1,14 @@ +use solana_sdk::pubkey; +use solana_sdk::pubkey::Pubkey; + +pub const BIN_ARRAY: &[u8] = b"bin_array"; + +pub const ORACLE: &[u8] = b"oracle"; + +pub const BIN_ARRAY_BITMAP_SEED: &[u8] = b"bitmap"; + +pub const PRESET_PARAMETER: &[u8] = b"preset_parameter"; + +pub const POSITION: &[u8] = b"position"; + +pub const ILM_BASE_KEY: Pubkey = pubkey!("MFGQxwAmB91SwuYX36okv2Qmdc9aMuHTwWGUrp4AtB1"); diff --git a/commons/src/typedefs.rs b/commons/src/typedefs.rs new file mode 100644 index 00000000..792cd65f --- /dev/null +++ b/commons/src/typedefs.rs @@ -0,0 +1,15 @@ +#[derive(Debug)] +pub struct SwapResult { + /// Amount of token swap into the bin + pub amount_in_with_fees: u64, + /// Amount of token swap out from the bin + pub amount_out: u64, + /// Swap fee, includes protocol fee + pub fee: u64, + /// Part of fee + pub protocol_fee_after_host_fee: u64, + /// Part of protocol fee + pub host_fee: u64, + /// Indicate whether we reach exact out amount + pub is_exact_out_amount: bool, +} diff --git a/commons/tests/helpers/utils.rs b/commons/tests/helpers/utils.rs index 6ce27db8..0ccfa1e1 100644 --- a/commons/tests/helpers/utils.rs +++ b/commons/tests/helpers/utils.rs @@ -1,11 +1,10 @@ -use anchor_lang::solana_program::{instruction::Instruction, pubkey::Pubkey}; -use anchor_lang::AccountDeserialize; use anchor_spl::associated_token::*; use anchor_spl::token::spl_token; use assert_matches::assert_matches; -use async_trait::async_trait; use solana_program_test::BanksClient; use solana_sdk::{ + instruction::Instruction, + pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction, }; @@ -30,24 +29,6 @@ pub async fn process_and_assert_ok( assert_matches!(banks_client.process_transaction(tx).await, Ok(())); } -#[async_trait] -pub trait AnchorAdapter { - async fn get_account_with_anchor_seder( - &mut self, - address: Pubkey, - ) -> Option; -} -#[async_trait] -impl AnchorAdapter for BanksClient { - async fn get_account_with_anchor_seder( - &mut self, - address: Pubkey, - ) -> Option { - let account = self.get_account(address).await.unwrap()?; - T::try_deserialize(&mut account.data.as_ref()).ok() - } -} - pub async fn get_or_create_ata( payer: &Keypair, token_mint: &Pubkey, diff --git a/commons/tests/test_swap.rs b/commons/tests/test_swap.rs index fdf9dd59..0c782315 100644 --- a/commons/tests/test_swap.rs +++ b/commons/tests/test_swap.rs @@ -1,15 +1,13 @@ mod helpers; -use anchor_lang::prelude::*; -use anchor_lang::solana_program::{instruction::Instruction, pubkey::Pubkey}; -use anchor_lang::InstructionData; -use anchor_lang::ToAccountMetas; +use anchor_client::anchor_lang::AccountDeserialize; use anchor_spl::token::{spl_token, TokenAccount}; +use commons::derive_event_authority_pda; +use dlmm_interface::{BinArrayAccount, LbPairAccount, SwapIxArgs, SWAP_IX_ACCOUNTS_LEN}; use helpers::*; -use lb_clmm::state::bin::BinArray; -use lb_clmm::state::lb_pair::LbPair; -use lb_clmm::utils::pda::derive_event_authority_pda; use solana_program_test::*; +use solana_sdk::instruction::{AccountMeta, Instruction}; use solana_sdk::native_token::LAMPORTS_PER_SOL; +use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Signer; use std::collections::HashMap; use std::str::FromStr; @@ -20,7 +18,7 @@ use utils::*; async fn test_swap() { let mut test = ProgramTest::default(); test.prefer_bpf(true); - test.add_program("./tests/artifacts/lb_clmm_prod", lb_clmm::id(), None); + test.add_program("./tests/artifacts/lb_clmm_prod", dlmm_interface::id(), None); let lb_pair = Pubkey::from_str("EtAdVRLFH22rjWh3mcUasKFF27WtHhsaCvK27tPFFWig").unwrap(); let reserve_x = Pubkey::from_str("BmW4cCRpJwwL8maFB1AoAuEQf96t64Eq5gUvXikZardM").unwrap(); @@ -34,28 +32,28 @@ async fn test_swap() { test.add_account_with_file_data( lb_pair, 10 * LAMPORTS_PER_SOL, - lb_clmm::id(), + dlmm_interface::id(), "EtAdVRLFH22rjWh3mcUasKFF27WtHhsaCvK27tPFFWig/lb_pair.bin", ); test.add_account_with_file_data( oracle, 10 * LAMPORTS_PER_SOL, - lb_clmm::id(), + dlmm_interface::id(), "EtAdVRLFH22rjWh3mcUasKFF27WtHhsaCvK27tPFFWig/oracle.bin", ); test.add_account_with_file_data( bin_array_1, 10 * LAMPORTS_PER_SOL, - lb_clmm::id(), + dlmm_interface::id(), "EtAdVRLFH22rjWh3mcUasKFF27WtHhsaCvK27tPFFWig/bin_array_1.bin", ); test.add_account_with_file_data( bin_array_2, 10 * LAMPORTS_PER_SOL, - lb_clmm::id(), + dlmm_interface::id(), "EtAdVRLFH22rjWh3mcUasKFF27WtHhsaCvK27tPFFWig/bin_array_2.bin", ); @@ -94,21 +92,37 @@ async fn test_swap() { let (event_authority, _bump) = derive_event_authority_pda(); - let lb_pair_state: LbPair = banks_client - .get_account_with_anchor_seder(lb_pair) + let lb_pair_account = banks_client + .get_account(lb_pair) .await + .ok() + .flatten() .unwrap(); - let bin_array_1_state: BinArray = banks_client - .get_account_with_anchor_seder(bin_array_1) + let lb_pair_state = LbPairAccount::deserialize(&lb_pair_account.data).unwrap().0; + + let bin_array_1_account = banks_client + .get_account(bin_array_1) .await + .ok() + .flatten() .unwrap(); - let bin_array_2_state: BinArray = banks_client - .get_account_with_anchor_seder(bin_array_2) + let bin_array_1_state = BinArrayAccount::deserialize(&bin_array_1_account.data) + .unwrap() + .0; + + let bin_array_2_account = banks_client + .get_account(bin_array_2) .await + .ok() + .flatten() .unwrap(); + let bin_array_2_state = BinArrayAccount::deserialize(&bin_array_2_account.data) + .unwrap() + .0; + let mut bin_arrays = HashMap::new(); bin_arrays.insert(bin_array_1, bin_array_1_state); bin_arrays.insert(bin_array_2, bin_array_2_state); @@ -129,52 +143,66 @@ async fn test_swap() { println!("quote_result {:?}", quote_result); - let user_token_out_state_before: TokenAccount = banks_client - .get_account_with_anchor_seder(user_token_out) + let user_token_out_account_before = banks_client + .get_account(user_token_out) .await + .ok() + .flatten() .unwrap(); - let mut accounts = lb_clmm::accounts::Swap { + let user_token_out_state_before = + TokenAccount::try_deserialize(&mut user_token_out_account_before.data.as_ref()).unwrap(); + + let main_accounts: [AccountMeta; SWAP_IX_ACCOUNTS_LEN] = dlmm_interface::SwapKeys { lb_pair, oracle, - bin_array_bitmap_extension: None, + bin_array_bitmap_extension: dlmm_interface::ID, reserve_x, reserve_y, user_token_in, user_token_out, token_x_mint, token_y_mint, - host_fee_in: None, + host_fee_in: dlmm_interface::ID, user: payer.pubkey(), token_x_program: spl_token::id(), token_y_program: spl_token::id(), - program: lb_clmm::id(), + program: dlmm_interface::id(), event_authority, } - .to_account_metas(None); + .into(); + + let mut all_accounts = main_accounts.to_vec(); + let mut remaining_accounts = vec![ AccountMeta::new(bin_array_1, false), AccountMeta::new(bin_array_2, false), ]; - accounts.append(&mut remaining_accounts); + all_accounts.append(&mut remaining_accounts); let swap_ix = Instruction { - program_id: lb_clmm::id(), - accounts, - data: lb_clmm::instruction::Swap { + program_id: dlmm_interface::id(), + accounts: all_accounts, + data: dlmm_interface::SwapIxData(SwapIxArgs { amount_in, min_amount_out: 0, - } - .data(), + }) + .try_to_vec() + .unwrap(), }; process_and_assert_ok(&[swap_ix], &payer, &[&payer], &mut banks_client).await; - let user_token_out_state_after: TokenAccount = banks_client - .get_account_with_anchor_seder(user_token_out) + let user_token_out_account_after = banks_client + .get_account(user_token_out) .await + .ok() + .flatten() .unwrap(); + let user_token_out_state_after = + TokenAccount::try_deserialize(&mut user_token_out_account_after.data.as_ref()).unwrap(); + assert_eq!( user_token_out_state_after.amount - user_token_out_state_before.amount, quote_result.amount_out diff --git a/dlmm_interface/Cargo.toml b/dlmm_interface/Cargo.toml index 4b24e219..075ebb3a 100644 --- a/dlmm_interface/Cargo.toml +++ b/dlmm_interface/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" version = "^0.10" [dependencies.bytemuck] -features = ["derive"] +features = ["derive", "min_const_generics"] version = "^1.0" [dependencies.num-derive] diff --git a/dlmm_interface/src/accounts.rs b/dlmm_interface/src/accounts.rs index 5281f2db..df06a329 100644 --- a/dlmm_interface/src/accounts.rs +++ b/dlmm_interface/src/accounts.rs @@ -1,17 +1,8 @@ +use crate::*; use borsh::{BorshDeserialize, BorshSerialize}; use bytemuck::{Pod, Zeroable}; use solana_program::pubkey::Pubkey; -use crate::*; -pub const BIN_ARRAY_BITMAP_EXTENSION_ACCOUNT_DISCM: [u8; 8] = [ - 80, - 111, - 124, - 113, - 55, - 237, - 18, - 5, -]; +pub const BIN_ARRAY_BITMAP_EXTENSION_ACCOUNT_DISCM: [u8; 8] = [80, 111, 124, 113, 55, 237, 18, 5]; #[repr(C)] #[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq, Pod, Copy, Zeroable)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -29,15 +20,13 @@ impl BinArrayBitmapExtensionAccount { let mut maybe_discm = [0u8; 8]; reader.read_exact(&mut maybe_discm)?; if maybe_discm != BIN_ARRAY_BITMAP_EXTENSION_ACCOUNT_DISCM { - return Err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "discm does not match. Expected: {:?}. Received: {:?}", - BIN_ARRAY_BITMAP_EXTENSION_ACCOUNT_DISCM, maybe_discm - ), + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "discm does not match. Expected: {:?}. Received: {:?}", + BIN_ARRAY_BITMAP_EXTENSION_ACCOUNT_DISCM, maybe_discm ), - ); + )); } Ok(Self(BinArrayBitmapExtension::deserialize(&mut reader)?)) } @@ -71,15 +60,13 @@ impl BinArrayAccount { let mut maybe_discm = [0u8; 8]; reader.read_exact(&mut maybe_discm)?; if maybe_discm != BIN_ARRAY_ACCOUNT_DISCM { - return Err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "discm does not match. Expected: {:?}. Received: {:?}", - BIN_ARRAY_ACCOUNT_DISCM, maybe_discm - ), + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "discm does not match. Expected: {:?}. Received: {:?}", + BIN_ARRAY_ACCOUNT_DISCM, maybe_discm ), - ); + )); } Ok(Self(BinArray::deserialize(&mut reader)?)) } @@ -122,15 +109,13 @@ impl PositionV3Account { let mut maybe_discm = [0u8; 8]; reader.read_exact(&mut maybe_discm)?; if maybe_discm != POSITION_V3_ACCOUNT_DISCM { - return Err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "discm does not match. Expected: {:?}. Received: {:?}", - POSITION_V3_ACCOUNT_DISCM, maybe_discm - ), + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "discm does not match. Expected: {:?}. Received: {:?}", + POSITION_V3_ACCOUNT_DISCM, maybe_discm ), - ); + )); } Ok(Self(PositionV3::deserialize(&mut reader)?)) } @@ -192,15 +177,13 @@ impl LbPairAccount { let mut maybe_discm = [0u8; 8]; reader.read_exact(&mut maybe_discm)?; if maybe_discm != LB_PAIR_ACCOUNT_DISCM { - return Err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "discm does not match. Expected: {:?}. Received: {:?}", - LB_PAIR_ACCOUNT_DISCM, maybe_discm - ), + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "discm does not match. Expected: {:?}. Received: {:?}", + LB_PAIR_ACCOUNT_DISCM, maybe_discm ), - ); + )); } Ok(Self(LbPair::deserialize(&mut reader)?)) } @@ -232,15 +215,13 @@ impl OracleAccount { let mut maybe_discm = [0u8; 8]; reader.read_exact(&mut maybe_discm)?; if maybe_discm != ORACLE_ACCOUNT_DISCM { - return Err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "discm does not match. Expected: {:?}. Received: {:?}", - ORACLE_ACCOUNT_DISCM, maybe_discm - ), + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "discm does not match. Expected: {:?}. Received: {:?}", + ORACLE_ACCOUNT_DISCM, maybe_discm ), - ); + )); } Ok(Self(Oracle::deserialize(&mut reader)?)) } @@ -281,15 +262,13 @@ impl PositionAccount { let mut maybe_discm = [0u8; 8]; reader.read_exact(&mut maybe_discm)?; if maybe_discm != POSITION_ACCOUNT_DISCM { - return Err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "discm does not match. Expected: {:?}. Received: {:?}", - POSITION_ACCOUNT_DISCM, maybe_discm - ), + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "discm does not match. Expected: {:?}. Received: {:?}", + POSITION_ACCOUNT_DISCM, maybe_discm ), - ); + )); } Ok(Self(Position::deserialize(&mut reader)?)) } @@ -334,15 +313,13 @@ impl PositionV2Account { let mut maybe_discm = [0u8; 8]; reader.read_exact(&mut maybe_discm)?; if maybe_discm != POSITION_V2_ACCOUNT_DISCM { - return Err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "discm does not match. Expected: {:?}. Received: {:?}", - POSITION_V2_ACCOUNT_DISCM, maybe_discm - ), + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "discm does not match. Expected: {:?}. Received: {:?}", + POSITION_V2_ACCOUNT_DISCM, maybe_discm ), - ); + )); } Ok(Self(PositionV2::deserialize(&mut reader)?)) } @@ -356,16 +333,7 @@ impl PositionV2Account { Ok(data) } } -pub const PRESET_PARAMETER2_ACCOUNT_DISCM: [u8; 8] = [ - 171, - 236, - 148, - 115, - 162, - 113, - 222, - 174, -]; +pub const PRESET_PARAMETER2_ACCOUNT_DISCM: [u8; 8] = [171, 236, 148, 115, 162, 113, 222, 174]; #[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PresetParameter2 { @@ -391,15 +359,13 @@ impl PresetParameter2Account { let mut maybe_discm = [0u8; 8]; reader.read_exact(&mut maybe_discm)?; if maybe_discm != PRESET_PARAMETER2_ACCOUNT_DISCM { - return Err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "discm does not match. Expected: {:?}. Received: {:?}", - PRESET_PARAMETER2_ACCOUNT_DISCM, maybe_discm - ), + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "discm does not match. Expected: {:?}. Received: {:?}", + PRESET_PARAMETER2_ACCOUNT_DISCM, maybe_discm ), - ); + )); } Ok(Self(PresetParameter2::deserialize(&mut reader)?)) } @@ -413,16 +379,7 @@ impl PresetParameter2Account { Ok(data) } } -pub const PRESET_PARAMETER_ACCOUNT_DISCM: [u8; 8] = [ - 242, - 62, - 244, - 34, - 181, - 112, - 58, - 170, -]; +pub const PRESET_PARAMETER_ACCOUNT_DISCM: [u8; 8] = [242, 62, 244, 34, 181, 112, 58, 170]; #[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PresetParameter { @@ -446,15 +403,13 @@ impl PresetParameterAccount { let mut maybe_discm = [0u8; 8]; reader.read_exact(&mut maybe_discm)?; if maybe_discm != PRESET_PARAMETER_ACCOUNT_DISCM { - return Err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "discm does not match. Expected: {:?}. Received: {:?}", - PRESET_PARAMETER_ACCOUNT_DISCM, maybe_discm - ), + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "discm does not match. Expected: {:?}. Received: {:?}", + PRESET_PARAMETER_ACCOUNT_DISCM, maybe_discm ), - ); + )); } Ok(Self(PresetParameter::deserialize(&mut reader)?)) } @@ -485,15 +440,13 @@ impl TokenBadgeAccount { let mut maybe_discm = [0u8; 8]; reader.read_exact(&mut maybe_discm)?; if maybe_discm != TOKEN_BADGE_ACCOUNT_DISCM { - return Err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "discm does not match. Expected: {:?}. Received: {:?}", - TOKEN_BADGE_ACCOUNT_DISCM, maybe_discm - ), + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "discm does not match. Expected: {:?}. Received: {:?}", + TOKEN_BADGE_ACCOUNT_DISCM, maybe_discm ), - ); + )); } Ok(Self(TokenBadge::deserialize(&mut reader)?)) } diff --git a/market_making/Cargo.toml b/market_making/Cargo.toml index 678c368b..cfa99375 100644 --- a/market_making/Cargo.toml +++ b/market_making/Cargo.toml @@ -6,23 +6,22 @@ edition = "2021" authors = ["andrew "] [dependencies] -tokio = {workspace=true, features = ["full"] } -hyper = { workspace=true, features = ["full"] } -routerify = {workspace=true} -ureq = { workspace=true, features = ["json"]} -anchor-client={workspace=true, features=["async"]} -anchor-spl={workspace=true} -anchor-lang={workspace=true} -env_logger={workspace=true} -log={workspace=true} -clap = { workspace=true, features = ["derive"] } -shellexpand = {workspace=true} -anyhow = {workspace=true} -lb_clmm = { path = "../programs/lb_clmm", features = ["cpi"] } -serde_json = {workspace=true} -serde = { workspace=true, features = ["derive"] } -spl-associated-token-account = {workspace=true} -solana-transaction-status={workspace=true} -bs58 = {workspace=true} -chrono={workspace=true} - +tokio = { workspace = true, features = ["full"] } +hyper = { workspace = true, features = ["full"] } +routerify = { workspace = true } +ureq = { workspace = true, features = ["json"] } +anchor-client = { workspace = true, features = ["async"] } +anchor-spl = { workspace = true } +anchor-lang = { workspace = true } +env_logger = { workspace = true } +log = { workspace = true } +clap = { workspace = true, features = ["derive"] } +shellexpand = { workspace = true } +anyhow = { workspace = true } +dlmm_interface = { path = "../dlmm_interface" } +serde_json = { workspace = true } +serde = { workspace = true, features = ["derive"] } +spl-associated-token-account = { workspace = true } +solana-transaction-status = { workspace = true } +bs58 = { workspace = true } +chrono = { workspace = true }