2026-01-23 16:30:54 -05:00
|
|
|
use amm_core::{compute_liquidity_token_pda, compute_pool_pda, compute_vault_pda};
|
2026-03-13 22:38:23 +03:00
|
|
|
use common::{HashType, transaction::NSSATransaction};
|
2026-05-15 18:15:54 -04:00
|
|
|
use nssa::{AccountId, program::Program, public_transaction::WitnessSet};
|
|
|
|
|
use pyo3::exceptions::PyRuntimeError;
|
2026-03-13 22:38:23 +03:00
|
|
|
use sequencer_service_rpc::RpcClient as _;
|
2026-01-16 04:11:33 +03:00
|
|
|
use token_core::TokenHolding;
|
2025-12-17 14:21:36 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
use crate::{
|
|
|
|
|
ExecutionFailureKind, WalletCore,
|
|
|
|
|
cli::CliAccountMention,
|
|
|
|
|
helperfunctions::read_pin,
|
|
|
|
|
signing::SigningGroups,
|
|
|
|
|
};
|
2026-03-04 18:42:33 +03:00
|
|
|
pub struct Amm<'wallet>(pub &'wallet WalletCore);
|
2025-12-22 04:42:32 +02:00
|
|
|
|
|
|
|
|
impl Amm<'_> {
|
2026-05-15 18:15:54 -04:00
|
|
|
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
2025-12-22 04:42:32 +02:00
|
|
|
pub async fn send_new_definition(
|
2025-12-16 14:05:34 +02:00
|
|
|
&self,
|
2025-12-18 11:44:38 +02:00
|
|
|
user_holding_a: AccountId,
|
|
|
|
|
user_holding_b: AccountId,
|
|
|
|
|
user_holding_lp: AccountId,
|
2025-12-17 11:44:52 +02:00
|
|
|
balance_a: u128,
|
|
|
|
|
balance_b: u128,
|
2026-05-15 18:15:54 -04:00
|
|
|
a_mention: &CliAccountMention,
|
|
|
|
|
b_mention: &CliAccountMention,
|
|
|
|
|
lp_mention: &CliAccountMention,
|
2026-03-13 22:38:23 +03:00
|
|
|
) -> Result<HashType, ExecutionFailureKind> {
|
2026-01-23 16:30:54 -05:00
|
|
|
let program = Program::amm();
|
2025-12-18 11:44:38 +02:00
|
|
|
let amm_program_id = Program::amm().id();
|
2026-01-23 16:30:54 -05:00
|
|
|
let instruction = amm_core::Instruction::NewDefinition {
|
|
|
|
|
token_a_amount: balance_a,
|
|
|
|
|
token_b_amount: balance_b,
|
|
|
|
|
amm_program_id,
|
|
|
|
|
};
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2025-12-22 04:42:32 +02:00
|
|
|
let user_a_acc = self
|
|
|
|
|
.0
|
|
|
|
|
.get_account_public(user_holding_a)
|
|
|
|
|
.await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-22 04:42:32 +02:00
|
|
|
let user_b_acc = self
|
|
|
|
|
.0
|
|
|
|
|
.get_account_public(user_holding_b)
|
|
|
|
|
.await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-01-16 04:11:33 +03:00
|
|
|
let definition_token_a_id = TokenHolding::try_from(&user_a_acc.data)
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_a))?
|
2026-01-16 04:11:33 +03:00
|
|
|
.definition_id();
|
|
|
|
|
let definition_token_b_id = TokenHolding::try_from(&user_b_acc.data)
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_b))?
|
2026-01-16 04:11:33 +03:00
|
|
|
.definition_id();
|
2025-12-18 11:44:38 +02:00
|
|
|
|
|
|
|
|
let amm_pool =
|
|
|
|
|
compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id);
|
|
|
|
|
let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id);
|
|
|
|
|
let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id);
|
|
|
|
|
let pool_lp = compute_liquidity_token_pda(amm_program_id, amm_pool);
|
|
|
|
|
|
|
|
|
|
let account_ids = vec![
|
|
|
|
|
amm_pool,
|
|
|
|
|
vault_holding_a,
|
|
|
|
|
vault_holding_b,
|
|
|
|
|
pool_lp,
|
|
|
|
|
user_holding_a,
|
|
|
|
|
user_holding_b,
|
|
|
|
|
user_holding_lp,
|
|
|
|
|
];
|
2025-12-16 14:05:34 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let mut groups = SigningGroups::new();
|
|
|
|
|
groups
|
|
|
|
|
.add_sender(a_mention, user_holding_a, self.0)
|
|
|
|
|
.and_then(|()| groups.add_sender(b_mention, user_holding_b, self.0))
|
|
|
|
|
.and_then(|()| groups.add_recipient(lp_mention, user_holding_lp, self.0))
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let mut nonces = self.0.get_accounts_nonces(vec![user_holding_a, user_holding_b]).await
|
2026-05-01 02:44:36 -04:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2026-03-27 21:43:28 +03:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
if groups.signing_ids().contains(&user_holding_lp) {
|
|
|
|
|
let lp_nonces = self.0.get_accounts_nonces(vec![user_holding_lp]).await
|
2026-03-27 21:43:28 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2026-05-15 18:15:54 -04:00
|
|
|
nonces.push(lp_nonces.into_iter().next().unwrap_or(nssa_core::account::Nonce(0)));
|
2026-03-27 21:43:28 +03:00
|
|
|
} else {
|
|
|
|
|
println!(
|
|
|
|
|
"Liquidity pool tokens receiver's account ({user_holding_lp}) private key not found in wallet. Proceeding with only liquidity provider's keys."
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
2026-05-01 02:44:36 -04:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let pin = if groups.needs_pin() {
|
|
|
|
|
read_pin()
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
|
|
|
|
.as_str()
|
|
|
|
|
.to_owned()
|
2026-05-01 02:44:36 -04:00
|
|
|
} else {
|
2026-05-15 18:15:54 -04:00
|
|
|
String::new()
|
2026-05-01 02:44:36 -04:00
|
|
|
};
|
2026-05-15 18:15:54 -04:00
|
|
|
let sigs = groups.sign_all(&message.hash(), &pin)
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
2026-05-01 02:44:36 -04:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-03-13 22:38:23 +03:00
|
|
|
Ok(self
|
|
|
|
|
.0
|
|
|
|
|
.sequencer_client
|
|
|
|
|
.send_transaction(NSSATransaction::Public(tx))
|
|
|
|
|
.await?)
|
2025-12-16 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
2026-04-02 09:28:33 +02:00
|
|
|
pub async fn send_swap_exact_input(
|
2025-12-16 14:05:34 +02:00
|
|
|
&self,
|
2025-12-18 11:44:38 +02:00
|
|
|
user_holding_a: AccountId,
|
|
|
|
|
user_holding_b: AccountId,
|
2026-01-23 16:30:54 -05:00
|
|
|
swap_amount_in: u128,
|
2025-12-16 14:05:34 +02:00
|
|
|
min_amount_out: u128,
|
2026-01-23 16:30:54 -05:00
|
|
|
token_definition_id_in: AccountId,
|
2026-05-15 18:15:54 -04:00
|
|
|
a_mention: &CliAccountMention,
|
|
|
|
|
b_mention: &CliAccountMention,
|
2026-03-13 22:38:23 +03:00
|
|
|
) -> Result<HashType, ExecutionFailureKind> {
|
2026-04-02 09:28:33 +02:00
|
|
|
let instruction = amm_core::Instruction::SwapExactInput {
|
2026-01-23 16:30:54 -05:00
|
|
|
swap_amount_in,
|
|
|
|
|
min_amount_out,
|
|
|
|
|
token_definition_id_in,
|
|
|
|
|
};
|
|
|
|
|
let program = Program::amm();
|
2025-12-18 11:44:38 +02:00
|
|
|
let amm_program_id = Program::amm().id();
|
|
|
|
|
|
2025-12-22 04:42:32 +02:00
|
|
|
let user_a_acc = self
|
|
|
|
|
.0
|
|
|
|
|
.get_account_public(user_holding_a)
|
|
|
|
|
.await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-22 04:42:32 +02:00
|
|
|
let user_b_acc = self
|
|
|
|
|
.0
|
|
|
|
|
.get_account_public(user_holding_b)
|
|
|
|
|
.await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-01-16 04:11:33 +03:00
|
|
|
let definition_token_a_id = TokenHolding::try_from(&user_a_acc.data)
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_a))?
|
2026-01-16 04:11:33 +03:00
|
|
|
.definition_id();
|
|
|
|
|
let definition_token_b_id = TokenHolding::try_from(&user_b_acc.data)
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_b))?
|
2026-01-16 04:11:33 +03:00
|
|
|
.definition_id();
|
2025-12-18 11:44:38 +02:00
|
|
|
|
|
|
|
|
let amm_pool =
|
|
|
|
|
compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id);
|
|
|
|
|
let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id);
|
|
|
|
|
let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id);
|
|
|
|
|
|
|
|
|
|
let account_ids = vec![
|
|
|
|
|
amm_pool,
|
|
|
|
|
vault_holding_a,
|
|
|
|
|
vault_holding_b,
|
|
|
|
|
user_holding_a,
|
|
|
|
|
user_holding_b,
|
|
|
|
|
];
|
|
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let (account_id_auth, seller_mention) = if definition_token_a_id == token_definition_id_in {
|
|
|
|
|
(user_holding_a, a_mention)
|
2026-02-25 13:11:54 +01:00
|
|
|
} else if definition_token_b_id == token_definition_id_in {
|
2026-05-15 18:15:54 -04:00
|
|
|
(user_holding_b, b_mention)
|
2026-02-25 13:11:54 +01:00
|
|
|
} else {
|
2026-05-15 18:15:54 -04:00
|
|
|
return Err(ExecutionFailureKind::AccountDataError(token_definition_id_in));
|
2026-02-25 13:11:54 +01:00
|
|
|
};
|
|
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let mut groups = SigningGroups::new();
|
|
|
|
|
groups
|
|
|
|
|
.add_sender(seller_mention, account_id_auth, self.0)
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
|
|
|
|
|
|
|
|
|
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
2026-02-25 13:11:54 +01:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
|
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
|
|
|
|
|
|
|
|
|
let pin = if groups.needs_pin() {
|
|
|
|
|
read_pin()
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
|
|
|
|
.as_str()
|
|
|
|
|
.to_owned()
|
2026-05-01 02:44:36 -04:00
|
|
|
} else {
|
2026-05-15 18:15:54 -04:00
|
|
|
String::new()
|
2026-05-01 02:44:36 -04:00
|
|
|
};
|
2026-05-15 18:15:54 -04:00
|
|
|
let sigs = groups.sign_all(&message.hash(), &pin)
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
2026-02-25 13:11:54 +01:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
2026-02-25 13:11:54 +01:00
|
|
|
|
|
|
|
|
Ok(self
|
|
|
|
|
.0
|
|
|
|
|
.sequencer_client
|
|
|
|
|
.send_transaction(NSSATransaction::Public(tx))
|
|
|
|
|
.await?)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
2026-02-25 13:11:54 +01:00
|
|
|
pub async fn send_swap_exact_output(
|
|
|
|
|
&self,
|
|
|
|
|
user_holding_a: AccountId,
|
|
|
|
|
user_holding_b: AccountId,
|
|
|
|
|
exact_amount_out: u128,
|
|
|
|
|
max_amount_in: u128,
|
|
|
|
|
token_definition_id_in: AccountId,
|
2026-05-15 18:15:54 -04:00
|
|
|
a_mention: &CliAccountMention,
|
|
|
|
|
b_mention: &CliAccountMention,
|
2026-02-25 13:11:54 +01:00
|
|
|
) -> Result<HashType, ExecutionFailureKind> {
|
|
|
|
|
let instruction = amm_core::Instruction::SwapExactOutput {
|
|
|
|
|
exact_amount_out,
|
|
|
|
|
max_amount_in,
|
|
|
|
|
token_definition_id_in,
|
|
|
|
|
};
|
|
|
|
|
let program = Program::amm();
|
|
|
|
|
let amm_program_id = Program::amm().id();
|
|
|
|
|
|
|
|
|
|
let user_a_acc = self
|
2025-12-18 11:44:38 +02:00
|
|
|
.0
|
|
|
|
|
.get_account_public(user_holding_a)
|
|
|
|
|
.await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2026-02-25 13:11:54 +01:00
|
|
|
let user_b_acc = self
|
2025-12-18 11:44:38 +02:00
|
|
|
.0
|
|
|
|
|
.get_account_public(user_holding_b)
|
|
|
|
|
.await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-02-25 13:11:54 +01:00
|
|
|
let definition_token_a_id = TokenHolding::try_from(&user_a_acc.data)
|
|
|
|
|
.map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_a))?
|
|
|
|
|
.definition_id();
|
|
|
|
|
let definition_token_b_id = TokenHolding::try_from(&user_b_acc.data)
|
|
|
|
|
.map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_b))?
|
|
|
|
|
.definition_id();
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-02-25 13:11:54 +01:00
|
|
|
let amm_pool =
|
|
|
|
|
compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id);
|
|
|
|
|
let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id);
|
|
|
|
|
let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id);
|
|
|
|
|
|
|
|
|
|
let account_ids = vec![
|
|
|
|
|
amm_pool,
|
|
|
|
|
vault_holding_a,
|
|
|
|
|
vault_holding_b,
|
|
|
|
|
user_holding_a,
|
|
|
|
|
user_holding_b,
|
|
|
|
|
];
|
|
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let (account_id_auth, seller_mention) = if definition_token_a_id == token_definition_id_in {
|
|
|
|
|
(user_holding_a, a_mention)
|
2026-02-25 13:11:54 +01:00
|
|
|
} else if definition_token_b_id == token_definition_id_in {
|
2026-05-15 18:15:54 -04:00
|
|
|
(user_holding_b, b_mention)
|
2025-12-18 11:44:38 +02:00
|
|
|
} else {
|
2026-05-15 18:15:54 -04:00
|
|
|
return Err(ExecutionFailureKind::AccountDataError(token_definition_id_in));
|
2026-02-25 13:11:54 +01:00
|
|
|
};
|
2025-12-16 14:05:34 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let mut groups = SigningGroups::new();
|
|
|
|
|
groups
|
|
|
|
|
.add_sender(seller_mention, account_id_auth, self.0)
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
|
|
|
|
|
|
|
|
|
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
|
|
|
|
|
|
|
|
|
let pin = if groups.needs_pin() {
|
|
|
|
|
read_pin()
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
|
|
|
|
.as_str()
|
|
|
|
|
.to_owned()
|
2026-05-01 02:44:36 -04:00
|
|
|
} else {
|
2026-05-15 18:15:54 -04:00
|
|
|
String::new()
|
2026-05-01 02:44:36 -04:00
|
|
|
};
|
2026-05-15 18:15:54 -04:00
|
|
|
let sigs = groups.sign_all(&message.hash(), &pin)
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-03-13 22:38:23 +03:00
|
|
|
Ok(self
|
|
|
|
|
.0
|
|
|
|
|
.sequencer_client
|
|
|
|
|
.send_transaction(NSSATransaction::Public(tx))
|
|
|
|
|
.await?)
|
2025-12-16 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
2025-12-22 04:42:32 +02:00
|
|
|
pub async fn send_add_liquidity(
|
2025-12-16 14:05:34 +02:00
|
|
|
&self,
|
2025-12-18 11:44:38 +02:00
|
|
|
user_holding_a: AccountId,
|
|
|
|
|
user_holding_b: AccountId,
|
|
|
|
|
user_holding_lp: AccountId,
|
2026-01-23 16:30:54 -05:00
|
|
|
min_amount_liquidity: u128,
|
|
|
|
|
max_amount_to_add_token_a: u128,
|
|
|
|
|
max_amount_to_add_token_b: u128,
|
2026-05-15 18:15:54 -04:00
|
|
|
a_mention: &CliAccountMention,
|
|
|
|
|
b_mention: &CliAccountMention,
|
2026-03-13 22:38:23 +03:00
|
|
|
) -> Result<HashType, ExecutionFailureKind> {
|
2026-01-23 16:30:54 -05:00
|
|
|
let instruction = amm_core::Instruction::AddLiquidity {
|
|
|
|
|
min_amount_liquidity,
|
|
|
|
|
max_amount_to_add_token_a,
|
|
|
|
|
max_amount_to_add_token_b,
|
|
|
|
|
};
|
|
|
|
|
let program = Program::amm();
|
2025-12-18 11:44:38 +02:00
|
|
|
let amm_program_id = Program::amm().id();
|
|
|
|
|
|
2025-12-22 04:42:32 +02:00
|
|
|
let user_a_acc = self
|
|
|
|
|
.0
|
|
|
|
|
.get_account_public(user_holding_a)
|
|
|
|
|
.await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-22 04:42:32 +02:00
|
|
|
let user_b_acc = self
|
|
|
|
|
.0
|
|
|
|
|
.get_account_public(user_holding_b)
|
|
|
|
|
.await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-01-16 04:11:33 +03:00
|
|
|
let definition_token_a_id = TokenHolding::try_from(&user_a_acc.data)
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_a))?
|
2026-01-16 04:11:33 +03:00
|
|
|
.definition_id();
|
|
|
|
|
let definition_token_b_id = TokenHolding::try_from(&user_b_acc.data)
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_b))?
|
2026-01-16 04:11:33 +03:00
|
|
|
.definition_id();
|
2025-12-18 11:44:38 +02:00
|
|
|
|
|
|
|
|
let amm_pool =
|
|
|
|
|
compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id);
|
|
|
|
|
let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id);
|
|
|
|
|
let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id);
|
|
|
|
|
let pool_lp = compute_liquidity_token_pda(amm_program_id, amm_pool);
|
|
|
|
|
|
|
|
|
|
let account_ids = vec![
|
2025-12-16 14:05:34 +02:00
|
|
|
amm_pool,
|
|
|
|
|
vault_holding_a,
|
|
|
|
|
vault_holding_b,
|
|
|
|
|
pool_lp,
|
|
|
|
|
user_holding_a,
|
|
|
|
|
user_holding_b,
|
|
|
|
|
user_holding_lp,
|
2025-12-18 11:44:38 +02:00
|
|
|
];
|
2025-12-16 14:05:34 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let mut groups = SigningGroups::new();
|
|
|
|
|
groups
|
|
|
|
|
.add_sender(a_mention, user_holding_a, self.0)
|
|
|
|
|
.and_then(|()| groups.add_sender(b_mention, user_holding_b, self.0))
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
|
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2026-05-01 02:44:36 -04:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
2026-05-01 02:44:36 -04:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let pin = if groups.needs_pin() {
|
|
|
|
|
read_pin()
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
|
|
|
|
.as_str()
|
|
|
|
|
.to_owned()
|
2026-05-01 02:44:36 -04:00
|
|
|
} else {
|
2026-05-15 18:15:54 -04:00
|
|
|
String::new()
|
2026-05-01 02:44:36 -04:00
|
|
|
};
|
2026-05-15 18:15:54 -04:00
|
|
|
let sigs = groups.sign_all(&message.hash(), &pin)
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
2026-05-01 02:44:36 -04:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-03-13 22:38:23 +03:00
|
|
|
Ok(self
|
|
|
|
|
.0
|
|
|
|
|
.sequencer_client
|
|
|
|
|
.send_transaction(NSSATransaction::Public(tx))
|
|
|
|
|
.await?)
|
2025-12-16 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
2025-12-22 04:42:32 +02:00
|
|
|
pub async fn send_remove_liquidity(
|
2025-12-16 14:05:34 +02:00
|
|
|
&self,
|
2025-12-18 11:44:38 +02:00
|
|
|
user_holding_a: AccountId,
|
|
|
|
|
user_holding_b: AccountId,
|
|
|
|
|
user_holding_lp: AccountId,
|
2026-01-23 16:30:54 -05:00
|
|
|
remove_liquidity_amount: u128,
|
|
|
|
|
min_amount_to_remove_token_a: u128,
|
|
|
|
|
min_amount_to_remove_token_b: u128,
|
2026-05-15 18:15:54 -04:00
|
|
|
lp_mention: &CliAccountMention,
|
2026-03-13 22:38:23 +03:00
|
|
|
) -> Result<HashType, ExecutionFailureKind> {
|
2026-01-23 16:30:54 -05:00
|
|
|
let instruction = amm_core::Instruction::RemoveLiquidity {
|
|
|
|
|
remove_liquidity_amount,
|
|
|
|
|
min_amount_to_remove_token_a,
|
|
|
|
|
min_amount_to_remove_token_b,
|
|
|
|
|
};
|
|
|
|
|
let program = Program::amm();
|
2025-12-18 11:44:38 +02:00
|
|
|
let amm_program_id = Program::amm().id();
|
|
|
|
|
|
2025-12-22 04:42:32 +02:00
|
|
|
let user_a_acc = self
|
|
|
|
|
.0
|
|
|
|
|
.get_account_public(user_holding_a)
|
|
|
|
|
.await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-22 04:42:32 +02:00
|
|
|
let user_b_acc = self
|
|
|
|
|
.0
|
|
|
|
|
.get_account_public(user_holding_b)
|
|
|
|
|
.await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-01-16 04:11:33 +03:00
|
|
|
let definition_token_a_id = TokenHolding::try_from(&user_a_acc.data)
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_a))?
|
2026-01-16 04:11:33 +03:00
|
|
|
.definition_id();
|
|
|
|
|
let definition_token_b_id = TokenHolding::try_from(&user_b_acc.data)
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_b))?
|
2026-01-16 04:11:33 +03:00
|
|
|
.definition_id();
|
2025-12-18 11:44:38 +02:00
|
|
|
|
|
|
|
|
let amm_pool =
|
|
|
|
|
compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id);
|
|
|
|
|
let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id);
|
|
|
|
|
let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id);
|
|
|
|
|
let pool_lp = compute_liquidity_token_pda(amm_program_id, amm_pool);
|
|
|
|
|
|
|
|
|
|
let account_ids = vec![
|
2025-12-16 14:05:34 +02:00
|
|
|
amm_pool,
|
|
|
|
|
vault_holding_a,
|
|
|
|
|
vault_holding_b,
|
|
|
|
|
pool_lp,
|
|
|
|
|
user_holding_a,
|
|
|
|
|
user_holding_b,
|
|
|
|
|
user_holding_lp,
|
2025-12-18 11:44:38 +02:00
|
|
|
];
|
|
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let mut groups = SigningGroups::new();
|
|
|
|
|
groups
|
|
|
|
|
.add_sender(lp_mention, user_holding_lp, self.0)
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
|
|
|
|
|
|
|
|
|
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
2026-03-04 18:42:33 +03:00
|
|
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
|
|
|
|
|
|
|
|
|
let pin = if groups.needs_pin() {
|
|
|
|
|
read_pin()
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
|
|
|
|
.as_str()
|
|
|
|
|
.to_owned()
|
2026-05-01 02:44:36 -04:00
|
|
|
} else {
|
2026-05-15 18:15:54 -04:00
|
|
|
String::new()
|
2026-05-01 02:44:36 -04:00
|
|
|
};
|
2026-05-15 18:15:54 -04:00
|
|
|
let sigs = groups.sign_all(&message.hash(), &pin)
|
|
|
|
|
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-05-15 18:15:54 -04:00
|
|
|
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
2025-12-18 11:44:38 +02:00
|
|
|
|
2026-03-13 22:38:23 +03:00
|
|
|
Ok(self
|
|
|
|
|
.0
|
|
|
|
|
.sequencer_client
|
|
|
|
|
.send_transaction(NSSATransaction::Public(tx))
|
|
|
|
|
.await?)
|
2025-12-16 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
}
|