mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-05-18 15:09:51 +00:00
refactor(amm)!: derive pool active state from LP supply instead of explicit flag
Remove the `active: bool` field from `PoolDefinition` and replace it with an implicit invariant: a pool is considered active when `liquidity_pool_supply >= MINIMUM_LIQUIDITY`. BREAKING CHANGE: `PoolDefinition` Borsh serialization format has changed. Existing on-chain pool accounts encoded with the `active` field are incompatible with this version. Closes #25
This commit is contained in:
parent
94f14ae305
commit
4a9a441ccd
@ -15,7 +15,7 @@ const LP_LOCK_HOLDING_PDA_SEED: [u8; 32] = [1; 32];
|
||||
/// AMM Program Instruction.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum Instruction {
|
||||
/// Initializes a new Pool (or re-initializes an inactive Pool).
|
||||
/// Initializes a new Pool (or re-initializes an existing zero-supply Pool).
|
||||
///
|
||||
/// On initialization, `MINIMUM_LIQUIDITY` LP tokens are permanently locked
|
||||
/// in the LP-lock holding PDA; the caller receives `initial_lp - MINIMUM_LIQUIDITY`.
|
||||
@ -104,7 +104,7 @@ pub enum Instruction {
|
||||
/// Sync pool reserves with current vault balances.
|
||||
///
|
||||
/// Required accounts:
|
||||
/// - AMM Pool (initialized, active)
|
||||
/// - AMM Pool (initialized, with LP supply at or above minimum liquidity)
|
||||
/// - Vault Holding Account for Token A (initialized)
|
||||
/// - Vault Holding Account for Token B (initialized)
|
||||
SyncReserves,
|
||||
@ -119,19 +119,13 @@ pub struct PoolDefinition {
|
||||
pub vault_a_id: AccountId,
|
||||
pub vault_b_id: AccountId,
|
||||
pub liquidity_pool_id: AccountId,
|
||||
/// Total LP supply tracked by the pool. After initialization it includes the permanently
|
||||
/// locked `MINIMUM_LIQUIDITY`; a zero supply means the pool is uninitialized
|
||||
pub liquidity_pool_supply: u128,
|
||||
pub reserve_a: u128,
|
||||
pub reserve_b: u128,
|
||||
/// Fee tier in basis points.
|
||||
pub fees: u128,
|
||||
/// Indicates whether the pool is initialized for use.
|
||||
/// `MINIMUM_LIQUIDITY` LP tokens are permanently locked at initialization
|
||||
/// and cannot be removed, so `liquidity_pool_supply` will never drop below
|
||||
/// `MINIMUM_LIQUIDITY` for pools created after the minimum-liquidity lock
|
||||
/// was introduced. Reaching that floor does not by itself imply
|
||||
/// `active = false`; pools may remain active with only the permanently
|
||||
/// locked minimum liquidity remaining.
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
pub const FEE_BPS_DENOMINATOR: u128 = 10_000;
|
||||
|
||||
@ -15,7 +15,7 @@ mod amm {
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
|
||||
/// Initializes a new Pool (or re-initializes an inactive Pool).
|
||||
/// Initializes a new Pool (or re-initializes an existing zero-supply Pool).
|
||||
#[instruction]
|
||||
pub fn new_definition(
|
||||
pool: AccountWithMetadata,
|
||||
|
||||
@ -73,7 +73,7 @@ pub fn new_definition(
|
||||
assert_supported_fee_tier(fees);
|
||||
|
||||
// TODO: return here
|
||||
// Verify that Pool Account is not active
|
||||
// A pool can only be initialized from a fresh account state.
|
||||
let is_new_pool = pool.account == Account::default();
|
||||
let pool_account_data = if is_new_pool {
|
||||
PoolDefinition::default()
|
||||
@ -82,16 +82,10 @@ pub fn new_definition(
|
||||
.expect("AMM program expects a valid Pool account")
|
||||
};
|
||||
|
||||
assert!(
|
||||
!pool_account_data.active,
|
||||
"Cannot initialize an active Pool Definition"
|
||||
assert_eq!(
|
||||
pool_account_data.liquidity_pool_supply, 0,
|
||||
"Cannot initialize a Pool Definition with nonzero LP supply"
|
||||
);
|
||||
if !is_new_pool {
|
||||
assert_eq!(
|
||||
pool_account_data.liquidity_pool_supply, 0,
|
||||
"New definition: inactive Pool Definition must have zero LP supply before reinitialization"
|
||||
);
|
||||
}
|
||||
|
||||
// LP Token minting calculation
|
||||
let initial_lp = token_a_amount
|
||||
@ -117,11 +111,10 @@ pub fn new_definition(
|
||||
reserve_a: token_a_amount.into(),
|
||||
reserve_b: token_b_amount.into(),
|
||||
fees,
|
||||
active: true,
|
||||
};
|
||||
|
||||
pool_post.data = Data::from(&pool_post_definition);
|
||||
let pool_post: AccountPostState = if pool.account == Account::default() {
|
||||
let pool_post: AccountPostState = if is_new_pool {
|
||||
AccountPostState::new_claimed(pool_post.clone())
|
||||
} else {
|
||||
AccountPostState::new(pool_post.clone())
|
||||
|
||||
@ -29,7 +29,10 @@ pub fn remove_liquidity(
|
||||
.expect("Remove liquidity: AMM Program expects a valid Pool Definition Account");
|
||||
assert_supported_fee_tier(pool_def_data.fees);
|
||||
|
||||
assert!(pool_def_data.active, "Pool is inactive");
|
||||
assert!(
|
||||
pool_def_data.liquidity_pool_supply >= MINIMUM_LIQUIDITY,
|
||||
"Pool liquidity supply is below minimum liquidity"
|
||||
);
|
||||
assert_eq!(
|
||||
pool_def_data.liquidity_pool_id, pool_definition_lp.account_id,
|
||||
"LP definition mismatch"
|
||||
@ -135,7 +138,6 @@ pub fn remove_liquidity(
|
||||
.reserve_b
|
||||
.checked_sub(withdraw_amount_b)
|
||||
.expect("reserve_b - withdraw_amount_b underflows"),
|
||||
active: true,
|
||||
..pool_def_data.clone()
|
||||
};
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use amm_core::assert_supported_fee_tier;
|
||||
use amm_core::{assert_supported_fee_tier, MINIMUM_LIQUIDITY};
|
||||
pub use amm_core::{compute_liquidity_token_pda_seed, compute_vault_pda_seed, PoolDefinition};
|
||||
use nssa_core::{
|
||||
account::{AccountId, AccountWithMetadata, Data},
|
||||
program::{AccountPostState, ChainedCall},
|
||||
};
|
||||
|
||||
/// Validates swap setup: checks pool is active, vaults match, and reserves are sufficient.
|
||||
/// Validates swap setup: checks pool liquidity is ready, vaults match, and reserves are sufficient.
|
||||
fn validate_swap_setup(
|
||||
pool: &AccountWithMetadata,
|
||||
vault_a: &AccountWithMetadata,
|
||||
@ -15,7 +15,10 @@ fn validate_swap_setup(
|
||||
.expect("AMM Program expects a valid Pool Definition Account");
|
||||
assert_supported_fee_tier(pool_def_data.fees);
|
||||
|
||||
assert!(pool_def_data.active, "Pool is inactive");
|
||||
assert!(
|
||||
pool_def_data.liquidity_pool_supply >= MINIMUM_LIQUIDITY,
|
||||
"Pool liquidity supply is below minimum liquidity"
|
||||
);
|
||||
assert_eq!(
|
||||
vault_a.account_id, pool_def_data.vault_a_id,
|
||||
"Vault A was not provided"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use amm_core::{read_vault_fungible_balances, PoolDefinition};
|
||||
use amm_core::{read_vault_fungible_balances, PoolDefinition, MINIMUM_LIQUIDITY};
|
||||
use nssa_core::{
|
||||
account::{AccountWithMetadata, Data},
|
||||
program::{AccountPostState, ChainedCall},
|
||||
@ -12,7 +12,10 @@ pub fn sync_reserves(
|
||||
let pool_def_data = PoolDefinition::try_from(&pool.account.data)
|
||||
.expect("Sync reserves: AMM Program expects a valid Pool Definition Account");
|
||||
|
||||
assert!(pool_def_data.active, "Pool is inactive");
|
||||
assert!(
|
||||
pool_def_data.liquidity_pool_supply >= MINIMUM_LIQUIDITY,
|
||||
"Pool liquidity supply is below minimum liquidity"
|
||||
);
|
||||
assert_eq!(
|
||||
vault_a.account_id, pool_def_data.vault_a_id,
|
||||
"Vault A was not provided"
|
||||
|
||||
110
amm/src/tests.rs
110
amm/src/tests.rs
@ -875,7 +875,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -903,7 +902,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: 1_000,
|
||||
reserve_b: 500,
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -927,7 +925,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: 0,
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -951,7 +948,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: 0,
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -975,7 +971,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_reserve_low(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_high(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -999,7 +994,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_reserve_high(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_low(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1023,7 +1017,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_swap_test_1(),
|
||||
reserve_b: BalanceForTests::vault_b_swap_test_1(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1047,7 +1040,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_swap_test_2(),
|
||||
reserve_b: BalanceForTests::vault_b_swap_test_2(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1071,7 +1063,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: 1498_u128,
|
||||
reserve_b: 334_u128,
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1095,7 +1086,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: 715_u128,
|
||||
reserve_b: 700_u128,
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1119,7 +1109,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1143,7 +1132,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_add_successful(),
|
||||
reserve_b: BalanceForTests::vault_b_add_successful(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1167,7 +1155,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_remove_successful(),
|
||||
reserve_b: BalanceForTests::vault_b_remove_successful(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1176,7 +1163,7 @@ impl AccountWithMetadataForTests {
|
||||
}
|
||||
}
|
||||
|
||||
fn pool_definition_inactive() -> AccountWithMetadata {
|
||||
fn pool_definition_below_minimum_liquidity() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
@ -1187,11 +1174,10 @@ impl AccountWithMetadataForTests {
|
||||
vault_a_id: IdForTests::vault_a_id(),
|
||||
vault_b_id: IdForTests::vault_b_id(),
|
||||
liquidity_pool_id: IdForTests::token_lp_definition_id(),
|
||||
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
|
||||
liquidity_pool_supply: MINIMUM_LIQUIDITY - 1,
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: false,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1200,7 +1186,7 @@ impl AccountWithMetadataForTests {
|
||||
}
|
||||
}
|
||||
|
||||
fn pool_definition_reinitializable() -> AccountWithMetadata {
|
||||
fn pool_definition_zero_supply_reinitializable() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
@ -1215,7 +1201,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: 0,
|
||||
reserve_b: 0,
|
||||
fees: 0u128,
|
||||
active: false,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1239,7 +1224,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: false,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1280,30 +1264,6 @@ impl AccountWithMetadataForTests {
|
||||
}
|
||||
}
|
||||
|
||||
fn pool_definition_active() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
vault_a_id: IdForTests::vault_a_id(),
|
||||
vault_b_id: IdForTests::vault_b_id(),
|
||||
liquidity_pool_id: IdForTests::token_lp_definition_id(),
|
||||
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
is_authorized: true,
|
||||
account_id: IdForTests::pool_definition_id(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Legacy/corrupted pool state whose reported supply has already been drained down to the
|
||||
/// permanent lock (liquidity_pool_supply == MINIMUM_LIQUIDITY).
|
||||
fn pool_definition_at_minimum_liquidity() -> AccountWithMetadata {
|
||||
@ -1321,7 +1281,6 @@ impl AccountWithMetadataForTests {
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -1944,11 +1903,11 @@ fn test_call_new_definition_wrong_vault_id_2() {
|
||||
);
|
||||
}
|
||||
|
||||
#[should_panic(expected = "Cannot initialize an active Pool Definition")]
|
||||
#[should_panic(expected = "Cannot initialize a Pool Definition with nonzero LP supply")]
|
||||
#[test]
|
||||
fn test_call_new_definition_cannot_initialize_active_pool() {
|
||||
fn test_call_new_definition_cannot_initialize_nonzero_supply_pool() {
|
||||
let _post_states = new_definition(
|
||||
AccountWithMetadataForTests::pool_definition_active(),
|
||||
AccountWithMetadataForTests::pool_definition_init(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::pool_lp_init(),
|
||||
@ -1968,7 +1927,7 @@ fn test_call_new_definition_cannot_initialize_active_pool() {
|
||||
fn test_call_new_definition_initial_lp_too_small() {
|
||||
// isqrt(1000 * 1000) = 1000 == MINIMUM_LIQUIDITY, so the assertion fires.
|
||||
let _post_states = new_definition(
|
||||
AccountWithMetadataForTests::pool_definition_reinitializable(),
|
||||
AccountWithMetadataForTests::pool_definition_zero_supply_reinitializable(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::pool_lp_reinitializable(),
|
||||
@ -1986,7 +1945,7 @@ fn test_call_new_definition_initial_lp_too_small() {
|
||||
#[test]
|
||||
fn test_call_new_definition_chained_call_successful() {
|
||||
let (post_states, chained_calls) = new_definition(
|
||||
AccountWithMetadataForTests::pool_definition_reinitializable(),
|
||||
AccountWithMetadataForTests::pool_definition_zero_supply_reinitializable(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::pool_lp_reinitializable(),
|
||||
@ -2090,11 +2049,11 @@ fn test_call_swap_reserves_vault_mismatch_2() {
|
||||
);
|
||||
}
|
||||
|
||||
#[should_panic(expected = "Pool is inactive")]
|
||||
#[should_panic(expected = "Pool liquidity supply is below minimum liquidity")]
|
||||
#[test]
|
||||
fn test_call_swap_ianctive() {
|
||||
fn test_call_swap_below_minimum_liquidity() {
|
||||
let _post_states = swap_exact_input(
|
||||
AccountWithMetadataForTests::pool_definition_inactive(),
|
||||
AccountWithMetadataForTests::pool_definition_below_minimum_liquidity(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::user_holding_a(),
|
||||
@ -2279,11 +2238,11 @@ fn call_swap_exact_output_reserves_vault_mismatch_2() {
|
||||
);
|
||||
}
|
||||
|
||||
#[should_panic(expected = "Pool is inactive")]
|
||||
#[should_panic(expected = "Pool liquidity supply is below minimum liquidity")]
|
||||
#[test]
|
||||
fn call_swap_exact_output_inactive() {
|
||||
fn call_swap_exact_output_below_minimum_liquidity() {
|
||||
let _post_states = swap_exact_output(
|
||||
AccountWithMetadataForTests::pool_definition_inactive(),
|
||||
AccountWithMetadataForTests::pool_definition_below_minimum_liquidity(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::user_holding_a(),
|
||||
@ -2426,11 +2385,10 @@ fn swap_exact_output_overflow_protection() {
|
||||
vault_a_id: IdForTests::vault_a_id(),
|
||||
vault_b_id: IdForTests::vault_b_id(),
|
||||
liquidity_pool_id: IdForTests::token_lp_definition_id(),
|
||||
liquidity_pool_supply: 1,
|
||||
liquidity_pool_supply: MINIMUM_LIQUIDITY,
|
||||
reserve_a: large_reserve,
|
||||
reserve_b,
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -2482,7 +2440,7 @@ fn swap_exact_output_overflow_protection() {
|
||||
#[test]
|
||||
fn test_new_definition_lp_asymmetric_amounts() {
|
||||
let (post_states, chained_calls) = new_definition(
|
||||
AccountWithMetadataForTests::pool_definition_reinitializable(),
|
||||
AccountWithMetadataForTests::pool_definition_zero_supply_reinitializable(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::pool_lp_reinitializable(),
|
||||
@ -2519,7 +2477,7 @@ fn test_new_definition_lp_symmetric_amounts() {
|
||||
assert_eq!(expected_lp, 2_000);
|
||||
|
||||
let (post_states, chained_calls) = new_definition(
|
||||
AccountWithMetadataForTests::pool_definition_reinitializable(),
|
||||
AccountWithMetadataForTests::pool_definition_zero_supply_reinitializable(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::pool_lp_reinitializable(),
|
||||
@ -2574,13 +2532,11 @@ fn test_new_definition_lp_symmetric_amounts() {
|
||||
assert_eq!(chained_call_lp_user, expected_lp_user_call);
|
||||
}
|
||||
|
||||
#[should_panic(
|
||||
expected = "New definition: inactive Pool Definition must have zero LP supply before reinitialization"
|
||||
)]
|
||||
#[should_panic(expected = "Cannot initialize a Pool Definition with nonzero LP supply")]
|
||||
#[test]
|
||||
fn test_call_new_definition_reinitialization_requires_zero_pool_supply() {
|
||||
let _post_states = new_definition(
|
||||
AccountWithMetadataForTests::pool_definition_inactive(),
|
||||
AccountWithMetadataForTests::pool_definition_init(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::pool_lp_reinitializable(),
|
||||
@ -2601,7 +2557,7 @@ fn test_call_new_definition_reinitialization_requires_zero_pool_supply() {
|
||||
#[test]
|
||||
fn test_call_new_definition_reinitialization_requires_zero_lp_definition_supply() {
|
||||
let _post_states = new_definition(
|
||||
AccountWithMetadataForTests::pool_definition_reinitializable(),
|
||||
AccountWithMetadataForTests::pool_definition_zero_supply_reinitializable(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::pool_lp_init(),
|
||||
@ -2702,7 +2658,6 @@ fn test_minimum_liquidity_lock_and_remove_all_user_lp() {
|
||||
assert_eq!(pool_after_remove.liquidity_pool_supply, MINIMUM_LIQUIDITY);
|
||||
assert!(pool_after_remove.reserve_a > 0);
|
||||
assert!(pool_after_remove.reserve_b > 0);
|
||||
assert!(pool_after_remove.active);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2754,6 +2709,16 @@ fn test_sync_reserves_panics_when_vault_b_under_collateralized() {
|
||||
);
|
||||
}
|
||||
|
||||
#[should_panic(expected = "Pool liquidity supply is below minimum liquidity")]
|
||||
#[test]
|
||||
fn test_sync_reserves_rejects_pool_below_minimum_liquidity() {
|
||||
let _ = sync_reserves(
|
||||
AccountWithMetadataForTests::pool_definition_below_minimum_liquidity(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_donation_then_add_liquidity_sync_mitigates_mispricing() {
|
||||
let donation_a = 100u128;
|
||||
@ -2822,7 +2787,7 @@ fn new_definition_overflow_protection() {
|
||||
let large_amount = u128::MAX / 2 + 1;
|
||||
|
||||
let _result = new_definition(
|
||||
AccountWithMetadataForTests::pool_definition_reinitializable(),
|
||||
AccountWithMetadataForTests::pool_definition_zero_supply_reinitializable(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::pool_lp_reinitializable(),
|
||||
@ -2853,11 +2818,10 @@ fn add_liquidity_overflow_protection() {
|
||||
vault_a_id: IdForTests::vault_a_id(),
|
||||
vault_b_id: IdForTests::vault_b_id(),
|
||||
liquidity_pool_id: IdForTests::token_lp_definition_id(),
|
||||
liquidity_pool_supply: 1_000,
|
||||
liquidity_pool_supply: MINIMUM_LIQUIDITY,
|
||||
reserve_a: large_reserve,
|
||||
reserve_b,
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -2928,7 +2892,6 @@ fn remove_liquidity_overflow_protection() {
|
||||
reserve_a: large_reserve,
|
||||
reserve_b,
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -3008,11 +2971,10 @@ fn swap_exact_input_overflow_protection() {
|
||||
vault_a_id: IdForTests::vault_a_id(),
|
||||
vault_b_id: IdForTests::vault_b_id(),
|
||||
liquidity_pool_id: IdForTests::token_lp_definition_id(),
|
||||
liquidity_pool_supply: 1,
|
||||
liquidity_pool_supply: MINIMUM_LIQUIDITY,
|
||||
reserve_a: 1_000,
|
||||
reserve_b: large_reserve,
|
||||
fees: BalanceForTests::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
@ -3071,7 +3033,7 @@ fn test_new_definition_supports_all_fee_tiers() {
|
||||
FEE_TIER_BPS_100,
|
||||
] {
|
||||
let (post_states, _) = new_definition(
|
||||
AccountWithMetadataForTests::pool_definition_reinitializable(),
|
||||
AccountWithMetadataForTests::pool_definition_zero_supply_reinitializable(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::pool_lp_reinitializable(),
|
||||
@ -3095,10 +3057,10 @@ fn test_new_definition_supports_all_fee_tiers() {
|
||||
#[test]
|
||||
fn test_new_definition_rejects_unsupported_fee_tier() {
|
||||
let _ = new_definition(
|
||||
AccountWithMetadataForTests::pool_definition_inactive(),
|
||||
AccountWithMetadataForTests::pool_definition_zero_supply_reinitializable(),
|
||||
AccountWithMetadataForTests::vault_a_init(),
|
||||
AccountWithMetadataForTests::vault_b_init(),
|
||||
AccountWithMetadataForTests::pool_lp_init(),
|
||||
AccountWithMetadataForTests::pool_lp_reinitializable(),
|
||||
AccountWithMetadataForTests::lp_lock_holding_uninit(),
|
||||
AccountWithMetadataForTests::user_holding_a(),
|
||||
AccountWithMetadataForTests::user_holding_b(),
|
||||
|
||||
@ -300,7 +300,6 @@ impl Accounts {
|
||||
reserve_a: Balances::vault_a_init(),
|
||||
reserve_b: Balances::vault_b_init(),
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
}
|
||||
@ -409,7 +408,6 @@ impl Accounts {
|
||||
reserve_a: Balances::vault_a_swap_1(),
|
||||
reserve_b: Balances::vault_b_swap_1(),
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
}
|
||||
@ -477,7 +475,6 @@ impl Accounts {
|
||||
reserve_a: Balances::vault_a_swap_2(),
|
||||
reserve_b: Balances::vault_b_swap_2(),
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
}
|
||||
@ -545,7 +542,6 @@ impl Accounts {
|
||||
reserve_a: Balances::vault_a_add(),
|
||||
reserve_b: Balances::vault_b_add(),
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
}
|
||||
@ -638,7 +634,6 @@ impl Accounts {
|
||||
reserve_a: Balances::vault_a_remove(),
|
||||
reserve_b: Balances::vault_b_remove(),
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
}
|
||||
@ -717,7 +712,7 @@ impl Accounts {
|
||||
}
|
||||
}
|
||||
|
||||
fn token_lp_definition_init_inactive() -> Account {
|
||||
fn token_lp_definition_reinitializable() -> Account {
|
||||
Account {
|
||||
program_owner: Ids::token_program(),
|
||||
balance: 0_u128,
|
||||
@ -730,7 +725,7 @@ impl Accounts {
|
||||
}
|
||||
}
|
||||
|
||||
fn vault_a_init_inactive() -> Account {
|
||||
fn vault_a_reinitializable() -> Account {
|
||||
Account {
|
||||
program_owner: Ids::token_program(),
|
||||
balance: 0_u128,
|
||||
@ -742,7 +737,7 @@ impl Accounts {
|
||||
}
|
||||
}
|
||||
|
||||
fn vault_b_init_inactive() -> Account {
|
||||
fn vault_b_reinitializable() -> Account {
|
||||
Account {
|
||||
program_owner: Ids::token_program(),
|
||||
balance: 0_u128,
|
||||
@ -754,7 +749,7 @@ impl Accounts {
|
||||
}
|
||||
}
|
||||
|
||||
fn pool_definition_inactive() -> Account {
|
||||
fn pool_definition_zero_supply_reinitializable() -> Account {
|
||||
Account {
|
||||
program_owner: Ids::amm_program(),
|
||||
balance: 0_u128,
|
||||
@ -768,7 +763,6 @@ impl Accounts {
|
||||
reserve_a: 0,
|
||||
reserve_b: 0,
|
||||
fees: Balances::fee_tier(),
|
||||
active: false,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
}
|
||||
@ -849,7 +843,6 @@ impl Accounts {
|
||||
reserve_a: Balances::vault_a_init(),
|
||||
reserve_b: Balances::vault_b_init(),
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
}
|
||||
@ -1056,14 +1049,17 @@ fn amm_remove_liquidity_insufficient_user_lp_fails() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amm_new_definition_inactive_initialized_pool_and_uninit_user_lp() {
|
||||
fn amm_new_definition_zero_supply_initialized_pool_and_uninit_user_lp() {
|
||||
let mut state = state_for_amm_tests_with_new_def();
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_init_inactive());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_init_inactive());
|
||||
state.force_insert_account(Ids::pool_definition(), Accounts::pool_definition_inactive());
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_reinitializable());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_reinitializable());
|
||||
state.force_insert_account(
|
||||
Ids::pool_definition(),
|
||||
Accounts::pool_definition_zero_supply_reinitializable(),
|
||||
);
|
||||
state.force_insert_account(
|
||||
Ids::token_lp_definition(),
|
||||
Accounts::token_lp_definition_init_inactive(),
|
||||
Accounts::token_lp_definition_reinitializable(),
|
||||
);
|
||||
|
||||
execute_new_definition(&mut state, Balances::fee_tier());
|
||||
@ -1103,14 +1099,17 @@ fn amm_new_definition_inactive_initialized_pool_and_uninit_user_lp() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amm_new_definition_inactive_initialized_pool_init_user_lp() {
|
||||
fn amm_new_definition_zero_supply_initialized_pool_init_user_lp() {
|
||||
let mut state = state_for_amm_tests_with_new_def();
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_init_inactive());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_init_inactive());
|
||||
state.force_insert_account(Ids::pool_definition(), Accounts::pool_definition_inactive());
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_reinitializable());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_reinitializable());
|
||||
state.force_insert_account(
|
||||
Ids::pool_definition(),
|
||||
Accounts::pool_definition_zero_supply_reinitializable(),
|
||||
);
|
||||
state.force_insert_account(
|
||||
Ids::token_lp_definition(),
|
||||
Accounts::token_lp_definition_init_inactive(),
|
||||
Accounts::token_lp_definition_reinitializable(),
|
||||
);
|
||||
state.force_insert_account(Ids::user_lp(), Accounts::user_lp_holding_init_zero());
|
||||
|
||||
@ -1153,8 +1152,8 @@ fn amm_new_definition_inactive_initialized_pool_init_user_lp() {
|
||||
#[test]
|
||||
fn amm_new_definition_uninitialized_pool() {
|
||||
let mut state = state_for_amm_tests_with_new_def();
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_init_inactive());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_init_inactive());
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_reinitializable());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_reinitializable());
|
||||
|
||||
execute_new_definition(&mut state, Balances::fee_tier());
|
||||
|
||||
@ -1201,8 +1200,8 @@ fn amm_new_definition_supports_all_fee_tiers() {
|
||||
FEE_TIER_BPS_100,
|
||||
] {
|
||||
let mut state = state_for_amm_tests_with_new_def();
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_init_inactive());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_init_inactive());
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_reinitializable());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_reinitializable());
|
||||
|
||||
execute_new_definition(&mut state, fees);
|
||||
|
||||
@ -1216,12 +1215,15 @@ fn amm_new_definition_supports_all_fee_tiers() {
|
||||
#[test]
|
||||
fn amm_new_definition_rejects_unsupported_fee_tier_transaction() {
|
||||
let mut state = state_for_amm_tests_with_new_def();
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_init_inactive());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_init_inactive());
|
||||
state.force_insert_account(Ids::pool_definition(), Accounts::pool_definition_inactive());
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_reinitializable());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_reinitializable());
|
||||
state.force_insert_account(
|
||||
Ids::pool_definition(),
|
||||
Accounts::pool_definition_zero_supply_reinitializable(),
|
||||
);
|
||||
state.force_insert_account(
|
||||
Ids::token_lp_definition(),
|
||||
Accounts::token_lp_definition_init_inactive(),
|
||||
Accounts::token_lp_definition_reinitializable(),
|
||||
);
|
||||
state.force_insert_account(Ids::user_lp(), Accounts::user_lp_holding_init_zero());
|
||||
|
||||
@ -1230,19 +1232,19 @@ fn amm_new_definition_rejects_unsupported_fee_tier_transaction() {
|
||||
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::pool_definition()),
|
||||
Accounts::pool_definition_inactive()
|
||||
Accounts::pool_definition_zero_supply_reinitializable()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::vault_a()),
|
||||
Accounts::vault_a_init_inactive()
|
||||
Accounts::vault_a_reinitializable()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::vault_b()),
|
||||
Accounts::vault_b_init_inactive()
|
||||
Accounts::vault_b_reinitializable()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::token_lp_definition()),
|
||||
Accounts::token_lp_definition_init_inactive()
|
||||
Accounts::token_lp_definition_reinitializable()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::user_a()),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user