diff --git a/Cargo.lock b/Cargo.lock index cf960c40..452a5f79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -868,6 +868,7 @@ dependencies = [ "const-crypto", "damm-v2", "dynamic-amm", + "dynamic-vault", "locker", "mpl-token-metadata", "num", @@ -887,6 +888,13 @@ dependencies = [ "dynamic-bonding-curve", ] +[[package]] +name = "dynamic-vault" +version = "0.0.1" +dependencies = [ + "anchor-lang", +] + [[package]] name = "either" version = "1.15.0" diff --git a/libs/dynamic-vault/Cargo.toml b/libs/dynamic-vault/Cargo.toml new file mode 100644 index 00000000..3e44e19f --- /dev/null +++ b/libs/dynamic-vault/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "dynamic-vault" +version = "0.0.1" +authors = [""] +edition = "2021" + +[dependencies] +anchor-lang = { workspace = true } diff --git a/libs/dynamic-vault/src/lib.rs b/libs/dynamic-vault/src/lib.rs new file mode 100644 index 00000000..607c165a --- /dev/null +++ b/libs/dynamic-vault/src/lib.rs @@ -0,0 +1,5 @@ +use anchor_lang::prelude::*; + +declare_program!(dynamic_vault); + +pub use dynamic_vault::*; diff --git a/package.json b/package.json index 338bc863..a93d16ce 100644 --- a/package.json +++ b/package.json @@ -10,16 +10,17 @@ "devDependencies": { "@coral-xyz/anchor-errors": "^0.31.0", "@solana/spl-token": "^0.4.8", + "@solana/spl-token-metadata": "^0.1.6", "@solana/web3.js": "^1.95.3", "@types/bn.js": "^5.1.0", "@types/chai": "^4.3.0", "@types/mocha": "^9.0.0", "chai": "^4.3.4", + "decimal.js": "^10.4.2", "mocha": "^9.0.3", "prettier": "^2.6.2", - "ts-mocha": "^10.0.0", - "typescript": "^4.3.5", "solana-bankrun": "^0.4.0", - "decimal.js": "^10.4.2" + "ts-mocha": "^10.0.0", + "typescript": "^4.3.5" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0e082fb..14a99a73 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,6 +18,9 @@ importers: '@solana/spl-token': specifier: ^0.4.8 version: 0.4.13(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@solana/spl-token-metadata': + specifier: ^0.1.6 + version: 0.1.6(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) '@solana/web3.js': specifier: ^1.95.3 version: 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) diff --git a/programs/dynamic-bonding-curve/Cargo.toml b/programs/dynamic-bonding-curve/Cargo.toml index 6501d963..057fb7e6 100644 --- a/programs/dynamic-bonding-curve/Cargo.toml +++ b/programs/dynamic-bonding-curve/Cargo.toml @@ -32,6 +32,7 @@ spl-token-metadata-interface = "0.6" dynamic-amm = { path = "../../libs/dynamic-amm" } damm-v2 = { path = "../../libs/damm-v2" } locker = { path = "../../libs/locker" } +dynamic-vault = { path = "../../libs/dynamic-vault" } [dev-dependencies] -proptest = "1.2.0" \ No newline at end of file +proptest = "1.2.0" diff --git a/programs/dynamic-bonding-curve/src/error.rs b/programs/dynamic-bonding-curve/src/error.rs index 6cdab922..c89cfb6e 100644 --- a/programs/dynamic-bonding-curve/src/error.rs +++ b/programs/dynamic-bonding-curve/src/error.rs @@ -112,4 +112,7 @@ pub enum PoolError { #[msg("Invalid creator trading fee percentage")] InvalidCreatorTradingFeePercentage, + + #[msg("Invalid migration accounts")] + InvalidMigrationAccounts, } diff --git a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_claim_lp_token.rs b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_claim_lp_token.rs index cdf59739..271ad447 100644 --- a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_claim_lp_token.rs +++ b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_claim_lp_token.rs @@ -1,5 +1,3 @@ -use std::u64; - use crate::{ const_pda, state::{MigrationProgress, VirtualPool}, @@ -52,7 +50,7 @@ pub struct MigrateMeteoraDammClaimLpTokenCtx<'info> { } impl<'info> MigrateMeteoraDammClaimLpTokenCtx<'info> { - fn transfer(&self, bump: u8, amount: u64) -> Result<()> { + pub fn transfer(&self, bump: u8, amount: u64) -> Result<()> { let pool_authority_seeds = pool_authority_seeds!(bump); transfer( @@ -86,10 +84,14 @@ pub fn handle_migrate_meteora_damm_claim_lp_token<'info>( let is_partner = ctx.accounts.owner.key() == migration_metadata.partner; let is_creator = ctx.accounts.owner.key() == migration_metadata.pool_creator; + let mut versioned_migration_metadata = migration_metadata.get_versioned_migration_metadata()?; + let lp_to_claim = match (is_partner, is_creator) { - (true, true) => migration_metadata.claim_as_self_partnered_creator()?, - (true, false) => migration_metadata.claim_as_partner()?, - (false, true) => migration_metadata.claim_as_creator()?, + (true, true) => { + versioned_migration_metadata.validate_and_claim_as_self_partnered_creator()? + } + (true, false) => versioned_migration_metadata.validate_and_claim_as_partner()?, + (false, true) => versioned_migration_metadata.validate_and_claim_as_creator()?, (false, false) => return Err(PoolError::InvalidOwnerAccount.into()), }; diff --git a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_lock_lp_token.rs b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_lock_lp_token.rs index 927462b5..dab16286 100644 --- a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_lock_lp_token.rs +++ b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_lock_lp_token.rs @@ -1,12 +1,12 @@ -use std::u64; - use crate::{ const_pda, state::{MigrationProgress, VirtualPool}, *, }; -use anchor_spl::token::{Token, TokenAccount}; +use anchor_spl::token::{Mint, Token, TokenAccount}; use dynamic_amm::accounts::LockEscrow; +use dynamic_amm::accounts::Pool; +use dynamic_vault::accounts::Vault; /// create lock escrow must be before that transaction #[derive(Accounts)] @@ -25,16 +25,23 @@ pub struct MigrateMeteoraDammLockLpTokenCtx<'info> { pub pool_authority: AccountInfo<'info>, /// CHECK: pool - #[account(mut)] - pub pool: UncheckedAccount<'info>, + #[account( + mut, + has_one = lp_mint @ PoolError::InvalidMigrationAccounts, + has_one = a_vault @ PoolError::InvalidMigrationAccounts, + has_one = b_vault @ PoolError::InvalidMigrationAccounts, + has_one = a_vault_lp @ PoolError::InvalidMigrationAccounts, + has_one = b_vault_lp @ PoolError::InvalidMigrationAccounts, + )] + pub pool: Box>, /// CHECK: lp_mint - pub lp_mint: UncheckedAccount<'info>, + pub lp_mint: Box>, #[account( mut, - has_one=pool, - has_one=owner, + has_one = pool, + has_one = owner, )] pub lock_escrow: Box>, @@ -57,18 +64,24 @@ pub struct MigrateMeteoraDammLockLpTokenCtx<'info> { #[account(address = dynamic_amm::ID)] pub amm_program: UncheckedAccount<'info>, - /// CHECK: Vault account for token a. token a of the pool will be deposit / withdraw from this vault account. - pub a_vault: UncheckedAccount<'info>, - /// CHECK: Vault account for token b. token b of the pool will be deposit / withdraw from this vault account. - pub b_vault: UncheckedAccount<'info>, - /// CHECK: LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault. - pub a_vault_lp: UncheckedAccount<'info>, - /// CHECK: LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault. - pub b_vault_lp: UncheckedAccount<'info>, - /// CHECK: LP token mint of vault a - pub a_vault_lp_mint: UncheckedAccount<'info>, - /// CHECK: LP token mint of vault b - pub b_vault_lp_mint: UncheckedAccount<'info>, + /// Vault account for token a. token a of the pool will be deposit / withdraw from this vault account. + pub a_vault: Box>, + /// Vault account for token b. token b of the pool will be deposit / withdraw from this vault account. + pub b_vault: Box>, + /// LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault. + #[account( + token::mint = a_vault_lp_mint.key() + )] + pub a_vault_lp: Box>, + /// LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault. + #[account( + token::mint = b_vault_lp_mint.key() + )] + pub b_vault_lp: Box>, + /// LP token mint of vault a + pub a_vault_lp_mint: Box>, + /// LP token mint of vault b + pub b_vault_lp_mint: Box>, /// token_program pub token_program: Program<'info, Token>, @@ -119,10 +132,27 @@ pub fn handle_migrate_meteora_damm_lock_lp_token<'info>( let is_partner = ctx.accounts.owner.key() == migration_metadata.partner; let is_creator = ctx.accounts.owner.key() == migration_metadata.pool_creator; + let damm_migration_accounts = DammAccounts { + lp_mint: &ctx.accounts.lp_mint, + a_vault: &ctx.accounts.a_vault, + b_vault: &ctx.accounts.b_vault, + a_vault_lp: &ctx.accounts.a_vault_lp, + b_vault_lp: &ctx.accounts.b_vault_lp, + a_vault_lp_mint: &ctx.accounts.a_vault_lp_mint, + b_vault_lp_mint: &ctx.accounts.b_vault_lp_mint, + }; + + let mut version_migration_metadata = migration_metadata.get_versioned_migration_metadata()?; + let lp_to_lock = match (is_partner, is_creator) { - (true, true) => migration_metadata.lock_as_self_partnered_creator()?, - (true, false) => migration_metadata.lock_as_partner()?, - (false, true) => migration_metadata.lock_as_creator()?, + (true, true) => version_migration_metadata + .validate_and_lock_as_self_partnered_creator(damm_migration_accounts)?, + (true, false) => { + version_migration_metadata.validate_and_lock_as_partner(damm_migration_accounts)? + } + (false, true) => { + version_migration_metadata.validate_and_lock_as_creator(damm_migration_accounts)? + } (false, false) => return Err(PoolError::InvalidOwnerAccount.into()), }; diff --git a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_metadata_state.rs b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_metadata_state.rs index ad4462e5..c4033acc 100644 --- a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_metadata_state.rs +++ b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/meteora_damm_metadata_state.rs @@ -1,7 +1,28 @@ -use crate::{safe_math::SafeMath, state::LiquidityDistributionU64, PoolError}; +use crate::{ + safe_math::SafeMath, state::LiquidityDistributionU64, utils_math::safe_mul_div_cast_u128, + PoolError, +}; use anchor_lang::prelude::*; +use anchor_spl::token::{Mint, TokenAccount}; +use dynamic_vault::accounts::Vault; +use num::Zero; use static_assertions::const_assert_eq; +use super::utils::{ + damm_utils::{calculate_constant_product_virtual_price, BASE_VIRTUAL_PRICE}, + vault_utils::get_amount_by_share, +}; + +use num_enum::{FromPrimitive, IntoPrimitive}; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, FromPrimitive)] +enum Version { + V0, + #[default] + V1, +} + #[account(zero_copy)] #[derive(InitSpace, Debug)] pub struct MeteoraDammMigrationMetadata { @@ -21,8 +42,8 @@ pub struct MeteoraDammMigrationMetadata { pub creator_locked_lp: u64, /// creator lp pub creator_lp: u64, - /// padding - pub _padding_0: u8, + /// Version + pub version: u8, /// flag to check whether lp is locked for creator pub creator_locked_status: u8, /// flag to check whether lp is locked for partner @@ -31,12 +52,250 @@ pub struct MeteoraDammMigrationMetadata { pub creator_claim_status: u8, /// flag to check whether partner has claimed lp token pub partner_claim_status: u8, + pub _padding_1: [u8; 3], + /// actual creator locked lp + pub actual_creator_locked_lp: u64, + /// actual partner locked lp + pub actual_partner_locked_lp: u64, /// Reserve - pub _padding: [u8; 107], + pub _padding: [u8; 88], } const_assert_eq!(MeteoraDammMigrationMetadata::INIT_SPACE, 272); +struct MigrationMetadataV1<'a> { + inner: &'a mut MeteoraDammMigrationMetadata, +} + +impl<'a> MigrationMetadataImplementation<'a> for MigrationMetadataV1<'a> { + fn validate_and_lock_as_creator(&mut self, _accounts: DammAccounts<'_, '_>) -> Result { + self.inner.common_validate_creator_lock_lp_action()?; + self.inner.set_creator_lock_status(); + Ok(self.inner.creator_locked_lp) + } + + fn validate_and_lock_as_partner(&mut self, _accounts: DammAccounts<'_, '_>) -> Result { + self.inner.common_validate_partner_lock_lp_action()?; + self.inner.set_partner_lock_status(); + Ok(self.inner.partner_locked_lp) + } + + fn validate_and_lock_as_self_partnered_creator( + &mut self, + _accounts: DammAccounts<'_, '_>, + ) -> Result { + self.inner + .common_validate_self_partnered_creator_lock_lp_action()?; + self.inner.set_creator_lock_status(); + self.inner.set_partner_lock_status(); + let total_lp_to_lock = self + .inner + .creator_locked_lp + .safe_add(self.inner.partner_locked_lp)?; + Ok(total_lp_to_lock) + } + + fn validate_and_claim_as_self_partnered_creator(&mut self) -> Result { + self.inner + .common_validate_self_partnered_creator_claim_lp_action()?; + self.inner.set_creator_claim_status(); + self.inner.set_partner_claim_status(); + let total_lp_to_claim = self.inner.creator_lp.safe_add(self.inner.partner_lp)?; + require!(total_lp_to_claim != 0, PoolError::NotPermitToDoThisAction); + Ok(total_lp_to_claim) + } + + fn validate_and_claim_as_creator(&mut self) -> Result { + self.inner.common_validate_creator_claim_lp_action()?; + self.inner.set_creator_claim_status(); + let total_lp_to_claim = self.inner.creator_lp; + require!(total_lp_to_claim != 0, PoolError::NotPermitToDoThisAction); + Ok(total_lp_to_claim) + } + + fn validate_and_claim_as_partner(&mut self) -> Result { + self.inner.common_validate_partner_claim_lp_action()?; + self.inner.set_partner_claim_status(); + let total_lp_to_claim = self.inner.partner_lp; + require!(total_lp_to_claim != 0, PoolError::NotPermitToDoThisAction); + Ok(total_lp_to_claim) + } +} + +struct MigrationMetadataV2<'a> { + inner: &'a mut MeteoraDammMigrationMetadata, +} + +impl<'a> MigrationMetadataImplementation<'a> for MigrationMetadataV2<'a> { + fn validate_and_lock_as_creator(&mut self, accounts: DammAccounts<'_, '_>) -> Result { + self.inner.common_validate_creator_lock_lp_action()?; + self.inner.set_creator_lock_status(); + let actual_lock_amount = exclude_fee_lp_amount(self.inner.creator_locked_lp, accounts)?; + self.inner.actual_creator_locked_lp = actual_lock_amount; + Ok(actual_lock_amount) + } + + fn validate_and_lock_as_partner(&mut self, accounts: DammAccounts<'_, '_>) -> Result { + self.inner.common_validate_partner_lock_lp_action()?; + self.inner.set_partner_lock_status(); + let actual_lock_amount = exclude_fee_lp_amount(self.inner.partner_locked_lp, accounts)?; + self.inner.actual_partner_locked_lp = actual_lock_amount; + Ok(actual_lock_amount) + } + + fn validate_and_lock_as_self_partnered_creator( + &mut self, + accounts: DammAccounts<'_, '_>, + ) -> Result { + self.inner + .common_validate_self_partnered_creator_lock_lp_action()?; + + self.inner.set_creator_lock_status(); + self.inner.set_partner_lock_status(); + + let total_lp_to_lock = self + .inner + .creator_locked_lp + .safe_add(self.inner.partner_locked_lp)?; + + // We still set lock status to true even amount is 0 + if total_lp_to_lock.is_zero() { + return Ok(0); + } + + let actual_lock_amount = exclude_fee_lp_amount(total_lp_to_lock, accounts)?; + + self.inner.actual_partner_locked_lp = actual_lock_amount + .safe_mul(self.inner.partner_locked_lp)? + .safe_div(total_lp_to_lock)?; + + self.inner.actual_creator_locked_lp = + actual_lock_amount.safe_sub(self.inner.actual_partner_locked_lp)?; + + Ok(actual_lock_amount) + } + + fn validate_and_claim_as_self_partnered_creator(&mut self) -> Result { + self.inner + .common_validate_self_partnered_creator_claim_lp_action()?; + + let total_lp_to_lock = self + .inner + .creator_locked_lp + .safe_add(self.inner.partner_locked_lp)?; + + let eligible_to_claim = self.inner.is_creator_lp_locked() + && self.inner.is_partner_lp_locked() + || total_lp_to_lock.is_zero(); + + // Must lock first to know actual locked lp amount + require!(eligible_to_claim, PoolError::NotPermitToDoThisAction); + + self.inner.set_creator_claim_status(); + self.inner.set_partner_claim_status(); + + let suppose_lp_to_claim = self.inner.creator_lp.safe_add(self.inner.partner_lp)?; + + let suppose_total_locked_lp = self + .inner + .creator_locked_lp + .safe_add(self.inner.partner_locked_lp)?; + + let actual_total_locked_lp = self + .inner + .actual_creator_locked_lp + .safe_add(self.inner.actual_partner_locked_lp)?; + + let non_locked_fee_lp_to_claim = + suppose_total_locked_lp.safe_sub(actual_total_locked_lp)?; + + let actual_lp_to_claim = suppose_lp_to_claim.safe_add(non_locked_fee_lp_to_claim)?; + + require!(actual_lp_to_claim != 0, PoolError::NotPermitToDoThisAction); + + Ok(actual_lp_to_claim) + } + + fn validate_and_claim_as_creator(&mut self) -> Result { + self.inner.common_validate_creator_claim_lp_action()?; + + let eligible_to_claim = + self.inner.is_creator_lp_locked() || self.inner.creator_locked_lp.is_zero(); + + // Must lock first to know actual locked lp amount + require!(eligible_to_claim, PoolError::NotPermitToDoThisAction); + + self.inner.set_creator_claim_status(); + + let non_locked_fee_lp_to_claim = self + .inner + .creator_locked_lp + .safe_sub(self.inner.actual_creator_locked_lp)?; + + let actual_lp_to_claim = self.inner.creator_lp.safe_add(non_locked_fee_lp_to_claim)?; + + require!(actual_lp_to_claim != 0, PoolError::NotPermitToDoThisAction); + + Ok(actual_lp_to_claim) + } + + fn validate_and_claim_as_partner(&mut self) -> Result { + self.inner.common_validate_partner_claim_lp_action()?; + + let eligible_to_claim = + self.inner.is_partner_lp_locked() || self.inner.partner_locked_lp.is_zero(); + + // Must lock first to know actual locked lp amount + require!(eligible_to_claim, PoolError::NotPermitToDoThisAction); + + self.inner.set_partner_claim_status(); + + let non_locked_fee_lp_to_claim = self + .inner + .partner_locked_lp + .safe_sub(self.inner.actual_partner_locked_lp)?; + + let actual_lp_to_claim = self.inner.partner_lp.safe_add(non_locked_fee_lp_to_claim)?; + + require!(actual_lp_to_claim != 0, PoolError::NotPermitToDoThisAction); + + Ok(actual_lp_to_claim) + } +} + +pub trait MigrationMetadataImplementation<'a> { + fn validate_and_lock_as_creator(&mut self, accounts: DammAccounts<'_, '_>) -> Result; + fn validate_and_lock_as_partner(&mut self, accounts: DammAccounts<'_, '_>) -> Result; + fn validate_and_lock_as_self_partnered_creator( + &mut self, + accounts: DammAccounts<'_, '_>, + ) -> Result; + fn validate_and_claim_as_self_partnered_creator(&mut self) -> Result; + fn validate_and_claim_as_creator(&mut self) -> Result; + fn validate_and_claim_as_partner(&mut self) -> Result; +} + impl MeteoraDammMigrationMetadata { + pub fn get_versioned_migration_metadata<'a>( + &'a mut self, + ) -> Result + 'a>> { + let versioned_migration_metadata: Box + 'a> = + match Version::from(self.version) { + Version::V0 => Box::new(MigrationMetadataV1 { inner: self }), + Version::V1 => Box::new(MigrationMetadataV2 { inner: self }), + #[allow(unreachable_patterns)] + _ => unreachable!("Invalid migration metadata version"), + }; + + Ok(versioned_migration_metadata) + } + + pub fn initialize(&mut self, virtual_pool: Pubkey, creator: Pubkey, partner: Pubkey) { + self.virtual_pool = virtual_pool; + self.pool_creator = creator; + self.partner = partner; + self.version = Version::V1.into(); + } + pub fn set_lp_minted(&mut self, lp_mint: Pubkey, lp_distribution: &LiquidityDistributionU64) { self.lp_mint = lp_mint; let &LiquidityDistributionU64 { @@ -51,11 +310,7 @@ impl MeteoraDammMigrationMetadata { self.creator_lp = creator_lp; } - pub fn set_creator_lock_status(&mut self) { - self.creator_locked_status = 1; - } - - pub fn lock_as_creator(&mut self) -> Result { + fn common_validate_creator_lock_lp_action(&self) -> Result<()> { require!( !self.is_creator_lp_locked(), PoolError::NotPermitToDoThisAction @@ -65,12 +320,10 @@ impl MeteoraDammMigrationMetadata { PoolError::NotPermitToDoThisAction ); - self.set_creator_lock_status(); - - Ok(self.creator_locked_lp) + Ok(()) } - pub fn lock_as_partner(&mut self) -> Result { + fn common_validate_partner_lock_lp_action(&self) -> Result<()> { require!( !self.is_partner_lp_locked(), PoolError::NotPermitToDoThisAction @@ -80,90 +333,140 @@ impl MeteoraDammMigrationMetadata { PoolError::NotPermitToDoThisAction ); - self.set_partner_lock_status(); - - Ok(self.partner_locked_lp) + Ok(()) } - pub fn lock_as_self_partnered_creator(&mut self) -> Result { + fn common_validate_self_partnered_creator_lock_lp_action(&self) -> Result<()> { require!( !self.is_creator_lp_locked() && !self.is_partner_lp_locked(), PoolError::NotPermitToDoThisAction ); + require!( + self.creator_locked_lp != 0 || self.partner_locked_lp != 0, + PoolError::NotPermitToDoThisAction + ); - let lp_to_lock = self.partner_locked_lp.safe_add(self.creator_locked_lp)?; - require!(lp_to_lock != 0, PoolError::NotPermitToDoThisAction); - - self.set_creator_lock_status(); - self.set_partner_lock_status(); - - Ok(lp_to_lock) - } - - pub fn set_partner_lock_status(&mut self) { - self.partner_locked_status = 1; + Ok(()) } - pub fn claim_as_creator(&mut self) -> Result { + fn common_validate_creator_claim_lp_action(&self) -> Result<()> { require!( !self.is_creator_claim_lp(), PoolError::NotPermitToDoThisAction ); - require!(self.creator_lp != 0, PoolError::NotPermitToDoThisAction); - self.set_creator_claim_status(); - - Ok(self.creator_lp) + Ok(()) } - pub fn claim_as_partner(&mut self) -> Result { + fn common_validate_self_partnered_creator_claim_lp_action(&self) -> Result<()> { require!( - !self.is_partner_claim_lp(), + !self.is_creator_claim_lp() && !self.is_partner_claim_lp(), PoolError::NotPermitToDoThisAction ); - require!(self.partner_lp != 0, PoolError::NotPermitToDoThisAction); - - self.set_partner_claim_status(); - Ok(self.partner_lp) + Ok(()) } - pub fn claim_as_self_partnered_creator(&mut self) -> Result { + fn common_validate_partner_claim_lp_action(&self) -> Result<()> { require!( - !self.is_creator_claim_lp() && !self.is_partner_claim_lp(), + !self.is_partner_claim_lp(), PoolError::NotPermitToDoThisAction ); - let lp_to_claim = self.partner_lp.safe_add(self.creator_lp)?; - require!(lp_to_claim != 0, PoolError::NotPermitToDoThisAction); - - self.set_creator_claim_status(); - self.set_partner_claim_status(); + Ok(()) + } - Ok(lp_to_claim) + fn set_partner_lock_status(&mut self) { + self.partner_locked_status = 1; } - pub fn set_creator_claim_status(&mut self) { + fn set_creator_claim_status(&mut self) { self.creator_claim_status = 1; } - pub fn set_partner_claim_status(&mut self) { + fn set_partner_claim_status(&mut self) { self.partner_claim_status = 1; } - pub fn is_creator_lp_locked(&self) -> bool { + fn set_creator_lock_status(&mut self) { + self.creator_locked_status = 1; + } + + fn is_creator_lp_locked(&self) -> bool { self.creator_locked_status == 1 } - pub fn is_partner_lp_locked(&self) -> bool { + fn is_partner_lp_locked(&self) -> bool { self.partner_locked_status == 1 } - pub fn is_creator_claim_lp(&self) -> bool { + fn is_creator_claim_lp(&self) -> bool { self.creator_claim_status == 1 } - pub fn is_partner_claim_lp(&self) -> bool { + fn is_partner_claim_lp(&self) -> bool { self.partner_claim_status == 1 } } + +pub struct DammAccounts<'c, 'info> { + pub lp_mint: &'c Account<'info, Mint>, + pub a_vault: &'c Account<'info, Vault>, + pub b_vault: &'c Account<'info, Vault>, + pub a_vault_lp: &'c Account<'info, TokenAccount>, + pub b_vault_lp: &'c Account<'info, TokenAccount>, + pub a_vault_lp_mint: &'c Account<'info, Mint>, + pub b_vault_lp_mint: &'c Account<'info, Mint>, +} + +fn get_damm_virtual_price(accounts: DammAccounts<'_, '_>) -> Result { + let DammAccounts { + lp_mint, + a_vault, + b_vault, + a_vault_lp, + b_vault_lp, + a_vault_lp_mint, + b_vault_lp_mint, + .. + } = accounts; + + let current_time: u64 = Clock::get()? + .unix_timestamp + .try_into() + .map_err(|_| PoolError::MathOverflow)?; + + let token_a_amount = get_amount_by_share( + current_time, + &a_vault, + a_vault_lp.amount, + a_vault_lp_mint.supply, + ) + .ok_or_else(|| PoolError::MathOverflow)?; + + let token_b_amount = get_amount_by_share( + current_time, + &b_vault, + b_vault_lp.amount, + b_vault_lp_mint.supply, + ) + .ok_or_else(|| PoolError::MathOverflow)?; + + let vp = + calculate_constant_product_virtual_price(token_a_amount, token_b_amount, lp_mint.supply) + .ok_or_else(|| PoolError::MathOverflow)?; + + Ok(vp) +} + +fn exclude_fee_lp_amount(lp_to_lock: u64, accounts: DammAccounts<'_, '_>) -> Result { + let vp = get_damm_virtual_price(accounts)?; + + let vp_delta = vp.safe_sub(BASE_VIRTUAL_PRICE)?; + + let fee_lp_amount = safe_mul_div_cast_u128(lp_to_lock.into(), vp_delta, vp)? + .try_into() + .map_err(|_| PoolError::MathOverflow)?; + + Ok(lp_to_lock.safe_sub(fee_lp_amount)?) +} diff --git a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/migration_meteora_damm_create_metadata.rs b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/migration_meteora_damm_create_metadata.rs index 739540b7..b5ff9599 100644 --- a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/migration_meteora_damm_create_metadata.rs +++ b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/migration_meteora_damm_create_metadata.rs @@ -47,9 +47,11 @@ pub fn handle_migration_meteora_damm_create_metadata( PoolError::InvalidMigrationOption ); let mut migration_metadata = ctx.accounts.migration_metadata.load_init()?; - migration_metadata.virtual_pool = ctx.accounts.virtual_pool.key(); - migration_metadata.pool_creator = virtual_pool.creator; - migration_metadata.partner = config.fee_claimer; + migration_metadata.initialize( + ctx.accounts.virtual_pool.key(), + virtual_pool.creator, + config.fee_claimer, + ); emit_cpi!(EvtCreateMeteoraMigrationMetadata { virtual_pool: ctx.accounts.virtual_pool.key(), diff --git a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/mod.rs b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/mod.rs index 1a786413..eb67471b 100644 --- a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/mod.rs +++ b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/mod.rs @@ -8,3 +8,5 @@ pub mod meteora_damm_metadata_state; pub use meteora_damm_metadata_state::*; pub mod meteora_damm_claim_lp_token; pub use meteora_damm_claim_lp_token::*; + +mod utils; diff --git a/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/utils.rs b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/utils.rs new file mode 100644 index 00000000..5e70a26d --- /dev/null +++ b/programs/dynamic-bonding-curve/src/instructions/migration/meteora_damm/utils.rs @@ -0,0 +1,66 @@ +pub mod damm_utils { + use crate::u128x128_math::{shl_div, Rounding}; + use num::integer::Roots; + + const SCALE_OFFSET: u8 = 64; + pub const BASE_VIRTUAL_PRICE: u128 = 1 << SCALE_OFFSET; + + pub fn calculate_constant_product_virtual_price( + token_a_amount: u64, + token_b_amount: u64, + lp_supply: u64, + ) -> Option { + if lp_supply == 0 { + return None; + } + let token_a_amount: u128 = token_a_amount.into(); + let token_b_amount: u128 = token_b_amount.into(); + let k = token_a_amount.checked_mul(token_b_amount)?; + let d = k.sqrt(); + shl_div(d, lp_supply.into(), SCALE_OFFSET, Rounding::Down) + } +} + +pub mod vault_utils { + use dynamic_vault::accounts::Vault; + + const LOCKED_PROFIT_DEGRADATION_DENOMINATOR: u128 = 1_000_000_000_000; + + pub fn get_amount_by_share( + current_time: u64, + vault: &Vault, + lp_amount: u64, + lp_supply: u64, + ) -> Option { + let total_amount = get_unlocked_amount(vault, current_time)?; + u64::try_from( + u128::from(lp_amount) + .checked_mul(u128::from(total_amount))? + .checked_div(u128::from(lp_supply))?, + ) + .ok() + } + + pub fn get_unlocked_amount(vault: &Vault, current_time: u64) -> Option { + vault + .total_amount + .checked_sub(calculate_locked_profit(vault, current_time)?) + } + + pub fn calculate_locked_profit(vault: &Vault, current_time: u64) -> Option { + let locked_profit_tracker = &vault.locked_profit_tracker; + let duration = u128::from(current_time.checked_sub(locked_profit_tracker.last_report)?); + let locked_profit_degradation = u128::from(locked_profit_tracker.locked_profit_degradation); + let locked_fund_ratio = duration.checked_mul(locked_profit_degradation)?; + + if locked_fund_ratio > LOCKED_PROFIT_DEGRADATION_DENOMINATOR { + return Some(0); + } + let locked_profit = u128::from(locked_profit_tracker.last_updated_locked_profit); + + let locked_profit = (locked_profit + .checked_mul(LOCKED_PROFIT_DEGRADATION_DENOMINATOR.checked_sub(locked_fund_ratio)?)?) + .checked_div(LOCKED_PROFIT_DEGRADATION_DENOMINATOR)?; + u64::try_from(locked_profit).ok() + } +} diff --git a/tests/claim_and_lock_lp_on_meteora_damm.tests.ts b/tests/claim_and_lock_lp_on_meteora_damm.tests.ts index 6e372097..5f74ab59 100644 --- a/tests/claim_and_lock_lp_on_meteora_damm.tests.ts +++ b/tests/claim_and_lock_lp_on_meteora_damm.tests.ts @@ -1,9 +1,18 @@ import { + createAssociatedTokenAccountIdempotentInstruction, + createSyncNativeInstruction, getAssociatedTokenAddressSync, NATIVE_MINT, + TOKEN_PROGRAM_ID, unpackAccount, } from "@solana/spl-token"; -import { Keypair, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; +import { + Keypair, + LAMPORTS_PER_SOL, + PublicKey, + SystemProgram, + Transaction, +} from "@solana/web3.js"; import { BN } from "bn.js"; import { expect } from "chai"; import { BanksClient, ProgramTestContext } from "solana-bankrun"; @@ -33,10 +42,12 @@ import { deriveLpMintAddress, derivePoolAuthority, fundSol, + getDynamicVault, MAX_SQRT_PRICE, MIN_SQRT_PRICE, startTest, U64_MAX, + VAULT_PROGRAM_ID, } from "./utils"; import { getConfig, @@ -44,7 +55,134 @@ import { getMeteoraDammMigrationMetadata, getVirtualPool, } from "./utils/fetcher"; -import { VirtualCurveProgram } from "./utils/types"; +import { DammPool, VirtualCurveProgram } from "./utils/types"; + +async function generateSwapFeesOnDamm( + banksClient: BanksClient, + pool: PublicKey, + userKeypair: Keypair +) { + const dammProgram = createDammProgram(); + + const poolAccount = await banksClient.getAccount(pool); + + const poolState: DammPool = dammProgram.coder.accounts.decode( + "pool", + Buffer.from(poolAccount.data) + ); + + const vaultAState = await getDynamicVault(banksClient, poolState.aVault); + const vaultBState = await getDynamicVault(banksClient, poolState.bVault); + + const swapDirection = [1, 0]; + + for (const direction of swapDirection) { + const bToA = direction == 1; + + const [ + userSourceToken, + sourceMint, + userDestinationToken, + destinationMint, + protocolTokenFee, + ] = bToA + ? [ + getAssociatedTokenAddressSync( + poolState.tokenBMint, + userKeypair.publicKey + ), + poolState.tokenBMint, + getAssociatedTokenAddressSync( + poolState.tokenAMint, + userKeypair.publicKey + ), + poolState.tokenAMint, + poolState.protocolTokenBFee, + ] + : [ + getAssociatedTokenAddressSync( + poolState.tokenAMint, + userKeypair.publicKey + ), + poolState.tokenAMint, + getAssociatedTokenAddressSync( + poolState.tokenBMint, + userKeypair.publicKey + ), + poolState.tokenBMint, + poolState.protocolTokenAFee, + ]; + + const initUserSourceIx = createAssociatedTokenAccountIdempotentInstruction( + userKeypair.publicKey, + userSourceToken, + userKeypair.publicKey, + sourceMint, + TOKEN_PROGRAM_ID + ); + + const initUserDestinationIx = + createAssociatedTokenAccountIdempotentInstruction( + userKeypair.publicKey, + userDestinationToken, + userKeypair.publicKey, + destinationMint, + TOKEN_PROGRAM_ID + ); + + const preInstructions = [initUserSourceIx, initUserDestinationIx]; + + let inAmount = new BN(0); + if (bToA) { + inAmount = new BN(100_000_000); + preInstructions.push( + SystemProgram.transfer({ + fromPubkey: userKeypair.publicKey, + toPubkey: userSourceToken, + lamports: BigInt(inAmount.toString()), + }), + createSyncNativeInstruction(userSourceToken) + ); + } else { + const sourceTokenAccount = await banksClient.getAccount(userSourceToken); + const sourceTokenState = unpackAccount( + userSourceToken, + sourceTokenAccount as any + ); + + inAmount = new BN(sourceTokenState.amount.toString()); + } + + const swapIx = await dammProgram.methods + .swap(inAmount, new BN(0)) + .accountsPartial({ + pool, + user: userKeypair.publicKey, + aVault: poolState.aVault, + bVault: poolState.bVault, + aVaultLp: poolState.aVaultLp, + bVaultLp: poolState.bVaultLp, + aTokenVault: vaultAState.tokenVault, + bTokenVault: vaultBState.tokenVault, + aVaultLpMint: vaultAState.lpMint, + bVaultLpMint: vaultBState.lpMint, + userSourceToken, + userDestinationToken, + protocolTokenFee, + tokenProgram: TOKEN_PROGRAM_ID, + vaultProgram: VAULT_PROGRAM_ID, + }) + .instruction(); + + let transaction = new Transaction(); + const [recentBlockhash] = await banksClient.getLatestBlockhash(); + transaction.recentBlockhash = recentBlockhash; + transaction.add(...preInstructions, swapIx); + transaction.sign(userKeypair); + + await banksClient.processTransaction(transaction); + } +} async function createPartnerConfig( payer: Keypair, @@ -129,6 +267,7 @@ async function setupPrerequisite( virtualPool: PublicKey; dammConfig: PublicKey; migrationMetadata: PublicKey; + dammPoolAddress: PublicKey; }> { const virtualPool = await createPoolWithSplToken(banksClient, program, { payer, @@ -175,12 +314,17 @@ async function setupPrerequisite( dammConfig, }; - await migrateToMeteoraDamm(banksClient, program, migrationParams); + const dammPoolAddress = await migrateToMeteoraDamm( + banksClient, + program, + migrationParams + ); return { virtualPool, dammConfig, migrationMetadata, + dammPoolAddress, }; } @@ -231,6 +375,7 @@ describe("Claim and lock lp on meteora dammm", () => { let virtualPool: PublicKey; let dammConfig: PublicKey; let migrationMetadata: PublicKey; + let dammPoolAddress: PublicKey; describe("Self partnered creator", () => { before(async () => { @@ -264,6 +409,7 @@ describe("Claim and lock lp on meteora dammm", () => { dammConfig: innerDammConfig, virtualPool: innerVirtualPool, migrationMetadata: innerMigrationMetadata, + dammPoolAddress: innerDammPoolAddress, } = await setupPrerequisite( context.banksClient, program, @@ -274,9 +420,19 @@ describe("Claim and lock lp on meteora dammm", () => { config ); + const migrationMetadataState = await getMeteoraDammMigrationMetadata( + context.banksClient, + program, + innerMigrationMetadata + ); + expect(migrationMetadataState.version).to.be.equal(1); + dammConfig = innerDammConfig; virtualPool = innerVirtualPool; migrationMetadata = innerMigrationMetadata; + dammPoolAddress = innerDammPoolAddress; + + await generateSwapFeesOnDamm(context.banksClient, dammPoolAddress, user); }); it("Self partnered creator lock LP", async () => { @@ -314,9 +470,21 @@ describe("Claim and lock lp on meteora dammm", () => { lockEscrowKey ); - const expectedTotalLockLp = beforeMigrationMetadata.creatorLockedLp.add( - beforeMigrationMetadata.partnerLockedLp - ); + expect( + afterMigrationMetadata.actualCreatorLockedLp.lt( + afterMigrationMetadata.creatorLockedLp + ) + ).to.be.true; + expect( + afterMigrationMetadata.actualPartnerLockedLp.lt( + afterMigrationMetadata.partnerLockedLp + ) + ).to.be.true; + + const expectedTotalLockLp = + afterMigrationMetadata.actualCreatorLockedLp.add( + afterMigrationMetadata.actualPartnerLockedLp + ); const totalLockLp = lockEscrowState.totalLockedAmount; @@ -332,13 +500,7 @@ describe("Claim and lock lp on meteora dammm", () => { virtualPool ); - const dammPool = deriveDammPoolAddress( - dammConfig, - virtualPoolState.baseMint, - configState.quoteMint - ); - - const lpMint = deriveLpMintAddress(dammPool); + const lpMint = deriveLpMintAddress(dammPoolAddress); const creatorLpAta = getAssociatedTokenAddressSync( lpMint, poolCreator.publicKey @@ -377,9 +539,17 @@ describe("Claim and lock lp on meteora dammm", () => { expect(afterMigrationMetadata.creatorClaimStatus).equal(Number(true)); expect(afterMigrationMetadata.partnerClaimStatus).equal(Number(true)); - const expectedLpToClaim = beforeMigrationMetadata.creatorLp.add( - beforeMigrationMetadata.partnerLp - ); + const nonLockedLpToClaim = beforeMigrationMetadata.creatorLockedLp + .add(beforeMigrationMetadata.partnerLockedLp) + .sub( + beforeMigrationMetadata.actualCreatorLockedLp.add( + beforeMigrationMetadata.actualPartnerLockedLp + ) + ); + + const expectedLpToClaim = beforeMigrationMetadata.creatorLp + .add(beforeMigrationMetadata.partnerLp) + .add(nonLockedLpToClaim); expect(expectedLpToClaim.toString()).equal( creatorLpTokenState.amount.toString() @@ -419,6 +589,7 @@ describe("Claim and lock lp on meteora dammm", () => { dammConfig: innerDammConfig, virtualPool: innerVirtualPool, migrationMetadata: innerMigrationMetadata, + dammPoolAddress: innerDammPoolAddress, } = await setupPrerequisite( context.banksClient, program, @@ -432,6 +603,9 @@ describe("Claim and lock lp on meteora dammm", () => { dammConfig = innerDammConfig; virtualPool = innerVirtualPool; migrationMetadata = innerMigrationMetadata; + dammPoolAddress = innerDammPoolAddress; + + await generateSwapFeesOnDamm(context.banksClient, dammPoolAddress, user); }); it("Creator lock LP", async () => { @@ -470,7 +644,16 @@ describe("Claim and lock lp on meteora dammm", () => { lockEscrowKey ); - const expectedTotalLockLp = beforeMigrationMetadata.creatorLockedLp; + expect(afterMigrationMetadata.creatorLockedStatus).equal(Number(true)); + expect(afterMigrationMetadata.partnerLockedStatus).equal(Number(false)); + + expect( + afterMigrationMetadata.actualCreatorLockedLp.lt( + afterMigrationMetadata.creatorLockedLp + ) + ).to.be.true; + + const expectedTotalLockLp = afterMigrationMetadata.actualCreatorLockedLp; const totalLockLp = lockEscrowState.totalLockedAmount; expect(expectedTotalLockLp.toString()).equal(totalLockLp.toString()); @@ -512,7 +695,16 @@ describe("Claim and lock lp on meteora dammm", () => { lockEscrowKey ); - const expectedTotalLockLp = beforeMigrationMetadata.partnerLockedLp; + expect(afterMigrationMetadata.creatorLockedStatus).equal(Number(true)); + expect(afterMigrationMetadata.partnerLockedStatus).equal(Number(true)); + + expect( + afterMigrationMetadata.actualPartnerLockedLp.lt( + afterMigrationMetadata.partnerLockedLp + ) + ).to.be.true; + + const expectedTotalLockLp = afterMigrationMetadata.actualPartnerLockedLp; const totalLockLp = lockEscrowState.totalLockedAmount; expect(expectedTotalLockLp.toString()).equal(totalLockLp.toString()); @@ -573,7 +765,13 @@ describe("Claim and lock lp on meteora dammm", () => { afterMigrationMetadata.partnerClaimStatus ); - const expectedLpToClaim = beforeMigrationMetadata.creatorLp; + const nonLockedFeeLpToClaim = beforeMigrationMetadata.creatorLockedLp.sub( + beforeMigrationMetadata.actualCreatorLockedLp + ); + + const expectedLpToClaim = beforeMigrationMetadata.creatorLp.add( + nonLockedFeeLpToClaim + ); expect(expectedLpToClaim.toString()).equal( creatorLpTokenState.amount.toString() @@ -635,7 +833,13 @@ describe("Claim and lock lp on meteora dammm", () => { afterMigrationMetadata.creatorClaimStatus ); - const expectedLpToClaim = beforeMigrationMetadata.partnerLp; + const nonLockedFeeLpToClaim = beforeMigrationMetadata.partnerLockedLp.sub( + beforeMigrationMetadata.actualPartnerLockedLp + ); + + const expectedLpToClaim = beforeMigrationMetadata.partnerLp.add( + nonLockedFeeLpToClaim + ); expect(expectedLpToClaim.toString()).equal( partnerLpTokenState.amount.toString() diff --git a/tests/instructions/adminInstructions.ts b/tests/instructions/adminInstructions.ts index 28896f02..bb12c543 100644 --- a/tests/instructions/adminInstructions.ts +++ b/tests/instructions/adminInstructions.ts @@ -32,7 +32,7 @@ export async function createClaimFeeOperator( const claimFeeOperator = deriveClaimFeeOperatorAddress(operator); const transaction = await program.methods .createClaimFeeOperator() - .accounts({ + .accountsPartial({ claimFeeOperator, operator, admin: admin.publicKey, @@ -103,8 +103,6 @@ export async function claimProtocolFee( poolState.quoteVault ); - - const tokenBaseProgram = configState.tokenType == 0 ? TOKEN_PROGRAM_ID : TOKEN_2022_PROGRAM_ID; @@ -134,12 +132,17 @@ export async function claimProtocolFee( createBaseTokenAccountIx && preInstructions.push(createBaseTokenAccountIx); createQuoteTokenAccountIx && preInstructions.push(createQuoteTokenAccountIx); - const tokenQuoteAccountState = await getTokenAccount(banksClient, tokenQuoteAccount); - const preQuoteTokenBalance = tokenQuoteAccountState ? tokenQuoteAccountState.amount : 0; + const tokenQuoteAccountState = await getTokenAccount( + banksClient, + tokenQuoteAccount + ); + const preQuoteTokenBalance = tokenQuoteAccountState + ? tokenQuoteAccountState.amount + : 0; const transaction = await program.methods .claimProtocolFee() - .accounts({ + .accountsPartial({ poolAuthority, config: poolState.config, pool, @@ -206,7 +209,7 @@ export async function protocolWithdrawSurplus( const transaction = await program.methods .protocolWithdrawSurplus() - .accounts({ + .accountsPartial({ poolAuthority, config: poolState.config, virtualPool, diff --git a/tests/instructions/meteoraMigration.ts b/tests/instructions/meteoraMigration.ts index bc31b3a8..27407a59 100644 --- a/tests/instructions/meteoraMigration.ts +++ b/tests/instructions/meteoraMigration.ts @@ -70,7 +70,7 @@ export async function migrateToMeteoraDamm( banksClient: BanksClient, program: VirtualCurveProgram, params: MigrateMeteoraParams -): Promise { +): Promise { const { payer, virtualPool, dammConfig } = params; const virtualPoolState = await getVirtualPool( banksClient, @@ -171,6 +171,8 @@ export async function migrateToMeteoraDamm( transaction.recentBlockhash = (await banksClient.getLatestBlockhash())[0]; transaction.sign(payer); await processTransactionMaybeThrow(banksClient, transaction); + + return dammPool; } export type LockLPDammForCreatorParams = { @@ -183,7 +185,7 @@ export async function lockLpForCreatorDamm( banksClient: BanksClient, program: VirtualCurveProgram, params: LockLPDammForCreatorParams -): Promsie { +): Promise { const { payer, virtualPool, dammConfig } = params; const virtualPoolState = await getVirtualPool( banksClient, @@ -287,6 +289,8 @@ export async function lockLpForCreatorDamm( } export type LockLPDammForPartnerParams = LockLPDammForCreatorParams; +export type ClaimFeeLpForCreatorParams = LockLPDammForCreatorParams; +export type ClaimFeeLpForPartnerParams = ClaimFeeLpForCreatorParams; export async function lockLpForPartnerDamm( banksClient: BanksClient, diff --git a/tests/utils/common.ts b/tests/utils/common.ts index 51c03a79..8e4aad73 100644 --- a/tests/utils/common.ts +++ b/tests/utils/common.ts @@ -303,7 +303,7 @@ export async function getDynamicVault( ): Promise { const program = createVaultProgram(); const account = await banksClient.getAccount(vault); - return program.coder.accounts.decode("Vault", Buffer.from(account.data)); + return program.coder.accounts.decode("vault", Buffer.from(account.data)); } export async function createDammConfig( @@ -382,7 +382,7 @@ export async function createDammV2Config( ); const transaction = await program.methods .createConfig(params) - .accounts({ + .accountsPartial({ config, admin: payer.publicKey, }) diff --git a/tests/utils/types.ts b/tests/utils/types.ts index 4b56ec72..d213ad56 100644 --- a/tests/utils/types.ts +++ b/tests/utils/types.ts @@ -6,10 +6,13 @@ export type VirtualCurveProgram = Program; export type Pool = IdlAccounts["virtualPool"]; export type PoolConfig = IdlAccounts["poolConfig"]; -export type PartnerMetadata = IdlAccounts["partnerMetadata"]; +export type PartnerMetadata = + IdlAccounts["partnerMetadata"]; export type VirtualPoolMetadata = IdlAccounts["virtualPoolMetadata"]; -export type ClaimFeeOperator = IdlAccounts["claimFeeOperator"]; +export type ClaimFeeOperator = + IdlAccounts["claimFeeOperator"]; export type MeteoraDammMigrationMetadata = IdlAccounts["meteoraDammMigrationMetadata"]; export type LockEscrow = IdlAccounts["lockEscrow"]; +export type DammPool = IdlAccounts["pool"];