198 lines
6.2 KiB
Rust
Raw Normal View History

2026-01-23 16:30:54 -05:00
//! This crate contains core data structures and utilities for the AMM Program.
2026-01-22 20:48:05 -05:00
2026-01-27 18:24:03 -05:00
use borsh::{BorshDeserialize, BorshSerialize};
2026-01-23 16:30:54 -05:00
use nssa_core::{
account::{AccountId, Data},
program::{PdaSeed, ProgramId},
};
2026-01-22 20:48:05 -05:00
use serde::{Deserialize, Serialize};
/// AMM Program Instruction.
#[derive(Serialize, Deserialize)]
pub enum Instruction {
2026-01-23 16:30:54 -05:00
/// Initializes a new Pool (or re-initializes an inactive Pool).
2026-01-22 20:48:05 -05:00
///
/// Required accounts:
2026-01-23 16:30:54 -05:00
/// - AMM Pool
/// - Vault Holding Account for Token A
/// - Vault Holding Account for Token B
/// - Pool Liquidity Token Definition
/// - User Holding Account for Token A (authorized)
/// - User Holding Account for Token B (authorized)
/// - User Holding Account for Pool Liquidity
NewDefinition {
token_a_amount: u128,
token_b_amount: u128,
amm_program_id: ProgramId,
2026-01-22 20:48:05 -05:00
},
2026-01-23 16:30:54 -05:00
/// Adds liquidity to the Pool
2026-01-22 20:48:05 -05:00
///
/// Required accounts:
2026-01-23 16:30:54 -05:00
/// - AMM Pool (initialized)
/// - Vault Holding Account for Token A (initialized)
/// - Vault Holding Account for Token B (initialized)
/// - Pool Liquidity Token Definition (initialized)
/// - User Holding Account for Token A (authorized)
/// - User Holding Account for Token B (authorized)
/// - User Holding Account for Pool Liquidity
AddLiquidity {
min_amount_liquidity: u128,
max_amount_to_add_token_a: u128,
max_amount_to_add_token_b: u128,
},
2026-01-22 20:48:05 -05:00
2026-01-23 16:30:54 -05:00
/// Removes liquidity from the Pool
2026-01-22 20:48:05 -05:00
///
/// Required accounts:
2026-01-23 16:30:54 -05:00
/// - AMM Pool (initialized)
/// - Vault Holding Account for Token A (initialized)
/// - Vault Holding Account for Token B (initialized)
/// - Pool Liquidity Token Definition (initialized)
/// - User Holding Account for Token A (initialized)
/// - User Holding Account for Token B (initialized)
/// - User Holding Account for Pool Liquidity (authorized)
RemoveLiquidity {
remove_liquidity_amount: u128,
min_amount_to_remove_token_a: u128,
min_amount_to_remove_token_b: u128,
},
2026-01-22 20:48:05 -05:00
2026-01-23 16:30:54 -05:00
/// Swap some quantity of Tokens (either Token A or Token B)
/// while maintaining the Pool constant product.
2026-01-22 20:48:05 -05:00
///
/// Required accounts:
2026-01-23 16:30:54 -05:00
/// - AMM Pool (initialized)
/// - Vault Holding Account for Token A (initialized)
/// - Vault Holding Account for Token B (initialized)
/// - User Holding Account for Token A
2026-01-23 16:58:25 -05:00
/// - User Holding Account for Token B Either User Holding Account for Token A or Token B is
/// authorized.
2026-01-23 16:30:54 -05:00
Swap {
swap_amount_in: u128,
min_amount_out: u128,
token_definition_id_in: AccountId,
2026-01-22 20:48:05 -05:00
},
}
2026-01-27 18:24:03 -05:00
#[derive(Clone, Default, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
2026-01-23 16:30:54 -05:00
pub struct PoolDefinition {
pub definition_token_a_id: AccountId,
pub definition_token_b_id: AccountId,
pub vault_a_id: AccountId,
pub vault_b_id: AccountId,
pub liquidity_pool_id: AccountId,
pub liquidity_pool_supply: u128,
pub reserve_a: u128,
pub reserve_b: u128,
/// Fees are currently not used
pub fees: u128,
/// A pool becomes inactive (active = false)
/// once all of its liquidity has been removed (e.g., reserves are emptied and
/// liquidity_pool_supply = 0)
pub active: bool,
2026-01-22 20:48:05 -05:00
}
2026-01-27 18:24:03 -05:00
impl TryFrom<&Data> for PoolDefinition {
type Error = std::io::Error;
2026-01-23 16:30:54 -05:00
2026-01-27 18:24:03 -05:00
fn try_from(data: &Data) -> Result<Self, Self::Error> {
PoolDefinition::try_from_slice(data.as_ref())
2026-01-22 20:48:05 -05:00
}
2026-01-27 18:24:03 -05:00
}
2026-01-22 20:48:05 -05:00
2026-01-27 18:24:03 -05:00
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));
2026-01-23 16:30:54 -05:00
2026-01-27 18:24:03 -05:00
BorshSerialize::serialize(definition, &mut data)
.expect("Serialization to Vec should not fail");
2026-01-23 16:30:54 -05:00
2026-01-27 18:24:03 -05:00
Data::try_from(data).expect("Token definition encoded data should fit into Data")
2026-01-22 20:48:05 -05:00
}
2026-01-23 16:30:54 -05:00
}
2026-01-22 20:48:05 -05:00
2026-01-23 16:30:54 -05:00
pub fn compute_pool_pda(
amm_program_id: ProgramId,
definition_token_a_id: AccountId,
definition_token_b_id: AccountId,
) -> AccountId {
AccountId::from((
&amm_program_id,
&compute_pool_pda_seed(definition_token_a_id, definition_token_b_id),
))
2026-01-22 20:48:05 -05:00
}
2026-01-23 16:30:54 -05:00
pub fn compute_pool_pda_seed(
definition_token_a_id: AccountId,
definition_token_b_id: AccountId,
) -> PdaSeed {
use risc0_zkvm::sha::{Impl, Sha256};
let (token_1, token_2) = match definition_token_a_id
.value()
.cmp(definition_token_b_id.value())
{
std::cmp::Ordering::Less => (definition_token_b_id, definition_token_a_id),
std::cmp::Ordering::Greater => (definition_token_a_id, definition_token_b_id),
std::cmp::Ordering::Equal => panic!("Definitions match"),
};
let mut bytes = [0; 64];
bytes[0..32].copy_from_slice(&token_1.to_bytes());
bytes[32..].copy_from_slice(&token_2.to_bytes());
PdaSeed::new(
Impl::hash_bytes(&bytes)
.as_bytes()
.try_into()
.expect("Hash output must be exactly 32 bytes long"),
)
}
2026-01-22 20:48:05 -05:00
2026-01-23 16:30:54 -05:00
pub fn compute_vault_pda(
amm_program_id: ProgramId,
pool_id: AccountId,
definition_token_id: AccountId,
) -> AccountId {
AccountId::from((
&amm_program_id,
&compute_vault_pda_seed(pool_id, definition_token_id),
))
2026-01-22 20:48:05 -05:00
}
2026-01-23 16:30:54 -05:00
pub fn compute_vault_pda_seed(pool_id: AccountId, definition_token_id: AccountId) -> PdaSeed {
use risc0_zkvm::sha::{Impl, Sha256};
2026-01-22 20:48:05 -05:00
2026-01-23 16:30:54 -05:00
let mut bytes = [0; 64];
bytes[0..32].copy_from_slice(&pool_id.to_bytes());
bytes[32..].copy_from_slice(&definition_token_id.to_bytes());
2026-01-22 20:48:05 -05:00
2026-01-23 16:30:54 -05:00
PdaSeed::new(
Impl::hash_bytes(&bytes)
.as_bytes()
.try_into()
.expect("Hash output must be exactly 32 bytes long"),
)
2026-01-22 20:48:05 -05:00
}
2026-01-23 16:30:54 -05:00
pub fn compute_liquidity_token_pda(amm_program_id: ProgramId, pool_id: AccountId) -> AccountId {
AccountId::from((&amm_program_id, &compute_liquidity_token_pda_seed(pool_id)))
2026-01-22 20:48:05 -05:00
}
2026-01-23 16:30:54 -05:00
pub fn compute_liquidity_token_pda_seed(pool_id: AccountId) -> PdaSeed {
use risc0_zkvm::sha::{Impl, Sha256};
2026-01-22 20:48:05 -05:00
2026-01-23 16:30:54 -05:00
let mut bytes = [0; 64];
bytes[0..32].copy_from_slice(&pool_id.to_bytes());
bytes[32..].copy_from_slice(&[0; 32]);
2026-01-22 20:48:05 -05:00
2026-01-23 16:30:54 -05:00
PdaSeed::new(
Impl::hash_bytes(&bytes)
.as_bytes()
.try_into()
.expect("Hash output must be exactly 32 bytes long"),
)
2026-01-22 20:48:05 -05:00
}