chore(programs/amm): fix initial LP supply calculations

ift-ts:sc:logos:2026q1-amm-core-improvements:fix-initial-lp-calc
This commit is contained in:
Andrea Franz 2026-02-24 16:24:36 +01:00
parent 67f49697f7
commit fee1ab30e9
4 changed files with 107 additions and 26 deletions

Binary file not shown.

View File

@ -2504,6 +2504,12 @@ pub mod tests {
fn user_token_b_holding_new_definition() -> u128 {
7_500
}
fn lp_supply_init() -> u128 {
// isqrt(vault_a_balance_init * vault_b_balance_init) = isqrt(5_000 * 2_500) = 3535
(BalanceForTests::vault_a_balance_init() * BalanceForTests::vault_b_balance_init())
.isqrt()
}
}
struct IdForTests;
@ -3098,7 +3104,7 @@ pub mod tests {
balance: 0u128,
data: Data::from(&TokenHolding::Fungible {
definition_id: IdForTests::token_lp_definition_id(),
balance: BalanceForTests::user_token_a_holding_new_definition(),
balance: BalanceForTests::lp_supply_init(),
}),
nonce: 0,
}
@ -3110,7 +3116,7 @@ pub mod tests {
balance: 0u128,
data: Data::from(&TokenDefinition::Fungible {
name: String::from("LP Token"),
total_supply: BalanceForTests::vault_a_balance_init(),
total_supply: BalanceForTests::lp_supply_init(),
metadata_id: None,
}),
nonce: 0,
@ -3127,7 +3133,7 @@ pub mod tests {
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::user_token_a_holding_new_definition(),
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
reserve_a: BalanceForTests::vault_a_balance_init(),
reserve_b: BalanceForTests::vault_b_balance_init(),
fees: 0u128,
@ -3417,7 +3423,7 @@ pub mod tests {
let user_token_b_post = state.get_account_by_id(IdForTests::user_token_b_id());
let user_token_lp_post = state.get_account_by_id(IdForTests::user_token_lp_id());
let expected_pool = AccountForTests::pool_definition_init();
let expected_pool = AccountForTests::pool_definition_new_init();
let expected_vault_a = AccountForTests::vault_a_init();
let expected_vault_b = AccountForTests::vault_b_init();
let expected_token_lp = AccountForTests::token_lp_definition_new_init();

View File

@ -77,7 +77,7 @@ pub fn new_definition(
);
// LP Token minting calculation
// We assume LP is based on the initial deposit amount for Token_A.
let initial_lp = (token_a_amount.get() * token_b_amount.get()).isqrt();
// Update pool account
let mut pool_post = pool.account.clone();
@ -87,7 +87,7 @@ pub fn new_definition(
vault_a_id: vault_a.account_id,
vault_b_id: vault_b.account_id,
liquidity_pool_id: pool_definition_lp.account_id,
liquidity_pool_supply: token_a_amount.into(),
liquidity_pool_supply: initial_lp,
reserve_a: token_a_amount.into(),
reserve_b: token_b_amount.into(),
fees: 0u128, // TODO: we assume all fees are 0 for now.
@ -124,11 +124,11 @@ pub fn new_definition(
let instruction = if pool.account == Account::default() {
token_core::Instruction::NewFungibleDefinition {
name: String::from("LP Token"),
total_supply: token_a_amount.into(),
total_supply: initial_lp,
}
} else {
token_core::Instruction::Mint {
amount_to_mint: token_a_amount.into(),
amount_to_mint: initial_lp,
}
};

View File

@ -70,7 +70,7 @@ impl BalanceForTests {
}
fn remove_actual_a_successful() -> u128 {
100
141
}
fn remove_min_amount_b_low() -> u128 {
@ -105,6 +105,11 @@ impl BalanceForTests {
20
}
fn lp_supply_init() -> u128 {
// sqrt(vault_a_reserve_init * vault_b_reserve_init) = sqrt(1000 * 500) = 707
(BalanceForTests::vault_a_reserve_init() * BalanceForTests::vault_b_reserve_init()).isqrt()
}
fn vault_a_swap_test_1() -> u128 {
1_500
}
@ -142,11 +147,11 @@ impl BalanceForTests {
}
fn vault_a_remove_successful() -> u128 {
900
859
}
fn vault_b_remove_successful() -> u128 {
450
430
}
}
@ -249,7 +254,7 @@ impl ChainedCallForTests {
TOKEN_PROGRAM_ID,
vec![pool_lp_auth, AccountForTests::user_holding_lp_init()],
&token_core::Instruction::Mint {
amount_to_mint: BalanceForTests::add_successful_amount_a(),
amount_to_mint: 282,
},
)
.with_pda_seeds(vec![compute_liquidity_token_pda_seed(
@ -282,7 +287,7 @@ impl ChainedCallForTests {
TOKEN_PROGRAM_ID,
vec![vault_b_auth, AccountForTests::user_holding_b()],
&token_core::Instruction::Transfer {
amount_to_transfer: BalanceForTests::remove_min_amount_b_low(),
amount_to_transfer: 70,
},
)
.with_pda_seeds(vec![compute_vault_pda_seed(
@ -341,7 +346,7 @@ impl ChainedCallForTests {
AccountForTests::user_holding_lp_uninit(),
],
&token_core::Instruction::Mint {
amount_to_mint: BalanceForTests::add_successful_amount_a(),
amount_to_mint: BalanceForTests::lp_supply_init(),
},
)
.with_pda_seeds(vec![compute_liquidity_token_pda_seed(
@ -568,7 +573,7 @@ impl AccountForTests {
balance: 0u128,
data: Data::from(&TokenDefinition::Fungible {
name: String::from("test"),
total_supply: BalanceForTests::vault_a_reserve_init(),
total_supply: BalanceForTests::lp_supply_init(),
metadata_id: None,
}),
nonce: 0,
@ -585,7 +590,7 @@ impl AccountForTests {
balance: 0u128,
data: Data::from(&TokenDefinition::Fungible {
name: String::from("test"),
total_supply: BalanceForTests::vault_a_reserve_init(),
total_supply: BalanceForTests::lp_supply_init(),
metadata_id: None,
}),
nonce: 0,
@ -638,7 +643,7 @@ impl AccountForTests {
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::vault_a_reserve_init(),
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
reserve_a: BalanceForTests::vault_a_reserve_init(),
reserve_b: BalanceForTests::vault_b_reserve_init(),
fees: 0u128,
@ -662,7 +667,7 @@ impl AccountForTests {
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::vault_a_reserve_init(),
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
reserve_a: 0,
reserve_b: BalanceForTests::vault_b_reserve_init(),
fees: 0u128,
@ -686,7 +691,7 @@ impl AccountForTests {
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::vault_a_reserve_init(),
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
reserve_a: BalanceForTests::vault_a_reserve_init(),
reserve_b: 0,
fees: 0u128,
@ -758,7 +763,7 @@ impl AccountForTests {
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::vault_a_reserve_init(),
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
reserve_a: BalanceForTests::vault_a_swap_test_1(),
reserve_b: BalanceForTests::vault_b_swap_test_1(),
fees: 0u128,
@ -782,7 +787,7 @@ impl AccountForTests {
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::vault_a_reserve_init(),
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
reserve_a: BalanceForTests::vault_a_swap_test_2(),
reserve_b: BalanceForTests::vault_b_swap_test_2(),
fees: 0u128,
@ -830,7 +835,7 @@ impl AccountForTests {
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::vault_a_add_successful(),
liquidity_pool_supply: 989,
reserve_a: BalanceForTests::vault_a_add_successful(),
reserve_b: BalanceForTests::vault_b_add_successful(),
fees: 0u128,
@ -854,7 +859,7 @@ impl AccountForTests {
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::vault_a_remove_successful(),
liquidity_pool_supply: 607,
reserve_a: BalanceForTests::vault_a_remove_successful(),
reserve_b: BalanceForTests::vault_b_remove_successful(),
fees: 0u128,
@ -878,7 +883,7 @@ impl AccountForTests {
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::vault_a_reserve_init(),
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
reserve_a: BalanceForTests::vault_a_reserve_init(),
reserve_b: BalanceForTests::vault_b_reserve_init(),
fees: 0u128,
@ -902,7 +907,7 @@ impl AccountForTests {
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::vault_a_reserve_init(),
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
reserve_a: BalanceForTests::vault_a_reserve_init(),
reserve_b: BalanceForTests::vault_b_reserve_init(),
fees: 0u128,
@ -958,7 +963,7 @@ impl AccountForTests {
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::vault_a_reserve_init(),
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
reserve_a: BalanceForTests::vault_a_reserve_init(),
reserve_b: BalanceForTests::vault_b_reserve_init(),
fees: 0u128,
@ -1717,3 +1722,73 @@ fn test_call_swap_chained_call_successful_2() {
ChainedCallForTests::cc_swap_token_b_test_2()
);
}
#[test]
fn test_new_definition_lp_asymmetric_amounts() {
let (post_states, chained_calls) = new_definition(
AccountForTests::pool_definition_inactive(),
AccountForTests::vault_a_init(),
AccountForTests::vault_b_init(),
AccountForTests::pool_lp_init(),
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_uninit(),
NonZero::new(BalanceForTests::vault_a_reserve_init()).unwrap(),
NonZero::new(BalanceForTests::vault_b_reserve_init()).unwrap(),
AMM_PROGRAM_ID,
);
// check the minted LP amount
let pool_post = post_states[0].clone();
let pool_def = PoolDefinition::try_from(&pool_post.account().data).unwrap();
assert_eq!(
pool_def.liquidity_pool_supply,
BalanceForTests::lp_supply_init()
);
let chained_call_lp = chained_calls[0].clone();
assert!(chained_call_lp == ChainedCallForTests::cc_new_definition_token_lp());
}
#[test]
fn test_new_definition_lp_symmetric_amounts() {
// token_a=100, token_b=100 → LP=sqrt(10_000)=100
let token_a_amount = 100u128;
let token_b_amount = 100u128;
let expected_lp = (token_a_amount * token_b_amount).isqrt();
assert_eq!(expected_lp, 100);
let (post_states, chained_calls) = new_definition(
AccountForTests::pool_definition_inactive(),
AccountForTests::vault_a_init(),
AccountForTests::vault_b_init(),
AccountForTests::pool_lp_init(),
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_uninit(),
NonZero::new(token_a_amount).unwrap(),
NonZero::new(token_b_amount).unwrap(),
AMM_PROGRAM_ID,
);
let pool_post = post_states[0].clone();
let pool_def = PoolDefinition::try_from(&pool_post.account().data).unwrap();
assert_eq!(pool_def.liquidity_pool_supply, expected_lp);
let chained_call_lp = chained_calls[0].clone();
let expected_lp_call = ChainedCall::new(
TOKEN_PROGRAM_ID,
vec![
AccountForTests::pool_lp_init(),
AccountForTests::user_holding_lp_uninit(),
],
&token_core::Instruction::Mint {
amount_to_mint: expected_lp,
},
)
.with_pda_seeds(vec![compute_liquidity_token_pda_seed(
IdForTests::pool_definition_id(),
)]);
assert_eq!(chained_call_lp, expected_lp_call);
}