feat: swaps, add/rem liq

This commit is contained in:
Pravdyvy 2025-12-16 14:05:34 +02:00
parent c5cca4376e
commit d4a471e948
11 changed files with 1035 additions and 55 deletions

View File

@ -16,7 +16,7 @@ use crate::{Commitment, account::Account};
pub type Scalar = [u8; 32];
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, Copy)]
pub struct SharedSecretKey(pub [u8; 32]);
pub struct EncryptionScheme;

View File

@ -55,6 +55,7 @@ use nssa_core::{
// * compute_pool_pda_seed: token definitions for the pool pair
// * compute_vault_pda_seed: pool definition id, definition token id,
// * compute_liquidity_token_pda_seed: pool definition id
//
const POOL_DEFINITION_DATA_SIZE: usize = 225;

View File

@ -216,7 +216,7 @@ mod tests {
&Program::serialize_instruction(balance_to_move).unwrap(),
&[0, 2],
&[0xdeadbeef],
&[(recipient_keys.npk(), shared_secret.clone())],
&[(recipient_keys.npk(), shared_secret)],
&[],
&Program::authenticated_transfer_program().into(),
)
@ -312,8 +312,8 @@ mod tests {
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&[
(sender_keys.npk(), shared_secret_1.clone()),
(recipient_keys.npk(), shared_secret_2.clone()),
(sender_keys.npk(), shared_secret_1),
(recipient_keys.npk(), shared_secret_2),
],
&[(
sender_keys.nsk,

View File

@ -2094,7 +2094,7 @@ pub mod tests {
&visibility_mask,
&[0xdeadbeef1, 0xdeadbeef2],
&[
(sender_keys.npk(), shared_secret.clone()),
(sender_keys.npk(), shared_secret),
(sender_keys.npk(), shared_secret),
],
&private_account_auth,
@ -2288,12 +2288,12 @@ pub mod tests {
let mut i: usize = 0;
let (token_1, token_2) = loop {
if definition_token_a_id.value()[i] > definition_token_b_id.value()[i] {
let token_1 = definition_token_a_id.clone();
let token_2 = definition_token_b_id.clone();
let token_1 = definition_token_a_id;
let token_2 = definition_token_b_id;
break (token_1, token_2);
} else if definition_token_a_id.value()[i] < definition_token_b_id.value()[i] {
let token_1 = definition_token_b_id.clone();
let token_2 = definition_token_a_id.clone();
let token_1 = definition_token_b_id;
let token_2 = definition_token_a_id;
break (token_1, token_2);
}
@ -2396,6 +2396,7 @@ pub mod tests {
.expect("225 bytes should fit into Data")
}
#[allow(unused)]
fn parse(data: &[u8]) -> Option<Self> {
if data.len() != POOL_DEFINITION_DATA_SIZE {
None
@ -2531,6 +2532,7 @@ pub mod tests {
UserTokenBHoldingNewDef,
}
#[allow(clippy::enum_variant_names)]
enum IdEnum {
PoolDefinitionId,
TokenLPDefinitionId,
@ -2543,6 +2545,7 @@ pub mod tests {
VaultBId,
}
#[allow(clippy::enum_variant_names)]
enum PrivateKeysEnum {
UserTokenAKey,
UserTokenBKey,
@ -2680,9 +2683,7 @@ pub mod tests {
reserve_b: helper_balances_constructor(BalancesEnum::VaultBBalanceInit),
fees: 0u128,
active: true,
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::TokenADefinitionAcc => Account {
@ -2692,9 +2693,7 @@ pub mod tests {
account_type: 0u8,
name: [1u8; 6],
total_supply: helper_balances_constructor(BalancesEnum::TokenASupply),
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::TokenBDefinitionAcc => Account {
@ -2704,9 +2703,7 @@ pub mod tests {
account_type: 0u8,
name: [1u8; 6],
total_supply: helper_balances_constructor(BalancesEnum::TokenBSupply),
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::TokenLPDefinitionAcc => Account {
@ -2716,9 +2713,7 @@ pub mod tests {
account_type: 0u8,
name: [1u8; 6],
total_supply: helper_balances_constructor(BalancesEnum::TokenLPSupply),
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::VaultAInit => Account {
@ -2787,9 +2782,7 @@ pub mod tests {
reserve_b: helper_balances_constructor(BalancesEnum::VaultBBalanceSwap1),
fees: 0u128,
active: true,
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::UserTokenAHoldingSwap1 => Account {
@ -2848,9 +2841,7 @@ pub mod tests {
reserve_b: helper_balances_constructor(BalancesEnum::VaultBBalanceSwap2),
fees: 0u128,
active: true,
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::UserTokenAHoldingSwap2 => Account {
@ -2909,9 +2900,7 @@ pub mod tests {
reserve_b: helper_balances_constructor(BalancesEnum::VaultBBalanceAdd),
fees: 0u128,
active: true,
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::UserTokenAHoldingAdd => Account {
@ -2951,9 +2940,7 @@ pub mod tests {
account_type: 0u8,
name: [1u8; 6],
total_supply: helper_balances_constructor(BalancesEnum::TokenLPSupplyAdd),
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::VaultARemove => Account {
@ -2992,9 +2979,7 @@ pub mod tests {
reserve_b: helper_balances_constructor(BalancesEnum::VaultBBalanceRemove),
fees: 0u128,
active: true,
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::UserTokenAHoldingRemove => Account {
@ -3034,9 +3019,7 @@ pub mod tests {
account_type: 0u8,
name: [1u8; 6],
total_supply: helper_balances_constructor(BalancesEnum::TokenLPSupplyRemove),
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::TokenLPDefinitionInitInactive => Account {
@ -3046,9 +3029,7 @@ pub mod tests {
account_type: 0u8,
name: [1u8; 6],
total_supply: 0,
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::VaultAInitInactive => Account {
@ -3085,9 +3066,7 @@ pub mod tests {
reserve_b: 0,
fees: 0u128,
active: false,
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::UserTokenAHoldingNewInit => Account {
@ -3127,9 +3106,7 @@ pub mod tests {
account_type: 0u8,
name: [1u8; 6],
total_supply: helper_balances_constructor(BalancesEnum::VaultABalanceInit),
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::PoolDefinitionNewInit => Account {
@ -3148,9 +3125,7 @@ pub mod tests {
reserve_b: helper_balances_constructor(BalancesEnum::VaultBBalanceInit),
fees: 0u128,
active: true,
})
.try_into()
.expect("Data too big"),
}),
nonce: 0,
},
AccountsEnum::UserTokenLPHoldingInitZero => Account {

View File

@ -11,8 +11,8 @@ use crate::{
chain::ChainSubcommand,
config::ConfigSubcommand,
programs::{
native_token_transfer::AuthTransferSubcommand, pinata::PinataProgramAgnosticSubcommand,
token::TokenProgramAgnosticSubcommand,
amm::AmmProgramAgnosticSubcommand, native_token_transfer::AuthTransferSubcommand,
pinata::PinataProgramAgnosticSubcommand, token::TokenProgramAgnosticSubcommand,
},
},
helperfunctions::{fetch_config, fetch_persistent_storage, merge_auth_config},
@ -47,6 +47,9 @@ pub enum Command {
/// Token program interaction subcommand
#[command(subcommand)]
Token(TokenProgramAgnosticSubcommand),
/// AMM program interaction subcommand
#[command(subcommand)]
AMM(AmmProgramAgnosticSubcommand),
/// Check the wallet can connect to the node and builtin local programs
/// match the remote versions
CheckHealth {},
@ -165,6 +168,7 @@ pub async fn execute_subcommand_with_auth(
Command::Token(token_subcommand) => {
token_subcommand.handle_subcommand(&mut wallet_core).await?
}
Command::AMM(amm_subcommand) => amm_subcommand.handle_subcommand(&mut wallet_core).await?,
Command::Config(config_subcommand) => {
config_subcommand
.handle_subcommand(&mut wallet_core)

View File

@ -0,0 +1,452 @@
use anyhow::Result;
use clap::Subcommand;
use crate::{
PrivacyPreservingAccount, WalletCore,
cli::{SubcommandReturnValue, WalletSubcommand},
helperfunctions::parse_addr_with_privacy_prefix,
program_facades::amm::AMM,
};
/// Represents generic CLI subcommand for a wallet working with amm program
#[derive(Subcommand, Debug, Clone)]
pub enum AmmProgramAgnosticSubcommand {
/// Produce a new token
///
/// user_holding_a and user_holding_b must be owned.
New {
/// amm_pool - valid 32 byte base58 string with privacy prefix
#[arg(long)]
amm_pool: String,
/// vault_holding_a - valid 32 byte base58 string with privacy prefix
#[arg(long)]
vault_holding_a: String,
/// vault_holding_b - valid 32 byte base58 string with privacy prefix
#[arg(long)]
vault_holding_b: String,
/// pool_lp - valid 32 byte base58 string with privacy prefix
#[arg(long)]
pool_lp: String,
/// user_holding_a - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_a: String,
/// user_holding_b - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_b: String,
/// user_holding_lp - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_lp: String,
#[arg(long)]
balance_a: u128,
#[arg(long)]
balance_b: u128,
},
/// Swap with variable privacy
///
/// The account associated with swapping token must be owned
Swap {
/// amm_pool - valid 32 byte base58 string with privacy prefix
#[arg(long)]
amm_pool: String,
/// vault_holding_1 - valid 32 byte base58 string with privacy prefix
#[arg(long)]
vault_holding_1: String,
/// vault_holding_2 - valid 32 byte base58 string with privacy prefix
#[arg(long)]
vault_holding_2: String,
/// user_holding_a - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_a: String,
/// user_holding_b - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_b: String,
#[arg(long)]
amount_in: u128,
#[arg(long)]
min_amount_out: u128,
/// token_definition - valid 32 byte base58 string WITHOUT privacy prefix
#[arg(long)]
token_definition: String,
},
/// Add liquidity with variable privacy
///
/// user_holding_a and user_holding_b must be owned.
AddLiquidity {
/// amm_pool - valid 32 byte base58 string with privacy prefix
#[arg(long)]
amm_pool: String,
/// vault_holding_a - valid 32 byte base58 string with privacy prefix
#[arg(long)]
vault_holding_a: String,
/// vault_holding_b - valid 32 byte base58 string with privacy prefix
#[arg(long)]
vault_holding_b: String,
/// pool_lp - valid 32 byte base58 string with privacy prefix
#[arg(long)]
pool_lp: String,
/// user_holding_a - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_a: String,
/// user_holding_b - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_b: String,
/// user_holding_lp - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_lp: String,
#[arg(long)]
min_amount_lp: u128,
#[arg(long)]
max_amount_a: u128,
#[arg(long)]
max_amount_b: u128,
},
/// Remove liquidity with variable privacy
///
/// user_holding_lp must be owned.
RemoveLiquidity {
/// amm_pool - valid 32 byte base58 string with privacy prefix
#[arg(long)]
amm_pool: String,
/// vault_holding_a - valid 32 byte base58 string with privacy prefix
#[arg(long)]
vault_holding_a: String,
/// vault_holding_b - valid 32 byte base58 string with privacy prefix
#[arg(long)]
vault_holding_b: String,
/// pool_lp - valid 32 byte base58 string with privacy prefix
#[arg(long)]
pool_lp: String,
/// user_holding_a - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_a: String,
/// user_holding_b - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_b: String,
/// user_holding_lp - valid 32 byte base58 string with privacy prefix
#[arg(long)]
user_holding_lp: String,
#[arg(long)]
balance_lp: u128,
#[arg(long)]
max_amount_a: u128,
#[arg(long)]
max_amount_b: u128,
},
}
impl WalletSubcommand for AmmProgramAgnosticSubcommand {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
AmmProgramAgnosticSubcommand::New {
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
balance_a,
balance_b,
} => {
let amm_pool = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&amm_pool)?,
)?;
let vault_holding_a = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&vault_holding_a)?,
)?;
let vault_holding_b = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&vault_holding_b)?,
)?;
let pool_lp = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&pool_lp)?,
)?;
let user_holding_a = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_a)?,
)?;
let user_holding_b = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_b)?,
)?;
let user_holding_lp = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_lp)?,
)?;
let is_public_tx = [
&amm_pool,
&vault_holding_a,
&vault_holding_b,
&pool_lp,
&user_holding_a,
&user_holding_b,
&user_holding_lp,
]
.into_iter()
.all(|acc| acc.is_public());
if is_public_tx {
AMM(wallet_core)
.send_new_amm_definition(
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
balance_a,
balance_b,
)
.await?;
Ok(SubcommandReturnValue::Empty)
} else {
AMM(wallet_core)
.send_new_amm_definition_privacy_preserving(
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
balance_a,
balance_b,
)
.await?;
// ToDo: change into correct return value
Ok(SubcommandReturnValue::Empty)
}
}
AmmProgramAgnosticSubcommand::Swap {
amm_pool,
vault_holding_1,
vault_holding_2,
user_holding_a,
user_holding_b,
amount_in,
min_amount_out,
token_definition,
} => {
let amm_pool = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&amm_pool)?,
)?;
let vault_holding_1 = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&vault_holding_1)?,
)?;
let vault_holding_2 = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&vault_holding_2)?,
)?;
let user_holding_a = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_a)?,
)?;
let user_holding_b = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_b)?,
)?;
let is_public_tx = [
&amm_pool,
&vault_holding_1,
&vault_holding_2,
&user_holding_a,
&user_holding_b,
]
.into_iter()
.all(|acc| acc.is_public());
if is_public_tx {
AMM(wallet_core)
.send_swap(
amm_pool,
vault_holding_1,
vault_holding_2,
user_holding_a,
user_holding_b,
amount_in,
min_amount_out,
token_definition.parse()?,
)
.await?;
Ok(SubcommandReturnValue::Empty)
} else {
AMM(wallet_core)
.send_swap_privacy_preserving(
amm_pool,
vault_holding_1,
vault_holding_2,
user_holding_a,
user_holding_b,
amount_in,
min_amount_out,
token_definition.parse()?,
)
.await?;
// ToDo: change into correct return value
Ok(SubcommandReturnValue::Empty)
}
}
AmmProgramAgnosticSubcommand::AddLiquidity {
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
min_amount_lp,
max_amount_a,
max_amount_b,
} => {
let amm_pool = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&amm_pool)?,
)?;
let vault_holding_a = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&vault_holding_a)?,
)?;
let vault_holding_b = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&vault_holding_b)?,
)?;
let pool_lp = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&pool_lp)?,
)?;
let user_holding_a = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_a)?,
)?;
let user_holding_b = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_b)?,
)?;
let user_holding_lp = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_lp)?,
)?;
let is_public_tx = [
&amm_pool,
&vault_holding_a,
&vault_holding_b,
&pool_lp,
&user_holding_a,
&user_holding_b,
&user_holding_lp,
]
.into_iter()
.all(|acc| acc.is_public());
if is_public_tx {
AMM(wallet_core)
.send_add_liq(
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
min_amount_lp,
max_amount_a,
max_amount_b,
)
.await?;
Ok(SubcommandReturnValue::Empty)
} else {
AMM(wallet_core)
.send_add_liq_privacy_preserving(
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
min_amount_lp,
max_amount_a,
max_amount_b,
)
.await?;
// ToDo: change into correct return value
Ok(SubcommandReturnValue::Empty)
}
}
AmmProgramAgnosticSubcommand::RemoveLiquidity {
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
balance_lp,
max_amount_a,
max_amount_b,
} => {
let amm_pool = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&amm_pool)?,
)?;
let vault_holding_a = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&vault_holding_a)?,
)?;
let vault_holding_b = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&vault_holding_b)?,
)?;
let pool_lp = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&pool_lp)?,
)?;
let user_holding_a = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_a)?,
)?;
let user_holding_b = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_b)?,
)?;
let user_holding_lp = PrivacyPreservingAccount::parse_with_privacy(
parse_addr_with_privacy_prefix(&user_holding_lp)?,
)?;
let is_public_tx = [
&amm_pool,
&vault_holding_a,
&vault_holding_b,
&pool_lp,
&user_holding_a,
&user_holding_b,
&user_holding_lp,
]
.into_iter()
.all(|acc| acc.is_public());
if is_public_tx {
AMM(wallet_core)
.send_remove_liq(
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
balance_lp,
max_amount_a,
max_amount_b,
)
.await?;
Ok(SubcommandReturnValue::Empty)
} else {
AMM(wallet_core)
.send_remove_liq_privacy_preserving(
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
balance_lp,
max_amount_a,
max_amount_b,
)
.await?;
// ToDo: change into correct return value
Ok(SubcommandReturnValue::Empty)
}
}
}
}
}

