various fixes

This commit is contained in:
jonesmarvin8
2026-01-27 18:24:03 -05:00
parent 3205e69e11
commit 8d5e6a37e6
15 changed files with 253 additions and 316 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+10 -10
View File
@@ -6,11 +6,10 @@
//! AMM program accepts [`Instruction`] as input, refer to the corresponding documentation
//! for more details.
use std::num::NonZero;
use amm_core::Instruction;
use nssa_core::program::{
AccountPostState, ChainedCall, ProgramInput, read_nssa_inputs,
write_nssa_outputs_with_chained_call,
};
use nssa_core::program::{ProgramInput, read_nssa_inputs, write_nssa_outputs_with_chained_call};
fn main() {
let (
@@ -23,8 +22,7 @@ fn main() {
let pre_states_clone = pre_states.clone();
let (post_states, chained_calls): (Vec<AccountPostState>, Vec<ChainedCall>) = match instruction
{
let (post_states, chained_calls) = match instruction {
Instruction::NewDefinition {
token_a_amount,
token_b_amount,
@@ -49,8 +47,8 @@ fn main() {
user_holding_a,
user_holding_b,
user_holding_lp,
token_a_amount,
token_b_amount,
NonZero::new(token_a_amount).expect("Token A should have a nonzero amount"),
NonZero::new(token_b_amount).expect("Token B should have a nonzero amount"),
amm_program_id,
)
}
@@ -78,7 +76,8 @@ fn main() {
user_holding_a,
user_holding_b,
user_holding_lp,
min_amount_liquidity,
NonZero::new(min_amount_liquidity)
.expect("Min amount of liquidity should be nonzero"),
max_amount_to_add_token_a,
max_amount_to_add_token_b,
)
@@ -107,7 +106,8 @@ fn main() {
user_holding_a,
user_holding_b,
user_holding_lp,
remove_liquidity_amount,
NonZero::new(remove_liquidity_amount)
.expect("Remove liquidity amount must be nonzero"),
min_amount_to_remove_token_a,
min_amount_to_remove_token_b,
)
+2 -1
View File
@@ -6,4 +6,5 @@ edition = "2024"
[dependencies]
nssa_core.workspace = true
serde.workspace = true
risc0-zkvm.workspace = true
risc0-zkvm.workspace = true
borsh.workspace = true
+14 -65
View File
@@ -1,5 +1,6 @@
//! This crate contains core data structures and utilities for the AMM Program.
use borsh::{BorshDeserialize, BorshSerialize};
use nssa_core::{
account::{AccountId, Data},
program::{PdaSeed, ProgramId},
@@ -74,9 +75,7 @@ pub enum Instruction {
},
}
const POOL_DEFINITION_DATA_SIZE: usize = 225;
#[derive(Clone, Default)]
#[derive(Clone, Default, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct PoolDefinition {
pub definition_token_a_id: AccountId,
pub definition_token_b_id: AccountId,
@@ -94,73 +93,23 @@ pub struct PoolDefinition {
pub active: bool,
}
impl PoolDefinition {
pub fn into_data(self) -> Data {
let mut bytes = [0; POOL_DEFINITION_DATA_SIZE];
bytes[0..32].copy_from_slice(&self.definition_token_a_id.to_bytes());
bytes[32..64].copy_from_slice(&self.definition_token_b_id.to_bytes());
bytes[64..96].copy_from_slice(&self.vault_a_id.to_bytes());
bytes[96..128].copy_from_slice(&self.vault_b_id.to_bytes());
bytes[128..160].copy_from_slice(&self.liquidity_pool_id.to_bytes());
bytes[160..176].copy_from_slice(&self.liquidity_pool_supply.to_le_bytes());
bytes[176..192].copy_from_slice(&self.reserve_a.to_le_bytes());
bytes[192..208].copy_from_slice(&self.reserve_b.to_le_bytes());
bytes[208..224].copy_from_slice(&self.fees.to_le_bytes());
bytes[224] = self.active as u8;
impl TryFrom<&Data> for PoolDefinition {
type Error = std::io::Error;
bytes
.to_vec()
.try_into()
.expect("225 bytes should fit into Data")
fn try_from(data: &Data) -> Result<Self, Self::Error> {
PoolDefinition::try_from_slice(data.as_ref())
}
}
pub fn parse(data: &[u8]) -> Option<Self> {
if data.len() != POOL_DEFINITION_DATA_SIZE {
None
} else {
let definition_token_a_id = AccountId::new(data[0..32].try_into().expect("Parse data: The AMM program must be provided a valid AccountId for Token A definition"));
let definition_token_b_id = AccountId::new(data[32..64].try_into().expect("Parse data: The AMM program must be provided a valid AccountId for Vault B definition"));
let vault_a_id = AccountId::new(data[64..96].try_into().expect(
"Parse data: The AMM program must be provided a valid AccountId for Vault A",
));
let vault_b_id = AccountId::new(data[96..128].try_into().expect(
"Parse data: The AMM program must be provided a valid AccountId for Vault B",
));
let liquidity_pool_id = AccountId::new(data[128..160].try_into().expect("Parse data: The AMM program must be provided a valid AccountId for Token liquidity pool definition"));
let liquidity_pool_supply = u128::from_le_bytes(data[160..176].try_into().expect(
"Parse data: The AMM program must be provided a valid u128 for liquidity cap",
));
let reserve_a = u128::from_le_bytes(data[176..192].try_into().expect(
"Parse data: The AMM program must be provided a valid u128 for reserve A balance",
));
let reserve_b = u128::from_le_bytes(data[192..208].try_into().expect(
"Parse data: The AMM program must be provided a valid u128 for reserve B balance",
));
let fees = u128::from_le_bytes(
data[208..224]
.try_into()
.expect("Parse data: The AMM program must be provided a valid u128 for fees"),
);
impl From<&PoolDefinition> for Data {
fn from(definition: &PoolDefinition) -> Self {
// Using size_of_val as size hint for Vec allocation
let mut data = Vec::with_capacity(std::mem::size_of_val(definition));
let active = match data[224] {
0 => false,
1 => true,
_ => panic!("Parse data: The AMM program must be provided a valid bool for active"),
};
BorshSerialize::serialize(definition, &mut data)
.expect("Serialization to Vec should not fail");
Some(Self {
definition_token_a_id,
definition_token_b_id,
vault_a_id,
vault_b_id,
liquidity_pool_id,
liquidity_pool_supply,
reserve_a,
reserve_b,
fees,
active,
})
}
Data::try_from(data).expect("Token definition encoded data should fit into Data")
}
}
+47 -38
View File
@@ -1,6 +1,8 @@
use std::num::NonZeroU128;
use amm_core::{PoolDefinition, compute_liquidity_token_pda_seed};
use nssa_core::{
account::AccountWithMetadata,
account::{AccountWithMetadata, Data},
program::{AccountPostState, ChainedCall},
};
@@ -13,32 +15,33 @@ pub fn add_liquidity(
user_holding_a: AccountWithMetadata,
user_holding_b: AccountWithMetadata,
user_holding_lp: AccountWithMetadata,
min_amount_liquidity: u128,
min_amount_liquidity: NonZeroU128,
max_amount_to_add_token_a: u128,
max_amount_to_add_token_b: u128,
) -> (Vec<AccountPostState>, Vec<ChainedCall>) {
// 1. Fetch Pool state
let pool_def_data = PoolDefinition::parse(&pool.account.data)
let pool_def_data = PoolDefinition::try_from(&pool.account.data)
.expect("Add liquidity: AMM Program expects valid Pool Definition Account");
if vault_a.account_id != pool_def_data.vault_a_id {
panic!("Vault A was not provided");
}
if pool_def_data.liquidity_pool_id != pool_definition_lp.account_id {
panic!("LP definition mismatch");
}
assert_eq!(
vault_a.account_id, pool_def_data.vault_a_id,
"Vault A was not provided"
);
if vault_b.account_id != pool_def_data.vault_b_id {
panic!("Vault B was not provided");
}
assert_eq!(
pool_def_data.liquidity_pool_id, pool_definition_lp.account_id,
"LP definition mismatch"
);
if max_amount_to_add_token_a == 0 || max_amount_to_add_token_b == 0 {
panic!("Both max-balances must be nonzero");
}
assert_eq!(
vault_b.account_id, pool_def_data.vault_b_id,
"Vault B was not provided"
);
if min_amount_liquidity == 0 {
panic!("Min-lp must be nonzero");
}
assert!(
max_amount_to_add_token_a != 0 && max_amount_to_add_token_b != 0,
"Both max-balances must be nonzero"
);
// 2. Determine deposit amount
let vault_b_token_holding = token_core::TokenHolding::try_from(&vault_b.account.data)
@@ -65,13 +68,16 @@ pub fn add_liquidity(
);
};
if pool_def_data.reserve_a == 0 || pool_def_data.reserve_b == 0 {
panic!("Reserves must be nonzero");
}
if vault_a_balance < pool_def_data.reserve_a || vault_b_balance < pool_def_data.reserve_b {
panic!("Vaults' balances must be at least the reserve amounts");
}
assert!(pool_def_data.reserve_a != 0, "Reserves must be nonzero");
assert!(pool_def_data.reserve_b != 0, "Reserves must be nonzero");
assert!(
vault_a_balance >= pool_def_data.reserve_a,
"Vaults' balances must be at least the reserve amounts"
);
assert!(
vault_b_balance >= pool_def_data.reserve_b,
"Vaults' balances must be at least the reserve amounts"
);
// Calculate actual_amounts
let ideal_a: u128 =
@@ -91,13 +97,17 @@ pub fn add_liquidity(
};
// 3. Validate amounts
if max_amount_to_add_token_a < actual_amount_a || max_amount_to_add_token_b < actual_amount_b {
panic!("Actual trade amounts cannot exceed max_amounts");
}
assert!(
max_amount_to_add_token_a >= actual_amount_a,
"Actual trade amounts cannot exceed max_amounts"
);
assert!(
max_amount_to_add_token_b >= actual_amount_b,
"Actual trade amounts cannot exceed max_amounts"
);
if actual_amount_a == 0 || actual_amount_b == 0 {
panic!("A trade amount is 0");
}
assert!(actual_amount_a != 0, "A trade amount is 0");
assert!(actual_amount_b != 0, "A trade amount is 0");
// 4. Calculate LP to mint
let delta_lp = std::cmp::min(
@@ -105,13 +115,12 @@ pub fn add_liquidity(
pool_def_data.liquidity_pool_supply * actual_amount_b / pool_def_data.reserve_b,
);
if delta_lp == 0 {
panic!("Payable LP must be nonzero");
}
assert!(delta_lp != 0, "Payable LP must be nonzero");
if delta_lp < min_amount_liquidity {
panic!("Payable LP is less than provided minimum LP amount");
}
assert!(
delta_lp >= min_amount_liquidity.into(),
"Payable LP is less than provided minimum LP amount"
);
// 5. Update pool account
let mut pool_post = pool.account.clone();
@@ -122,7 +131,7 @@ pub fn add_liquidity(
..pool_def_data
};
pool_post.data = pool_post_definition.into_data();
pool_post.data = Data::from(&pool_post_definition);
let token_program_id = user_holding_a.account.program_owner;
// Chain call for Token A (UserHoldingA -> Vault_A)
+1 -1
View File
@@ -1,6 +1,6 @@
//! The AMM Program implementation.
pub use token_core as core;
pub use amm_core as core;
pub mod add;
pub mod new_definition;
+46 -44
View File
@@ -1,9 +1,11 @@
use std::num::NonZeroU128;
use amm_core::{
PoolDefinition, compute_liquidity_token_pda, compute_liquidity_token_pda_seed,
compute_pool_pda, compute_vault_pda,
};
use nssa_core::{
account::{Account, AccountWithMetadata},
account::{Account, AccountWithMetadata, Data},
program::{AccountPostState, ChainedCall, ProgramId},
};
@@ -20,11 +22,6 @@ pub fn new_definition(
token_b_amount: NonZeroU128,
amm_program_id: ProgramId,
) -> (Vec<AccountPostState>, Vec<ChainedCall>) {
// Prevents pool constant coefficient (k) from being 0.
if token_a_amount == 0 || token_b_amount == 0 {
panic!("Balances must be nonzero")
}
// Verify token_a and token_b are different
let definition_token_a_id = token_core::TokenHolding::try_from(&user_holding_a.account.data)
.expect("New definition: AMM Program expects valid Token Holding account for Token A")
@@ -36,43 +33,48 @@ pub fn new_definition(
// both instances of the same token program
let token_program = user_holding_a.account.program_owner;
if user_holding_b.account.program_owner != token_program {
panic!("User Token holdings must use the same Token Program");
}
if definition_token_a_id == definition_token_b_id {
panic!("Cannot set up a swap for a token with itself")
}
if pool.account_id
!= compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id)
{
panic!("Pool Definition Account ID does not match PDA");
}
if vault_a.account_id
!= compute_vault_pda(amm_program_id, pool.account_id, definition_token_a_id)
|| vault_b.account_id
!= compute_vault_pda(amm_program_id, pool.account_id, definition_token_b_id)
{
panic!("Vault ID does not match PDA");
}
if pool_definition_lp.account_id != compute_liquidity_token_pda(amm_program_id, pool.account_id)
{
panic!("Liquidity pool Token Definition Account ID does not match PDA");
}
assert_eq!(
user_holding_b.account.program_owner, token_program,
"User Token holdings must use the same Token Program"
);
assert!(
definition_token_a_id != definition_token_b_id,
"Cannot set up a swap for a token with itself"
);
assert_eq!(
pool.account_id,
compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id),
"Pool Definition Account ID does not match PDA"
);
assert_eq!(
vault_a.account_id,
compute_vault_pda(amm_program_id, pool.account_id, definition_token_a_id),
"Vault ID does not match PDA"
);
assert_eq!(
vault_b.account_id,
compute_vault_pda(amm_program_id, pool.account_id, definition_token_b_id),
"Vault ID does not match PDA"
);
assert_eq!(
pool_definition_lp.account_id,
compute_liquidity_token_pda(amm_program_id, pool.account_id),
"Liquidity pool Token Definition Account ID does not match PDA"
);
// TODO: return here
// Verify that Pool Account is not active
let pool_account_data = if pool.account == Account::default() {
PoolDefinition::default()
} else {
PoolDefinition::parse(&pool.account.data).expect("AMM program expects a valid Pool account")
PoolDefinition::try_from(&pool.account.data)
.expect("AMM program expects a valid Pool account")
};
if pool_account_data.active {
panic!("Cannot initialize an active Pool Definition")
}
assert!(
!pool_account_data.active,
"Cannot initialize an active Pool Definition"
);
// LP Token minting calculation
// We assume LP is based on the initial deposit amount for Token_A.
@@ -85,14 +87,14 @@ 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,
reserve_a: token_a_amount,
reserve_b: token_b_amount,
liquidity_pool_supply: token_a_amount.into(),
reserve_a: token_a_amount.into(),
reserve_b: token_b_amount.into(),
fees: 0u128, // TODO: we assume all fees are 0 for now.
active: true,
};
pool_post.data = pool_post_definition.into_data();
pool_post.data = Data::from(&pool_post_definition);
let pool_post: AccountPostState = if pool.account == Account::default() {
AccountPostState::new_claimed(pool_post.clone())
} else {
@@ -106,7 +108,7 @@ pub fn new_definition(
token_program_id,
vec![user_holding_a.clone(), vault_a.clone()],
&token_core::Instruction::Transfer {
amount_to_transfer: token_a_amount,
amount_to_transfer: token_a_amount.into(),
},
);
// Chain call for Token B (user_holding_b -> Vault_B)
@@ -114,7 +116,7 @@ pub fn new_definition(
token_program_id,
vec![user_holding_b.clone(), vault_b.clone()],
&token_core::Instruction::Transfer {
amount_to_transfer: token_b_amount,
amount_to_transfer: token_b_amount.into(),
},
);
@@ -122,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,
total_supply: token_a_amount.into(),
}
} else {
token_core::Instruction::Mint {
amount_to_mint: token_a_amount,
amount_to_mint: token_a_amount.into(),
}
};
+46 -37
View File
@@ -1,6 +1,8 @@
use std::num::NonZeroU128;
use amm_core::{PoolDefinition, compute_liquidity_token_pda_seed, compute_vault_pda_seed};
use nssa_core::{
account::AccountWithMetadata,
account::{AccountWithMetadata, Data},
program::{AccountPostState, ChainedCall},
};
@@ -13,29 +15,29 @@ pub fn remove_liquidity(
user_holding_a: AccountWithMetadata,
user_holding_b: AccountWithMetadata,
user_holding_lp: AccountWithMetadata,
remove_liquidity_amount: u128,
remove_liquidity_amount: NonZeroU128,
min_amount_to_remove_token_a: u128,
min_amount_to_remove_token_b: u128,
) -> (Vec<AccountPostState>, Vec<ChainedCall>) {
let remove_liquidity_amount: u128 = remove_liquidity_amount.into();
// 1. Fetch Pool state
let pool_def_data = PoolDefinition::parse(&pool.account.data)
let pool_def_data = PoolDefinition::try_from(&pool.account.data)
.expect("Remove liquidity: AMM Program expects a valid Pool Definition Account");
if !pool_def_data.active {
panic!("Pool is inactive");
}
if pool_def_data.liquidity_pool_id != pool_definition_lp.account_id {
panic!("LP definition mismatch");
}
if vault_a.account_id != pool_def_data.vault_a_id {
panic!("Vault A was not provided");
}
if vault_b.account_id != pool_def_data.vault_b_id {
panic!("Vault B was not provided");
}
assert!(pool_def_data.active, "Pool is inactive");
assert_eq!(
pool_def_data.liquidity_pool_id, pool_definition_lp.account_id,
"LP definition mismatch"
);
assert_eq!(
vault_a.account_id, pool_def_data.vault_a_id,
"Vault A was not provided"
);
assert_eq!(
vault_b.account_id, pool_def_data.vault_b_id,
"Vault B was not provided"
);
// Vault addresses do not need to be checked with PDA
// calculation for setting authorization since stored
@@ -45,13 +47,14 @@ pub fn remove_liquidity(
running_vault_a.is_authorized = true;
running_vault_b.is_authorized = true;
if min_amount_to_remove_token_a == 0 || min_amount_to_remove_token_b == 0 {
panic!("Minimum withdraw amount must be nonzero");
}
if remove_liquidity_amount == 0 {
panic!("Liquidity amount must be nonzero");
}
assert!(
min_amount_to_remove_token_a != 0,
"Minimum withdraw amount must be nonzero"
);
assert!(
min_amount_to_remove_token_b != 0,
"Minimum withdraw amount must be nonzero"
);
// 2. Compute withdrawal amounts
let user_holding_lp_data = token_core::TokenHolding::try_from(&user_holding_lp.account.data)
@@ -66,11 +69,15 @@ pub fn remove_liquidity(
);
};
if user_lp_balance > pool_def_data.liquidity_pool_supply
|| user_holding_lp_data.definition_id() != pool_def_data.liquidity_pool_id
{
panic!("Invalid liquidity account provided");
}
assert!(
user_lp_balance <= pool_def_data.liquidity_pool_supply,
"Invalid liquidity account provided"
);
assert_eq!(
user_holding_lp_data.definition_id(),
pool_def_data.liquidity_pool_id,
"Invalid liquidity account provided"
);
let withdraw_amount_a =
(pool_def_data.reserve_a * remove_liquidity_amount) / pool_def_data.liquidity_pool_supply;
@@ -78,12 +85,14 @@ pub fn remove_liquidity(
(pool_def_data.reserve_b * remove_liquidity_amount) / pool_def_data.liquidity_pool_supply;
// 3. Validate and slippage check
if withdraw_amount_a < min_amount_to_remove_token_a {
panic!("Insufficient minimal withdraw amount (Token A) provided for liquidity amount");
}
if withdraw_amount_b < min_amount_to_remove_token_b {
panic!("Insufficient minimal withdraw amount (Token B) provided for liquidity amount");
}
assert!(
withdraw_amount_a >= min_amount_to_remove_token_a,
"Insufficient minimal withdraw amount (Token A) provided for liquidity amount"
);
assert!(
withdraw_amount_b >= min_amount_to_remove_token_b,
"Insufficient minimal withdraw amount (Token B) provided for liquidity amount"
);
// 4. Calculate LP to reduce cap by
let delta_lp: u128 = (pool_def_data.liquidity_pool_supply * remove_liquidity_amount)
@@ -101,7 +110,7 @@ pub fn remove_liquidity(
..pool_def_data.clone()
};
pool_post.data = pool_post_definition.into_data();
pool_post.data = Data::from(&pool_post_definition);
let token_program_id = user_holding_a.account.program_owner;
+32 -33
View File
@@ -1,6 +1,6 @@
pub use amm_core::{PoolDefinition, compute_liquidity_token_pda_seed, compute_vault_pda_seed};
use nssa_core::{
account::{AccountId, AccountWithMetadata},
account::{AccountId, AccountWithMetadata, Data},
program::{AccountPostState, ChainedCall},
};
@@ -16,20 +16,18 @@ pub fn swap(
token_in_id: AccountId,
) -> (Vec<AccountPostState>, Vec<ChainedCall>) {
// Verify vaults are in fact vaults
let pool_def_data = PoolDefinition::parse(&pool.account.data)
let pool_def_data = PoolDefinition::try_from(&pool.account.data)
.expect("Swap: AMM Program expects a valid Pool Definition Account");
if !pool_def_data.active {
panic!("Pool is inactive");
}
if vault_a.account_id != pool_def_data.vault_a_id {
panic!("Vault A was not provided");
}
if vault_b.account_id != pool_def_data.vault_b_id {
panic!("Vault B was not provided");
}
assert!(pool_def_data.active, "Pool is inactive");
assert_eq!(
vault_a.account_id, pool_def_data.vault_a_id,
"Vault A was not provided"
);
assert_eq!(
vault_b.account_id, pool_def_data.vault_b_id,
"Vault B was not provided"
);
// fetch pool reserves
// validates reserves is at least the vaults' balances
@@ -42,9 +40,11 @@ pub fn swap(
else {
panic!("Swap: AMM Program expects a valid Fungible Token Holding Account for Vault A");
};
if vault_a_balance < pool_def_data.reserve_a {
panic!("Reserve for Token A exceeds vault balance");
}
assert!(
vault_a_balance >= pool_def_data.reserve_a,
"Reserve for Token A exceeds vault balance"
);
let vault_b_token_holding = token_core::TokenHolding::try_from(&vault_b.account.data)
.expect("Swap: AMM Program expects a valid Token Holding Account for Vault B");
@@ -56,9 +56,10 @@ pub fn swap(
panic!("Swap: AMM Program expects a valid Fungible Token Holding Account for Vault B");
};
if vault_b_balance < pool_def_data.reserve_b {
panic!("Reserve for Token B exceeds vault balance");
}
assert!(
vault_b_balance >= pool_def_data.reserve_b,
"Reserve for Token B exceeds vault balance"
);
let (chained_calls, [deposit_a, withdraw_a], [deposit_b, withdraw_b]) =
if token_in_id == pool_def_data.definition_token_a_id {
@@ -69,7 +70,8 @@ pub fn swap(
user_holding_b.clone(),
swap_amount_in,
min_amount_out,
&[pool_def_data.reserve_a, pool_def_data.reserve_b],
pool_def_data.reserve_a,
pool_def_data.reserve_b,
pool.account_id,
);
@@ -82,7 +84,8 @@ pub fn swap(
user_holding_a.clone(),
swap_amount_in,
min_amount_out,
&[pool_def_data.reserve_b, pool_def_data.reserve_a],
pool_def_data.reserve_b,
pool_def_data.reserve_a,
pool.account_id,
);
@@ -99,7 +102,7 @@ pub fn swap(
..pool_def_data
};
pool_post.data = pool_post_definition.into_data();
pool_post.data = Data::from(&pool_post_definition);
let post_states = vec![
AccountPostState::new(pool_post.clone()),
@@ -120,12 +123,10 @@ fn swap_logic(
user_withdraw: AccountWithMetadata,
swap_amount_in: u128,
min_amount_out: u128,
reserve_amounts: &[u128],
reserve_deposit_vault_amount: u128,
reserve_withdraw_vault_amount: u128,
pool_id: AccountId,
) -> (Vec<ChainedCall>, u128, u128) {
let reserve_deposit_vault_amount = reserve_amounts[0];
let reserve_withdraw_vault_amount = reserve_amounts[1];
// Compute withdraw amount
// Maintains pool constant product
// k = pool_def_data.reserve_a * pool_def_data.reserve_b;
@@ -133,13 +134,11 @@ fn swap_logic(
/ (reserve_deposit_vault_amount + swap_amount_in);
// Slippage check
if min_amount_out > withdraw_amount {
panic!("Withdraw amount is less than minimal amount out");
}
if withdraw_amount == 0 {
panic!("Withdraw amount should be nonzero");
}
assert!(
min_amount_out <= withdraw_amount,
"Withdraw amount is less than minimal amount out"
);
assert!(withdraw_amount != 0, "Withdraw amount should be nonzero");
let token_program_id = user_deposit.account.program_owner;
+55 -87
View File
@@ -1,5 +1,7 @@
#![cfg(test)]
use std::num::NonZero;
use amm_core::{
PoolDefinition, compute_liquidity_token_pda, compute_liquidity_token_pda_seed,
compute_pool_pda, compute_vault_pda, compute_vault_pda_seed,
@@ -630,7 +632,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -654,7 +656,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -678,7 +680,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -702,7 +704,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -726,7 +728,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -750,7 +752,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -774,7 +776,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -798,7 +800,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -822,7 +824,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -846,7 +848,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -870,7 +872,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -894,7 +896,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -950,7 +952,7 @@ impl AccountForTests {
account: Account {
program_owner: ProgramId::default(),
balance: 0u128,
data: PoolDefinition::into_data(PoolDefinition {
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(),
@@ -996,7 +998,7 @@ fn test_call_add_liquidity_vault_a_omitted() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
BalanceForTests::add_max_amount_a(),
BalanceForTests::add_max_amount_b(),
);
@@ -1013,7 +1015,7 @@ fn test_call_add_liquidity_vault_b_omitted() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
BalanceForTests::add_max_amount_a(),
BalanceForTests::add_max_amount_b(),
);
@@ -1030,7 +1032,7 @@ fn test_call_add_liquidity_lp_definition_mismatch() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
BalanceForTests::add_max_amount_a(),
BalanceForTests::add_max_amount_b(),
);
@@ -1047,7 +1049,7 @@ fn test_call_add_liquidity_zero_balance_1() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
0,
BalanceForTests::add_max_amount_b(),
);
@@ -1064,29 +1066,12 @@ fn test_call_add_liquidity_zero_balance_2() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
0,
BalanceForTests::add_max_amount_a(),
);
}
#[should_panic(expected = "Min-lp must be nonzero")]
#[test]
fn test_call_add_liquidity_zero_min_lp() {
let _post_states = add_liquidity(
AccountForTests::pool_definition_init(),
AccountForTests::vault_a_init(),
AccountForTests::vault_b_init(),
AccountForTests::pool_lp_init(),
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
0,
BalanceForTests::add_max_amount_a(),
BalanceForTests::add_max_amount_b(),
);
}
#[should_panic(expected = "Vaults' balances must be at least the reserve amounts")]
#[test]
fn test_call_add_liquidity_vault_insufficient_balance_1() {
@@ -1098,7 +1083,7 @@ fn test_call_add_liquidity_vault_insufficient_balance_1() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_max_amount_a(),
NonZero::new(BalanceForTests::add_max_amount_a()).unwrap(),
BalanceForTests::add_max_amount_b(),
BalanceForTests::add_min_amount_lp(),
);
@@ -1115,7 +1100,7 @@ fn test_call_add_liquidity_vault_insufficient_balance_2() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_max_amount_a(),
NonZero::new(BalanceForTests::add_max_amount_a()).unwrap(),
BalanceForTests::add_max_amount_b(),
BalanceForTests::add_min_amount_lp(),
);
@@ -1132,7 +1117,7 @@ fn test_call_add_liquidity_actual_amount_zero_1() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
BalanceForTests::add_max_amount_a(),
BalanceForTests::add_max_amount_b(),
);
@@ -1149,7 +1134,7 @@ fn test_call_add_liquidity_actual_amount_zero_2() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
BalanceForTests::add_max_amount_a_low(),
BalanceForTests::add_max_amount_b_low(),
);
@@ -1166,7 +1151,7 @@ fn test_call_add_liquidity_reserves_zero_1() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
BalanceForTests::add_max_amount_a(),
BalanceForTests::add_max_amount_b(),
);
@@ -1183,7 +1168,7 @@ fn test_call_add_liquidity_reserves_zero_2() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
BalanceForTests::add_max_amount_a(),
BalanceForTests::add_max_amount_b(),
);
@@ -1200,7 +1185,7 @@ fn test_call_add_liquidity_payable_lp_zero() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
BalanceForTests::add_max_amount_a_low(),
BalanceForTests::add_max_amount_b_low(),
);
@@ -1216,7 +1201,7 @@ fn test_call_add_liquidity_chained_call_successsful() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::add_min_amount_lp(),
NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(),
BalanceForTests::add_max_amount_a(),
BalanceForTests::add_max_amount_b(),
);
@@ -1245,7 +1230,7 @@ fn test_call_remove_liquidity_vault_a_omitted() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::remove_amount_lp(),
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
BalanceForTests::remove_min_amount_a(),
BalanceForTests::remove_min_amount_b(),
);
@@ -1262,7 +1247,7 @@ fn test_call_remove_liquidity_vault_b_omitted() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::remove_amount_lp(),
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
BalanceForTests::remove_min_amount_a(),
BalanceForTests::remove_min_amount_b(),
);
@@ -1279,7 +1264,7 @@ fn test_call_remove_liquidity_lp_def_mismatch() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::remove_amount_lp(),
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
BalanceForTests::remove_min_amount_a(),
BalanceForTests::remove_min_amount_b(),
);
@@ -1297,7 +1282,7 @@ fn test_call_remove_liquidity_insufficient_liquidity_amount() {
AccountForTests::user_holding_b(),
AccountForTests::user_holding_a(), /* different token account than lp to create desired
* error */
BalanceForTests::remove_amount_lp(),
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
BalanceForTests::remove_min_amount_a(),
BalanceForTests::remove_min_amount_b(),
);
@@ -1316,7 +1301,7 @@ fn test_call_remove_liquidity_insufficient_balance_1() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::remove_amount_lp_1(),
NonZero::new(BalanceForTests::remove_amount_lp_1()).unwrap(),
BalanceForTests::remove_min_amount_a(),
BalanceForTests::remove_min_amount_b(),
);
@@ -1335,7 +1320,7 @@ fn test_call_remove_liquidity_insufficient_balance_2() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::remove_amount_lp(),
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
BalanceForTests::remove_min_amount_a(),
BalanceForTests::remove_min_amount_b(),
);
@@ -1352,7 +1337,7 @@ fn test_call_remove_liquidity_min_bal_zero_1() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::remove_amount_lp(),
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
0,
BalanceForTests::remove_min_amount_b(),
);
@@ -1369,29 +1354,12 @@ fn test_call_remove_liquidity_min_bal_zero_2() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::remove_amount_lp(),
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
BalanceForTests::remove_min_amount_a(),
0,
);
}
#[should_panic(expected = "Liquidity amount must be nonzero")]
#[test]
fn test_call_remove_liquidity_lp_bal_zero() {
let _post_states = remove_liquidity(
AccountForTests::pool_definition_init(),
AccountForTests::vault_a_init(),
AccountForTests::vault_b_init(),
AccountForTests::pool_lp_init(),
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
0,
BalanceForTests::remove_min_amount_a(),
BalanceForTests::remove_min_amount_b(),
);
}
#[test]
fn test_call_remove_liquidity_chained_call_successful() {
let (post_states, chained_calls) = remove_liquidity(
@@ -1402,7 +1370,7 @@ fn test_call_remove_liquidity_chained_call_successful() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_init(),
BalanceForTests::remove_amount_lp(),
NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(),
BalanceForTests::remove_min_amount_a(),
BalanceForTests::remove_min_amount_b_low(),
);
@@ -1431,8 +1399,8 @@ fn test_call_new_definition_with_zero_balance_1() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_uninit(),
0,
BalanceForTests::vault_b_reserve_init(),
NonZero::new(0).expect("Balances must be nonzero"),
NonZero::new(BalanceForTests::vault_b_reserve_init()).unwrap(),
AMM_PROGRAM_ID,
);
}
@@ -1448,8 +1416,8 @@ fn test_call_new_definition_with_zero_balance_2() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_uninit(),
BalanceForTests::vault_a_reserve_init(),
0,
NonZero::new(BalanceForTests::vault_a_reserve_init()).unwrap(),
NonZero::new(0).expect("Balances must be nonzero"),
AMM_PROGRAM_ID,
);
}
@@ -1465,8 +1433,8 @@ fn test_call_new_definition_same_token_definition() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_a(),
AccountForTests::user_holding_lp_uninit(),
BalanceForTests::vault_a_reserve_init(),
BalanceForTests::vault_b_reserve_init(),
NonZero::new(BalanceForTests::vault_a_reserve_init()).unwrap(),
NonZero::new(BalanceForTests::vault_b_reserve_init()).unwrap(),
AMM_PROGRAM_ID,
);
}
@@ -1482,8 +1450,8 @@ fn test_call_new_definition_wrong_liquidity_id() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_uninit(),
BalanceForTests::vault_a_reserve_init(),
BalanceForTests::vault_b_reserve_init(),
NonZero::new(BalanceForTests::vault_a_reserve_init()).unwrap(),
NonZero::new(BalanceForTests::vault_b_reserve_init()).unwrap(),
AMM_PROGRAM_ID,
);
}
@@ -1499,8 +1467,8 @@ fn test_call_new_definition_wrong_pool_id() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_uninit(),
BalanceForTests::vault_a_reserve_init(),
BalanceForTests::vault_b_reserve_init(),
NonZero::new(BalanceForTests::vault_a_reserve_init()).unwrap(),
NonZero::new(BalanceForTests::vault_b_reserve_init()).unwrap(),
AMM_PROGRAM_ID,
);
}
@@ -1516,8 +1484,8 @@ fn test_call_new_definition_wrong_vault_id_1() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_uninit(),
BalanceForTests::vault_a_reserve_init(),
BalanceForTests::vault_b_reserve_init(),
NonZero::new(BalanceForTests::vault_a_reserve_init()).unwrap(),
NonZero::new(BalanceForTests::vault_b_reserve_init()).unwrap(),
AMM_PROGRAM_ID,
);
}
@@ -1533,8 +1501,8 @@ fn test_call_new_definition_wrong_vault_id_2() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_uninit(),
BalanceForTests::vault_a_reserve_init(),
BalanceForTests::vault_b_reserve_init(),
NonZero::new(BalanceForTests::vault_a_reserve_init()).unwrap(),
NonZero::new(BalanceForTests::vault_b_reserve_init()).unwrap(),
AMM_PROGRAM_ID,
);
}
@@ -1550,8 +1518,8 @@ fn test_call_new_definition_cannot_initialize_active_pool() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_uninit(),
BalanceForTests::vault_a_reserve_init(),
BalanceForTests::vault_b_reserve_init(),
NonZero::new(BalanceForTests::vault_a_reserve_init()).unwrap(),
NonZero::new(BalanceForTests::vault_b_reserve_init()).unwrap(),
AMM_PROGRAM_ID,
);
}
@@ -1567,8 +1535,8 @@ fn test_call_new_definition_chained_call_successful() {
AccountForTests::user_holding_a(),
AccountForTests::user_holding_b(),
AccountForTests::user_holding_lp_uninit(),
BalanceForTests::vault_a_reserve_init(),
BalanceForTests::vault_b_reserve_init(),
NonZero::new(BalanceForTests::vault_a_reserve_init()).unwrap(),
NonZero::new(BalanceForTests::vault_b_reserve_init()).unwrap(),
AMM_PROGRAM_ID,
);