mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-08-27 07:01:14 +00:00
feat(amm): add configurable fee tiers
- accept a supported fee tier in pool creation - store fee tiers in AMM pool state and validate them - update AMM tests and IDL for the new pool creation argument
This commit is contained in:
+121
-92
@@ -1,5 +1,9 @@
|
||||
use amm_core::{PoolDefinition, MINIMUM_LIQUIDITY};
|
||||
use amm_core::{
|
||||
PoolDefinition, FEE_TIER_BPS_1, FEE_TIER_BPS_100, FEE_TIER_BPS_30, FEE_TIER_BPS_5,
|
||||
MINIMUM_LIQUIDITY,
|
||||
};
|
||||
use nssa::{
|
||||
error::NssaError,
|
||||
program_deployment_transaction::{self, ProgramDeploymentTransaction},
|
||||
public_transaction, PrivateKey, PublicKey, PublicTransaction, V03State,
|
||||
};
|
||||
@@ -88,6 +92,10 @@ impl Ids {
|
||||
}
|
||||
|
||||
impl Balances {
|
||||
fn fee_tier() -> u128 {
|
||||
FEE_TIER_BPS_30
|
||||
}
|
||||
|
||||
fn user_a_init() -> u128 {
|
||||
10_000
|
||||
}
|
||||
@@ -291,7 +299,7 @@ impl Accounts {
|
||||
liquidity_pool_supply: Balances::pool_lp_supply_init(),
|
||||
reserve_a: Balances::vault_a_init(),
|
||||
reserve_b: Balances::vault_b_init(),
|
||||
fees: 0_u128,
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
@@ -400,7 +408,7 @@ impl Accounts {
|
||||
liquidity_pool_supply: Balances::pool_lp_supply_init(),
|
||||
reserve_a: Balances::vault_a_swap_1(),
|
||||
reserve_b: Balances::vault_b_swap_1(),
|
||||
fees: 0_u128,
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
@@ -468,7 +476,7 @@ impl Accounts {
|
||||
liquidity_pool_supply: Balances::pool_lp_supply_init(),
|
||||
reserve_a: Balances::vault_a_swap_2(),
|
||||
reserve_b: Balances::vault_b_swap_2(),
|
||||
fees: 0_u128,
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
@@ -536,7 +544,7 @@ impl Accounts {
|
||||
liquidity_pool_supply: Balances::token_lp_supply_add(),
|
||||
reserve_a: Balances::vault_a_add(),
|
||||
reserve_b: Balances::vault_b_add(),
|
||||
fees: 0_u128,
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
@@ -629,7 +637,7 @@ impl Accounts {
|
||||
liquidity_pool_supply: Balances::token_lp_supply_remove(),
|
||||
reserve_a: Balances::vault_a_remove(),
|
||||
reserve_b: Balances::vault_b_remove(),
|
||||
fees: 0_u128,
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
@@ -759,7 +767,7 @@ impl Accounts {
|
||||
liquidity_pool_supply: 0,
|
||||
reserve_a: 0,
|
||||
reserve_b: 0,
|
||||
fees: 0_u128,
|
||||
fees: Balances::fee_tier(),
|
||||
active: false,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
@@ -840,7 +848,7 @@ impl Accounts {
|
||||
liquidity_pool_supply: Balances::lp_supply_init(),
|
||||
reserve_a: Balances::vault_a_init(),
|
||||
reserve_b: Balances::vault_b_init(),
|
||||
fees: 0_u128,
|
||||
fees: Balances::fee_tier(),
|
||||
active: true,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
@@ -917,6 +925,42 @@ fn state_for_amm_tests_with_new_def() -> V03State {
|
||||
state
|
||||
}
|
||||
|
||||
fn try_execute_new_definition(state: &mut V03State, fees: u128) -> Result<(), NssaError> {
|
||||
let instruction = amm_core::Instruction::NewDefinition {
|
||||
token_a_amount: Balances::vault_a_init(),
|
||||
token_b_amount: Balances::vault_b_init(),
|
||||
fees,
|
||||
amm_program_id: Ids::amm_program(),
|
||||
};
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::amm_program(),
|
||||
vec![
|
||||
Ids::pool_definition(),
|
||||
Ids::vault_a(),
|
||||
Ids::vault_b(),
|
||||
Ids::token_lp_definition(),
|
||||
Ids::lp_lock_holding(),
|
||||
Ids::user_a(),
|
||||
Ids::user_b(),
|
||||
Ids::user_lp(),
|
||||
],
|
||||
vec![Nonce(0), Nonce(0)],
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set =
|
||||
public_transaction::WitnessSet::for_message(&message, &[&Keys::user_a(), &Keys::user_b()]);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 0)
|
||||
}
|
||||
|
||||
fn execute_new_definition(state: &mut V03State, fees: u128) {
|
||||
try_execute_new_definition(state, fees).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amm_remove_liquidity() {
|
||||
let mut state = state_for_amm_tests();
|
||||
@@ -1022,34 +1066,7 @@ fn amm_new_definition_inactive_initialized_pool_and_uninit_user_lp() {
|
||||
Accounts::token_lp_definition_init_inactive(),
|
||||
);
|
||||
|
||||
let instruction = amm_core::Instruction::NewDefinition {
|
||||
token_a_amount: Balances::vault_a_init(),
|
||||
token_b_amount: Balances::vault_b_init(),
|
||||
amm_program_id: Ids::amm_program(),
|
||||
};
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::amm_program(),
|
||||
vec![
|
||||
Ids::pool_definition(),
|
||||
Ids::vault_a(),
|
||||
Ids::vault_b(),
|
||||
Ids::token_lp_definition(),
|
||||
Ids::lp_lock_holding(),
|
||||
Ids::user_a(),
|
||||
Ids::user_b(),
|
||||
Ids::user_lp(),
|
||||
],
|
||||
vec![Nonce(0), Nonce(0)],
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set =
|
||||
public_transaction::WitnessSet::for_message(&message, &[&Keys::user_a(), &Keys::user_b()]);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 0).unwrap();
|
||||
execute_new_definition(&mut state, Balances::fee_tier());
|
||||
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::pool_definition()),
|
||||
@@ -1097,34 +1114,7 @@ fn amm_new_definition_inactive_initialized_pool_init_user_lp() {
|
||||
);
|
||||
state.force_insert_account(Ids::user_lp(), Accounts::user_lp_holding_init_zero());
|
||||
|
||||
let instruction = amm_core::Instruction::NewDefinition {
|
||||
token_a_amount: Balances::vault_a_init(),
|
||||
token_b_amount: Balances::vault_b_init(),
|
||||
amm_program_id: Ids::amm_program(),
|
||||
};
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::amm_program(),
|
||||
vec![
|
||||
Ids::pool_definition(),
|
||||
Ids::vault_a(),
|
||||
Ids::vault_b(),
|
||||
Ids::token_lp_definition(),
|
||||
Ids::lp_lock_holding(),
|
||||
Ids::user_a(),
|
||||
Ids::user_b(),
|
||||
Ids::user_lp(),
|
||||
],
|
||||
vec![Nonce(0), Nonce(0)],
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set =
|
||||
public_transaction::WitnessSet::for_message(&message, &[&Keys::user_a(), &Keys::user_b()]);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 0).unwrap();
|
||||
execute_new_definition(&mut state, Balances::fee_tier());
|
||||
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::pool_definition()),
|
||||
@@ -1166,34 +1156,7 @@ fn amm_new_definition_uninitialized_pool() {
|
||||
state.force_insert_account(Ids::vault_a(), Accounts::vault_a_init_inactive());
|
||||
state.force_insert_account(Ids::vault_b(), Accounts::vault_b_init_inactive());
|
||||
|
||||
let instruction = amm_core::Instruction::NewDefinition {
|
||||
token_a_amount: Balances::vault_a_init(),
|
||||
token_b_amount: Balances::vault_b_init(),
|
||||
amm_program_id: Ids::amm_program(),
|
||||
};
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::amm_program(),
|
||||
vec![
|
||||
Ids::pool_definition(),
|
||||
Ids::vault_a(),
|
||||
Ids::vault_b(),
|
||||
Ids::token_lp_definition(),
|
||||
Ids::lp_lock_holding(),
|
||||
Ids::user_a(),
|
||||
Ids::user_b(),
|
||||
Ids::user_lp(),
|
||||
],
|
||||
vec![Nonce(0), Nonce(0)],
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set =
|
||||
public_transaction::WitnessSet::for_message(&message, &[&Keys::user_a(), &Keys::user_b()]);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 0).unwrap();
|
||||
execute_new_definition(&mut state, Balances::fee_tier());
|
||||
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::pool_definition()),
|
||||
@@ -1229,6 +1192,72 @@ fn amm_new_definition_uninitialized_pool() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amm_new_definition_supports_all_fee_tiers() {
|
||||
for fees in [
|
||||
FEE_TIER_BPS_1,
|
||||
FEE_TIER_BPS_5,
|
||||
FEE_TIER_BPS_30,
|
||||
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());
|
||||
|
||||
execute_new_definition(&mut state, fees);
|
||||
|
||||
let pool_definition =
|
||||
PoolDefinition::try_from(&state.get_account_by_id(Ids::pool_definition()).data)
|
||||
.expect("new definition should create a valid pool");
|
||||
assert_eq!(pool_definition.fees, fees);
|
||||
}
|
||||
}
|
||||
|
||||
#[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::token_lp_definition(),
|
||||
Accounts::token_lp_definition_init_inactive(),
|
||||
);
|
||||
state.force_insert_account(Ids::user_lp(), Accounts::user_lp_holding_init_zero());
|
||||
|
||||
let result = try_execute_new_definition(&mut state, 2);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::pool_definition()),
|
||||
Accounts::pool_definition_inactive()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::vault_a()),
|
||||
Accounts::vault_a_init_inactive()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::vault_b()),
|
||||
Accounts::vault_b_init_inactive()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::token_lp_definition()),
|
||||
Accounts::token_lp_definition_init_inactive()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::user_a()),
|
||||
Accounts::user_a_holding()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::user_b()),
|
||||
Accounts::user_b_holding()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::user_lp()),
|
||||
Accounts::user_lp_holding_init_zero()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amm_add_liquidity() {
|
||||
let mut state = state_for_amm_tests();
|
||||
|
||||
Reference in New Issue
Block a user