View File

@ -1,3 +1,4 @@
pub mod amm;
pub mod native_token_transfer;
pub mod pinata;
pub mod token;

View File

@ -280,7 +280,7 @@ impl WalletCore {
&produce_random_nonces(private_account_keys.len()),
&private_account_keys
.iter()
.map(|keys| (keys.npk.clone(), keys.ssk.clone()))
.map(|keys| (keys.npk.clone(), keys.ssk))
.collect::<Vec<_>>(),
&acc_manager.private_account_auth(),
&program.to_owned().into(),

View File

@ -1,3 +1,4 @@
use anyhow::Result;
use common::error::ExecutionFailureKind;
use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder;
use nssa::{AccountId, PrivateKey};
@ -7,8 +8,9 @@ use nssa_core::{
encryption::{EphemeralPublicKey, IncomingViewingPublicKey},
};
use crate::WalletCore;
use crate::{WalletCore, helperfunctions::AccountPrivacyKind};
#[derive(Clone)]
pub enum PrivacyPreservingAccount {
Public(AccountId),
PrivateOwned(AccountId),
@ -18,6 +20,28 @@ pub enum PrivacyPreservingAccount {
},
}
impl PrivacyPreservingAccount {
pub fn parse_with_privacy(input: (String, AccountPrivacyKind)) -> Result<Self> {
let acc_id: AccountId = input.0.parse()?;
match input.1 {
AccountPrivacyKind::Public => Ok(Self::Public(acc_id)),
AccountPrivacyKind::Private => Ok(Self::PrivateOwned(acc_id)),
}
}
pub fn is_public(&self) -> bool {
matches!(&self, Self::Public(_))
}
pub fn is_private(&self) -> bool {
matches!(
&self,
Self::PrivateOwned(_) | Self::PrivateForeign { npk: _, ipk: _ }
)
}
}
pub struct PrivateAccountKeys {
pub npk: NullifierPublicKey,
pub ssk: SharedSecretKey,

View File

@ -0,0 +1,522 @@
use common::{error::ExecutionFailureKind, rpc_primitives::requests::SendTxResponse};
use nssa::{AccountId, program::Program};
use nssa_core::{SharedSecretKey, program::InstructionData};
use serde::Serialize;
use crate::{PrivacyPreservingAccount, WalletCore};
struct OrphanHack65BytesInput([u8; 65]);
impl Serialize for OrphanHack65BytesInput {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_bytes(&self.0)
}
}
struct OrphanHack49BytesInput([u8; 49]);
impl Serialize for OrphanHack49BytesInput {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_bytes(&self.0)
}
}
pub struct AMM<'w>(pub &'w WalletCore);
impl AMM<'_> {
#[allow(clippy::too_many_arguments)]
pub async fn send_new_amm_definition(
&self,
_amm_pool: PrivacyPreservingAccount,
_vault_holding_a: PrivacyPreservingAccount,
_vault_holding_b: PrivacyPreservingAccount,
_pool_lp: PrivacyPreservingAccount,
_user_holding_a: PrivacyPreservingAccount,
_user_holding_b: PrivacyPreservingAccount,
_user_holding_lp: PrivacyPreservingAccount,
_balance_a: u128,
_balance_b: u128,
) -> Result<SendTxResponse, ExecutionFailureKind> {
todo!()
}
#[allow(clippy::too_many_arguments)]
pub async fn send_new_amm_definition_privacy_preserving(
&self,
_amm_pool: PrivacyPreservingAccount,
_vault_holding_a: PrivacyPreservingAccount,
_vault_holding_b: PrivacyPreservingAccount,
_pool_lp: PrivacyPreservingAccount,
_user_holding_a: PrivacyPreservingAccount,
_user_holding_b: PrivacyPreservingAccount,
_user_holding_lp: PrivacyPreservingAccount,
_balance_a: u128,
_balance_b: u128,
) -> Result<(SendTxResponse, [Option<SharedSecretKey>; 7]), ExecutionFailureKind> {
todo!()
}
#[allow(clippy::too_many_arguments)]
pub async fn send_swap(
&self,
amm_pool: PrivacyPreservingAccount,
vault_holding_1: PrivacyPreservingAccount,
vault_holding_2: PrivacyPreservingAccount,
user_holding_a: PrivacyPreservingAccount,
user_holding_b: PrivacyPreservingAccount,
amount_in: u128,
min_amount_out: u128,
token_definition_id: AccountId,
) -> Result<SendTxResponse, ExecutionFailureKind> {
let (instruction, program) =
amm_program_preparation_swap(amount_in, min_amount_out, token_definition_id);
match (
amm_pool,
vault_holding_1,
vault_holding_2,
user_holding_a,
user_holding_b,
) {
(
PrivacyPreservingAccount::Public(amm_pool),
PrivacyPreservingAccount::Public(vault_holding_1),
PrivacyPreservingAccount::Public(vault_holding_2),
PrivacyPreservingAccount::Public(user_holding_a),
PrivacyPreservingAccount::Public(user_holding_b),
) => {
let account_ids = vec![
amm_pool,
vault_holding_1,
vault_holding_2,
user_holding_a,
user_holding_b,
];
// ToDo: Correct authorization
// ToDo: Also correct instruction serialization
let message = nssa::public_transaction::Message::try_new(
program.id(),
account_ids,
vec![],
instruction,
)
.unwrap();
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]);
let tx = nssa::PublicTransaction::new(message, witness_set);
Ok(self.0.sequencer_client.send_tx_public(tx).await?)
}
_ => unreachable!(),
}
}
#[allow(clippy::too_many_arguments)]
pub async fn send_swap_privacy_preserving(
&self,
amm_pool: PrivacyPreservingAccount,
vault_holding_1: PrivacyPreservingAccount,
vault_holding_2: PrivacyPreservingAccount,
user_holding_a: PrivacyPreservingAccount,
user_holding_b: PrivacyPreservingAccount,
amount_in: u128,
min_amount_out: u128,
token_definition_id: AccountId,
) -> Result<(SendTxResponse, [Option<SharedSecretKey>; 5]), ExecutionFailureKind> {
let (instruction_data, program) =
amm_program_preparation_swap(amount_in, min_amount_out, token_definition_id);
self.0
.send_privacy_preserving_tx(
vec![
amm_pool.clone(),
vault_holding_1.clone(),
vault_holding_2.clone(),
user_holding_a.clone(),
user_holding_b.clone(),
],
&instruction_data,
&program,
)
.await
.map(|(resp, secrets)| {
let mut secrets = secrets.into_iter();
let mut secrets_res = [None; 5];
for acc_id in [
amm_pool,
vault_holding_1,
vault_holding_2,
user_holding_a,
user_holding_b,
]
.iter()
.enumerate()
{
if acc_id.1.is_private() {
let secret = secrets.next().expect("expected next secret");
secrets_res[acc_id.0] = Some(secret);
}
}
(resp, secrets_res)
})
}
#[allow(clippy::too_many_arguments)]
pub async fn send_add_liq(
&self,
amm_pool: PrivacyPreservingAccount,
vault_holding_a: PrivacyPreservingAccount,
vault_holding_b: PrivacyPreservingAccount,
pool_lp: PrivacyPreservingAccount,
user_holding_a: PrivacyPreservingAccount,
user_holding_b: PrivacyPreservingAccount,
user_holding_lp: PrivacyPreservingAccount,
min_amount_lp: u128,
max_amount_a: u128,
max_amount_b: u128,
) -> Result<SendTxResponse, ExecutionFailureKind> {
let (instruction, program) =
amm_program_preparation_add_liq(min_amount_lp, max_amount_a, max_amount_b);
match (
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
) {
(
PrivacyPreservingAccount::Public(amm_pool),
PrivacyPreservingAccount::Public(vault_holding_a),
PrivacyPreservingAccount::Public(vault_holding_b),
PrivacyPreservingAccount::Public(pool_lp),
PrivacyPreservingAccount::Public(user_holding_a),
PrivacyPreservingAccount::Public(user_holding_b),
PrivacyPreservingAccount::Public(user_holding_lp),
) => {
let account_ids = vec![
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
];
// ToDo: Correct authorization
// ToDo: Also correct instruction serialization
let message = nssa::public_transaction::Message::try_new(
program.id(),
account_ids,
vec![],
instruction,
)
.unwrap();
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]);
let tx = nssa::PublicTransaction::new(message, witness_set);
Ok(self.0.sequencer_client.send_tx_public(tx).await?)
}
_ => unreachable!(),
}
}
#[allow(clippy::too_many_arguments)]
pub async fn send_add_liq_privacy_preserving(
&self,
amm_pool: PrivacyPreservingAccount,
vault_holding_a: PrivacyPreservingAccount,
vault_holding_b: PrivacyPreservingAccount,
pool_lp: PrivacyPreservingAccount,
user_holding_a: PrivacyPreservingAccount,
user_holding_b: PrivacyPreservingAccount,
user_holding_lp: PrivacyPreservingAccount,
min_amount_lp: u128,
max_amount_a: u128,
max_amount_b: u128,
) -> Result<(SendTxResponse, [Option<SharedSecretKey>; 7]), ExecutionFailureKind> {
let (instruction_data, program) =
amm_program_preparation_add_liq(min_amount_lp, max_amount_a, max_amount_b);
self.0
.send_privacy_preserving_tx(
vec![
amm_pool.clone(),
vault_holding_a.clone(),
vault_holding_b.clone(),
pool_lp.clone(),
user_holding_a.clone(),
user_holding_b.clone(),
user_holding_lp.clone(),
],
&instruction_data,
&program,
)
.await
.map(|(resp, secrets)| {
let mut secrets = secrets.into_iter();
let mut secrets_res = [None; 7];
for acc_id in [
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
]
.iter()
.enumerate()
{
if acc_id.1.is_private() {
let secret = secrets.next().expect("expected next secret");
secrets_res[acc_id.0] = Some(secret);
}
}
(resp, secrets_res)
})
}
#[allow(clippy::too_many_arguments)]
pub async fn send_remove_liq(
&self,
amm_pool: PrivacyPreservingAccount,
vault_holding_a: PrivacyPreservingAccount,
vault_holding_b: PrivacyPreservingAccount,
pool_lp: PrivacyPreservingAccount,
user_holding_a: PrivacyPreservingAccount,
user_holding_b: PrivacyPreservingAccount,
user_holding_lp: PrivacyPreservingAccount,
balance_lp: u128,
max_amount_a: u128,
max_amount_b: u128,
) -> Result<SendTxResponse, ExecutionFailureKind> {
let (instruction, program) =
amm_program_preparation_remove_liq(balance_lp, max_amount_a, max_amount_b);
match (
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
) {
(
PrivacyPreservingAccount::Public(amm_pool),
PrivacyPreservingAccount::Public(vault_holding_a),
PrivacyPreservingAccount::Public(vault_holding_b),
PrivacyPreservingAccount::Public(pool_lp),
PrivacyPreservingAccount::Public(user_holding_a),
PrivacyPreservingAccount::Public(user_holding_b),
PrivacyPreservingAccount::Public(user_holding_lp),
) => {
let account_ids = vec![
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
];
// ToDo: Correct authorization
// ToDo: Also correct instruction serialization
let message = nssa::public_transaction::Message::try_new(
program.id(),
account_ids,
vec![],
instruction,
)
.unwrap();
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]);
let tx = nssa::PublicTransaction::new(message, witness_set);
Ok(self.0.sequencer_client.send_tx_public(tx).await?)
}
_ => unreachable!(),
}
}
#[allow(clippy::too_many_arguments)]
pub async fn send_remove_liq_privacy_preserving(
&self,
amm_pool: PrivacyPreservingAccount,
vault_holding_a: PrivacyPreservingAccount,
vault_holding_b: PrivacyPreservingAccount,
pool_lp: PrivacyPreservingAccount,
user_holding_a: PrivacyPreservingAccount,
user_holding_b: PrivacyPreservingAccount,
user_holding_lp: PrivacyPreservingAccount,
balance_lp: u128,
max_amount_a: u128,
max_amount_b: u128,
) -> Result<(SendTxResponse, [Option<SharedSecretKey>; 7]), ExecutionFailureKind> {
let (instruction_data, program) =
amm_program_preparation_remove_liq(balance_lp, max_amount_a, max_amount_b);
self.0
.send_privacy_preserving_tx(
vec![
amm_pool.clone(),
vault_holding_a.clone(),
vault_holding_b.clone(),
pool_lp.clone(),
user_holding_a.clone(),
user_holding_b.clone(),
user_holding_lp.clone(),
],
&instruction_data,
&program,
)
.await
.map(|(resp, secrets)| {
let mut secrets = secrets.into_iter();
let mut secrets_res = [None; 7];
for acc_id in [
amm_pool,
vault_holding_a,
vault_holding_b,
pool_lp,
user_holding_a,
user_holding_b,
user_holding_lp,
]
.iter()
.enumerate()
{
if acc_id.1.is_private() {
let secret = secrets.next().expect("expected next secret");
secrets_res[acc_id.0] = Some(secret);
}
}
(resp, secrets_res)
})
}
}
#[allow(unused)]
fn amm_program_preparation_definition(
balance_a: u128,
balance_b: u128,
) -> (InstructionData, Program) {
// An instruction data of 65-bytes, indicating the initial amm reserves' balances and
// token_program_id with the following layout:
// [0x00 || array of balances (little-endian 16 bytes) || AMM_PROGRAM_ID)]
let amm_program_id = Program::token().id();
let mut instruction = [0; 65];
instruction[1..17].copy_from_slice(&balance_a.to_le_bytes());
instruction[17..33].copy_from_slice(&balance_b.to_le_bytes());
// This can be done less verbose, but it is better to use same way, as in amm program
instruction[33..37].copy_from_slice(&amm_program_id[0].to_le_bytes());
instruction[37..41].copy_from_slice(&amm_program_id[1].to_le_bytes());
instruction[41..45].copy_from_slice(&amm_program_id[2].to_le_bytes());
instruction[45..49].copy_from_slice(&amm_program_id[3].to_le_bytes());
instruction[49..53].copy_from_slice(&amm_program_id[4].to_le_bytes());
instruction[53..57].copy_from_slice(&amm_program_id[5].to_le_bytes());
instruction[57..61].copy_from_slice(&amm_program_id[6].to_le_bytes());
instruction[61..].copy_from_slice(&amm_program_id[7].to_le_bytes());
let instruction_data =
Program::serialize_instruction(OrphanHack65BytesInput(instruction)).unwrap();
let program = Program::token();
(instruction_data, program)
}
fn amm_program_preparation_swap(
amount_in: u128,
min_amount_out: u128,
token_definition_id: AccountId,
) -> (InstructionData, Program) {
// An instruction data byte string of length 65, indicating which token type to swap, quantity
// of tokens put into the swap (of type TOKEN_DEFINITION_ID) and min_amount_out.
// [0x01 || amount (little-endian 16 bytes) || TOKEN_DEFINITION_ID].
let mut instruction = [0; 65];
instruction[1..17].copy_from_slice(&amount_in.to_le_bytes());
instruction[17..33].copy_from_slice(&min_amount_out.to_le_bytes());
// This can be done less verbose, but it is better to use same way, as in amm program
instruction[33..].copy_from_slice(&token_definition_id.to_bytes());
let instruction_data =
Program::serialize_instruction(OrphanHack65BytesInput(instruction)).unwrap();
let program = Program::token();
(instruction_data, program)
}
fn amm_program_preparation_add_liq(
min_amount_lp: u128,
max_amount_a: u128,
max_amount_b: u128,
) -> (InstructionData, Program) {
// An instruction data byte string of length 49, amounts for minimum amount of liquidity from
// add (min_amount_lp), max amount added for each token (max_amount_a and max_amount_b);
// indicate [0x02 || array of of balances (little-endian 16 bytes)].
let mut instruction = [0; 49];
instruction[0] = 0x02;
instruction[1..17].copy_from_slice(&min_amount_lp.to_le_bytes());
instruction[17..33].copy_from_slice(&max_amount_a.to_le_bytes());
instruction[33..49].copy_from_slice(&max_amount_b.to_le_bytes());
let instruction_data =
Program::serialize_instruction(OrphanHack49BytesInput(instruction)).unwrap();
let program = Program::token();
(instruction_data, program)
}
fn amm_program_preparation_remove_liq(
balance_lp: u128,
max_amount_a: u128,
max_amount_b: u128,
) -> (InstructionData, Program) {
// An instruction data byte string of length 49, amounts for minimum amount of liquidity to
// redeem (balance_lp), minimum balance of each token to remove (min_amount_a and
// min_amount_b); indicate [0x03 || array of balances (little-endian 16 bytes)].
let mut instruction = [0; 49];
instruction[0] = 0x03;
instruction[1..17].copy_from_slice(&balance_lp.to_le_bytes());
instruction[17..33].copy_from_slice(&max_amount_a.to_le_bytes());
instruction[33..49].copy_from_slice(&max_amount_b.to_le_bytes());
let instruction_data =
Program::serialize_instruction(OrphanHack49BytesInput(instruction)).unwrap();
let program = Program::token();
(instruction_data, program)
}

View File

@ -1,6 +1,7 @@
//! This module contains [`WalletCore`](crate::WalletCore) facades for interacting with various
//! on-chain programs.
pub mod amm;
pub mod native_token_transfer;
pub mod pinata;
pub mod token;