mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +00:00
feat: mint and burn
This commit is contained in:
parent
7e8b8c7624
commit
f77481f3b5
@ -1548,6 +1548,246 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
||||
info!("Success!");
|
||||
}
|
||||
|
||||
/// This test creates a new token using the token program. After creating the token, the test
|
||||
/// executes a token transfer to a new account then mint, then burn.
|
||||
#[nssa_integration_test]
|
||||
pub async fn test_success_token_program_burn_mint() {
|
||||
info!("########## test_success_token_program_burn_mint ##########");
|
||||
let wallet_config = fetch_config().await.unwrap();
|
||||
|
||||
// Create new account for the token definition
|
||||
let SubcommandReturnValue::RegisterAccount {
|
||||
account_id: definition_account_id,
|
||||
} = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||
NewSubcommand::Public { cci: None },
|
||||
)))
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("invalid subcommand return value");
|
||||
};
|
||||
// Create new account for the token supply holder
|
||||
let SubcommandReturnValue::RegisterAccount {
|
||||
account_id: supply_account_id,
|
||||
} = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||
NewSubcommand::Public { cci: None },
|
||||
)))
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("invalid subcommand return value");
|
||||
};
|
||||
// Create new account for receiving a token transaction
|
||||
let SubcommandReturnValue::RegisterAccount {
|
||||
account_id: recipient_account_id,
|
||||
} = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||
NewSubcommand::Public { cci: None },
|
||||
)))
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("invalid subcommand return value");
|
||||
};
|
||||
|
||||
// Create new token
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: make_public_account_input_from_str(
|
||||
&definition_account_id.to_string(),
|
||||
),
|
||||
supply_account_id: make_public_account_input_from_str(&supply_account_id.to_string()),
|
||||
name: "A NAME".to_string(),
|
||||
total_supply: 37,
|
||||
};
|
||||
wallet::cli::execute_subcommand(Command::Token(subcommand))
|
||||
.await
|
||||
.unwrap();
|
||||
info!("Waiting for next block creation");
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
|
||||
|
||||
// Check the status of the token definition account is the expected after the execution
|
||||
let definition_acc = seq_client
|
||||
.get_account(definition_account_id.to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
|
||||
assert_eq!(definition_acc.program_owner, Program::token().id());
|
||||
// The data of a token definition account has the following layout:
|
||||
// [ 0x00 || name (6 bytes) || total supply (little endian 16 bytes) ]
|
||||
assert_eq!(
|
||||
definition_acc.data.as_ref(),
|
||||
&[
|
||||
0, 65, 32, 78, 65, 77, 69, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
);
|
||||
|
||||
// Check the status of the token holding account with the total supply is the expected after
|
||||
// the execution
|
||||
let supply_acc = seq_client
|
||||
.get_account(supply_account_id.to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
|
||||
// The account must be owned by the token program
|
||||
assert_eq!(supply_acc.program_owner, Program::token().id());
|
||||
// The data of a token definition account has the following layout:
|
||||
// [ 0x01 || corresponding_token_definition_id (32 bytes) || balance (little endian 16
|
||||
// bytes) ] First byte of the data equal to 1 means it's a token holding account
|
||||
assert_eq!(supply_acc.data.as_ref()[0], 1);
|
||||
// Bytes from 1 to 33 represent the id of the token this account is associated with.
|
||||
// In this example, this is a token account of the newly created token, so it is expected
|
||||
// to be equal to the account_id of the token definition account.
|
||||
assert_eq!(
|
||||
&supply_acc.data.as_ref()[1..33],
|
||||
definition_account_id.to_bytes()
|
||||
);
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(supply_acc.data[33..].try_into().unwrap()),
|
||||
37
|
||||
);
|
||||
|
||||
// Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id`
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||
from: make_public_account_input_from_str(&supply_account_id.to_string()),
|
||||
to: Some(make_public_account_input_from_str(
|
||||
&recipient_account_id.to_string(),
|
||||
)),
|
||||
to_npk: None,
|
||||
to_ipk: None,
|
||||
amount: 7,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(Command::Token(subcommand))
|
||||
.await
|
||||
.unwrap();
|
||||
info!("Waiting for next block creation");
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
// Check the status of the account at `supply_account_id` is the expected after the
|
||||
// execution
|
||||
let supply_acc = seq_client
|
||||
.get_account(supply_account_id.to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
// The account must be owned by the token program
|
||||
assert_eq!(supply_acc.program_owner, Program::token().id());
|
||||
// First byte equal to 1 means it's a token holding account
|
||||
assert_eq!(supply_acc.data[0], 1);
|
||||
// Bytes from 1 to 33 represent the id of the token this account is associated with.
|
||||
assert_eq!(&supply_acc.data[1..33], definition_account_id.to_bytes());
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(supply_acc.data[33..].try_into().unwrap()),
|
||||
30
|
||||
);
|
||||
|
||||
// Check the status of the account at `recipient_account_id` is the expected after the
|
||||
// execution
|
||||
let recipient_acc = seq_client
|
||||
.get_account(recipient_account_id.to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
|
||||
// The account must be owned by the token program
|
||||
assert_eq!(recipient_acc.program_owner, Program::token().id());
|
||||
// First byte equal to 1 means it's a token holding account
|
||||
assert_eq!(recipient_acc.data[0], 1);
|
||||
// Bytes from 1 to 33 represent the id of the token this account is associated with.
|
||||
assert_eq!(&recipient_acc.data[1..33], definition_account_id.to_bytes());
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()),
|
||||
7
|
||||
);
|
||||
|
||||
// Burn 3 tokens from `recipient_acc`
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Burn {
|
||||
definition: Some(make_public_account_input_from_str(&definition_account_id.to_string())),
|
||||
definition_npk: None,
|
||||
definition_ipk: None,
|
||||
holder: make_public_account_input_from_str(&recipient_account_id.to_string()),
|
||||
amount: 3,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(Command::Token(subcommand))
|
||||
.await
|
||||
.unwrap();
|
||||
info!("Waiting for next block creation");
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
// Check the status of the token definition account is the expected after the execution
|
||||
let definition_acc = seq_client
|
||||
.get_account(definition_account_id.to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
|
||||
assert_eq!(
|
||||
definition_acc.data.as_ref(),
|
||||
&[
|
||||
0, 65, 32, 78, 65, 77, 69, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
);
|
||||
|
||||
// Check the status of the account at `recipient_account_id` is the expected after the
|
||||
// execution
|
||||
let recipient_acc = seq_client
|
||||
.get_account(recipient_account_id.to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()),
|
||||
4
|
||||
);
|
||||
|
||||
// Mint 10 tokens at `recipient_acc`
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
||||
definition: make_public_account_input_from_str(&definition_account_id.to_string()),
|
||||
holder: Some(make_public_account_input_from_str(&recipient_account_id.to_string())),
|
||||
holder_npk: None,
|
||||
holder_ipk: None,
|
||||
amount: 10,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(Command::Token(subcommand))
|
||||
.await
|
||||
.unwrap();
|
||||
info!("Waiting for next block creation");
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
// Check the status of the token definition account is the expected after the execution
|
||||
let definition_acc = seq_client
|
||||
.get_account(definition_account_id.to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
|
||||
assert_eq!(
|
||||
definition_acc.data.as_ref(),
|
||||
&[
|
||||
0, 65, 32, 78, 65, 77, 69, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
);
|
||||
|
||||
// Check the status of the account at `recipient_account_id` is the expected after the
|
||||
// execution
|
||||
let recipient_acc = seq_client
|
||||
.get_account(recipient_account_id.to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()),
|
||||
14
|
||||
);
|
||||
}
|
||||
|
||||
#[nssa_integration_test]
|
||||
pub async fn test_pinata() {
|
||||
info!("########## test_pinata ##########");
|
||||
|
||||
@ -26,13 +26,13 @@ use nssa_core::{
|
||||
// Arguments to this function are:
|
||||
// * Two accounts: [definition_account, holding_account].
|
||||
// * Authorization required: holding_account
|
||||
// * An instruction data byte string of length 23, indicating the balance to burn with the folloiwng layout
|
||||
// * An instruction data byte string of length 23, indicating the balance to burn with the following layout
|
||||
// [0x03 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
||||
// 5. Mint additional supply of tokens tokens to a Token Holding account (thus increasing total supply)
|
||||
// Arguments to this function are:
|
||||
// * Two accounts: [definition_account, holding_account].
|
||||
// * Authorization required: definition_account
|
||||
// * An instruction data byte string of length 23, indicating the balance to mint with the folloiwng layout
|
||||
// * An instruction data byte string of length 23, indicating the balance to mint with the following layout
|
||||
// [0x04 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
||||
|
||||
const TOKEN_DEFINITION_TYPE: u8 = 0;
|
||||
|
||||
@ -4,6 +4,7 @@ use common::transaction::NSSATransaction;
|
||||
use nssa::AccountId;
|
||||
|
||||
use crate::{
|
||||
AccDecodeData::Decode,
|
||||
WalletCore,
|
||||
cli::{SubcommandReturnValue, WalletSubcommand},
|
||||
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix},
|
||||
@ -87,7 +88,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret, account_id)];
|
||||
let acc_decode_data = vec![Decode(secret, account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -328,7 +329,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_from, from), (secret_to, to)];
|
||||
let acc_decode_data = vec![Decode(secret_from, from), Decode(secret_to, to)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -372,7 +373,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_from, from)];
|
||||
let acc_decode_data = vec![Decode(secret_from, from)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -412,7 +413,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret, to)];
|
||||
let acc_decode_data = vec![Decode(secret, to)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -491,7 +492,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret, from)];
|
||||
let acc_decode_data = vec![Decode(secret, from)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
|
||||
@ -3,6 +3,7 @@ use clap::Subcommand;
|
||||
use common::{PINATA_BASE58, transaction::NSSATransaction};
|
||||
|
||||
use crate::{
|
||||
AccDecodeData::Decode,
|
||||
WalletCore,
|
||||
cli::{SubcommandReturnValue, WalletSubcommand},
|
||||
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix},
|
||||
@ -159,7 +160,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate {
|
||||
println!("Transaction data is {transfer_tx:?}");
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_winner, winner_account_id)];
|
||||
let acc_decode_data = vec![Decode(secret_winner, winner_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
|
||||
@ -4,6 +4,7 @@ use common::transaction::NSSATransaction;
|
||||
use nssa::AccountId;
|
||||
|
||||
use crate::{
|
||||
AccDecodeData::{Decode, Skip},
|
||||
WalletCore,
|
||||
cli::{SubcommandReturnValue, WalletSubcommand},
|
||||
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix},
|
||||
@ -49,6 +50,56 @@ pub enum TokenProgramAgnosticSubcommand {
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
/// Burn tokens on `holder`, modify `definition`.
|
||||
///
|
||||
/// `holder` is owned
|
||||
///
|
||||
/// If `definition` is private, then `definition` and (`definition_npk` , `definition_ipk`) is a
|
||||
/// mutually exclusive patterns.
|
||||
///
|
||||
/// First is used for owned accounts, second otherwise.
|
||||
Burn {
|
||||
/// definition - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
definition: Option<String>,
|
||||
/// definition_npk - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
definition_npk: Option<String>,
|
||||
/// definition_ipk - valid 33 byte hex string
|
||||
#[arg(long)]
|
||||
definition_ipk: Option<String>,
|
||||
/// holder - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
holder: String,
|
||||
/// amount - amount of balance to burn
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
/// Mint tokens on `holder`, modify `definition`.
|
||||
///
|
||||
/// `definition` is owned
|
||||
///
|
||||
/// If `holder` is private, then `holder` and (`holder_npk` , `holder_ipk`) is a mutually
|
||||
/// exclusive patterns.
|
||||
///
|
||||
/// First is used for owned accounts, second otherwise.
|
||||
Mint {
|
||||
/// definition - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
definition: String,
|
||||
/// holder - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
holder: Option<String>,
|
||||
/// holder_npk - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
holder_npk: Option<String>,
|
||||
/// to_ipk - valid 33 byte hex string
|
||||
#[arg(long)]
|
||||
holder_ipk: Option<String>,
|
||||
/// amount - amount of balance to mint
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
}
|
||||
|
||||
impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
@ -201,6 +252,189 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
}
|
||||
};
|
||||
|
||||
underlying_subcommand.handle_subcommand(wallet_core).await
|
||||
}
|
||||
TokenProgramAgnosticSubcommand::Burn {
|
||||
definition,
|
||||
definition_npk,
|
||||
definition_ipk,
|
||||
holder,
|
||||
amount,
|
||||
} => {
|
||||
let underlying_subcommand = match (definition, definition_npk, definition_ipk) {
|
||||
(None, None, None) => {
|
||||
anyhow::bail!(
|
||||
"Provide either account account_id of definition or their public keys"
|
||||
);
|
||||
}
|
||||
(Some(_), Some(_), Some(_)) => {
|
||||
anyhow::bail!(
|
||||
"Provide only one variant: either account_id of definition or their public keys"
|
||||
);
|
||||
}
|
||||
(_, Some(_), None) | (_, None, Some(_)) => {
|
||||
anyhow::bail!("List of public keys is uncomplete");
|
||||
}
|
||||
(Some(definition), None, None) => {
|
||||
let (definition, definition_privacy) =
|
||||
parse_addr_with_privacy_prefix(&definition)?;
|
||||
let (holder, holder_privacy) = parse_addr_with_privacy_prefix(&holder)?;
|
||||
|
||||
match (definition_privacy, holder_privacy) {
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Public) => {
|
||||
TokenProgramSubcommand::Public(
|
||||
TokenProgramSubcommandPublic::BurnToken {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Private) => {
|
||||
TokenProgramSubcommand::Private(
|
||||
TokenProgramSubcommandPrivate::BurnTokenPrivateOwned {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Public) => {
|
||||
TokenProgramSubcommand::Deshielded(
|
||||
TokenProgramSubcommandDeshielded::BurnTokenDeshieldedOwned {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Private) => {
|
||||
TokenProgramSubcommand::Shielded(
|
||||
TokenProgramSubcommandShielded::BurnTokenShielded {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
(None, Some(definition_npk), Some(definition_ipk)) => {
|
||||
let (holder, holder_privacy) = parse_addr_with_privacy_prefix(&holder)?;
|
||||
|
||||
match holder_privacy {
|
||||
AccountPrivacyKind::Private => TokenProgramSubcommand::Private(
|
||||
TokenProgramSubcommandPrivate::BurnTokenPrivateForeign {
|
||||
definition_npk,
|
||||
definition_ipk,
|
||||
holder_account_id: holder,
|
||||
amount,
|
||||
},
|
||||
),
|
||||
AccountPrivacyKind::Public => TokenProgramSubcommand::Deshielded(
|
||||
TokenProgramSubcommandDeshielded::BurnTokenDeshieldedForeign {
|
||||
definition_npk,
|
||||
definition_ipk,
|
||||
holder_account_id: holder,
|
||||
amount,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
underlying_subcommand.handle_subcommand(wallet_core).await
|
||||
}
|
||||
TokenProgramAgnosticSubcommand::Mint {
|
||||
definition,
|
||||
holder,
|
||||
holder_npk,
|
||||
holder_ipk,
|
||||
amount,
|
||||
} => {
|
||||
let underlying_subcommand = match (holder, holder_npk, holder_ipk) {
|
||||
(None, None, None) => {
|
||||
anyhow::bail!(
|
||||
"Provide either account account_id of holder or their public keys"
|
||||
);
|
||||
}
|
||||
(Some(_), Some(_), Some(_)) => {
|
||||
anyhow::bail!(
|
||||
"Provide only one variant: either account_id of holder or their public keys"
|
||||
);
|
||||
}
|
||||
(_, Some(_), None) | (_, None, Some(_)) => {
|
||||
anyhow::bail!("List of public keys is uncomplete");
|
||||
}
|
||||
(Some(holder), None, None) => {
|
||||
let (definition, definition_privacy) =
|
||||
parse_addr_with_privacy_prefix(&definition)?;
|
||||
let (holder, holder_privacy) = parse_addr_with_privacy_prefix(&holder)?;
|
||||
|
||||
match (definition_privacy, holder_privacy) {
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Public) => {
|
||||
TokenProgramSubcommand::Public(
|
||||
TokenProgramSubcommandPublic::MintToken {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Private) => {
|
||||
TokenProgramSubcommand::Private(
|
||||
TokenProgramSubcommandPrivate::MintTokenPrivateOwned {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Public) => {
|
||||
TokenProgramSubcommand::Deshielded(
|
||||
TokenProgramSubcommandDeshielded::MintTokenDeshielded {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Private) => {
|
||||
TokenProgramSubcommand::Shielded(
|
||||
TokenProgramSubcommandShielded::MintTokenShieldedOwned {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
(None, Some(holder_npk), Some(holder_ipk)) => {
|
||||
let (definition, definition_privacy) =
|
||||
parse_addr_with_privacy_prefix(&definition)?;
|
||||
|
||||
match definition_privacy {
|
||||
AccountPrivacyKind::Private => TokenProgramSubcommand::Private(
|
||||
TokenProgramSubcommandPrivate::MintTokenPrivateForeign {
|
||||
definition_account_id: definition,
|
||||
holder_npk,
|
||||
holder_ipk,
|
||||
amount,
|
||||
},
|
||||
),
|
||||
AccountPrivacyKind::Public => TokenProgramSubcommand::Shielded(
|
||||
TokenProgramSubcommandShielded::MintTokenShieldedForeign {
|
||||
definition_account_id: definition,
|
||||
holder_npk,
|
||||
holder_ipk,
|
||||
amount,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
underlying_subcommand.handle_subcommand(wallet_core).await
|
||||
}
|
||||
}
|
||||
@ -239,6 +473,24 @@ pub enum TokenProgramSubcommandPublic {
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
},
|
||||
// Burn tokens using the token program
|
||||
BurnToken {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
// Transfer tokens using the token program
|
||||
MintToken {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
}
|
||||
|
||||
/// Represents generic private CLI subcommand for a wallet working with token_program
|
||||
@ -266,6 +518,46 @@ pub enum TokenProgramSubcommandPrivate {
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
},
|
||||
// Burn tokens using the token program
|
||||
BurnTokenPrivateOwned {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
// Transfer tokens using the token program
|
||||
MintTokenPrivateOwned {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
// Burn tokens using the token program
|
||||
BurnTokenPrivateForeign {
|
||||
#[arg(short, long)]
|
||||
definition_npk: String,
|
||||
#[arg(short, long)]
|
||||
definition_ipk: String,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
// Transfer tokens using the token program
|
||||
MintTokenPrivateForeign {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
holder_npk: String,
|
||||
#[arg(short, long)]
|
||||
holder_ipk: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
}
|
||||
|
||||
/// Represents deshielded public CLI subcommand for a wallet working with token_program
|
||||
@ -280,6 +572,35 @@ pub enum TokenProgramSubcommandDeshielded {
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
},
|
||||
// Burn tokens using the token program
|
||||
BurnTokenDeshieldedOwned {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
// Transfer tokens using the token program
|
||||
MintTokenDeshielded {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
// Burn tokens using the token program
|
||||
BurnTokenDeshieldedForeign {
|
||||
#[arg(short, long)]
|
||||
definition_npk: String,
|
||||
#[arg(short, long)]
|
||||
definition_ipk: String,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
}
|
||||
|
||||
/// Represents generic shielded CLI subcommand for a wallet working with token_program
|
||||
@ -307,6 +628,35 @@ pub enum TokenProgramSubcommandShielded {
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
},
|
||||
// Burn tokens using the token program
|
||||
BurnTokenShielded {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
// Transfer tokens using the token program
|
||||
MintTokenShieldedOwned {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
// Transfer tokens using the token program
|
||||
MintTokenShieldedForeign {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
holder_npk: String,
|
||||
#[arg(short, long)]
|
||||
holder_ipk: String,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
}
|
||||
|
||||
/// Represents generic initialization subcommand for a wallet working with token_program
|
||||
@ -386,6 +736,34 @@ impl WalletSubcommand for TokenProgramSubcommandPublic {
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
TokenProgramSubcommandPublic::BurnToken {
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
Token(wallet_core)
|
||||
.send_burn_transaction(
|
||||
definition_account_id.parse().unwrap(),
|
||||
holder_account_id.parse().unwrap(),
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
TokenProgramSubcommandPublic::MintToken {
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
Token(wallet_core)
|
||||
.send_mint_transaction(
|
||||
definition_account_id.parse().unwrap(),
|
||||
holder_account_id.parse().unwrap(),
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -421,8 +799,8 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![
|
||||
(secret_sender, sender_account_id),
|
||||
(secret_recipient, recipient_account_id),
|
||||
Decode(secret_sender, sender_account_id),
|
||||
Decode(secret_recipient, recipient_account_id),
|
||||
];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
@ -473,7 +851,191 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_sender, sender_account_id)];
|
||||
let acc_decode_data = vec![Decode(secret_sender, sender_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
&acc_decode_data,
|
||||
)?;
|
||||
}
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandPrivate::BurnTokenPrivateOwned {
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
let definition_account_id: AccountId = definition_account_id.parse().unwrap();
|
||||
let holder_account_id: AccountId = holder_account_id.parse().unwrap();
|
||||
|
||||
let (res, [secret_definition, secret_holder]) = Token(wallet_core)
|
||||
.send_burn_transaction_private_owned_account(
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send are {res:#?}");
|
||||
|
||||
let tx_hash = res.tx_hash;
|
||||
let transfer_tx = wallet_core
|
||||
.poll_native_token_transfer(tx_hash.clone())
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![
|
||||
Decode(secret_definition, definition_account_id),
|
||||
Decode(secret_holder, holder_account_id),
|
||||
];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
&acc_decode_data,
|
||||
)?;
|
||||
}
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandPrivate::BurnTokenPrivateForeign {
|
||||
definition_npk,
|
||||
definition_ipk,
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
let definition_npk_res = hex::decode(definition_npk)?;
|
||||
let mut definition_npk = [0; 32];
|
||||
definition_npk.copy_from_slice(&definition_npk_res);
|
||||
let definition_npk = nssa_core::NullifierPublicKey(definition_npk);
|
||||
|
||||
let definition_ipk_res = hex::decode(definition_ipk)?;
|
||||
let mut definition_ipk = [0u8; 33];
|
||||
definition_ipk.copy_from_slice(&definition_ipk_res);
|
||||
let definition_ipk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
definition_ipk.to_vec(),
|
||||
);
|
||||
|
||||
let holder_account_id: AccountId = holder_account_id.parse().unwrap();
|
||||
|
||||
let (res, [_, secret_holder]) = Token(wallet_core)
|
||||
.send_burn_transaction_private_foreign_account(
|
||||
definition_npk,
|
||||
definition_ipk,
|
||||
holder_account_id,
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send are {res:#?}");
|
||||
|
||||
let tx_hash = res.tx_hash;
|
||||
let transfer_tx = wallet_core
|
||||
.poll_native_token_transfer(tx_hash.clone())
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![Skip, Decode(secret_holder, holder_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
&acc_decode_data,
|
||||
)?;
|
||||
}
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandPrivate::MintTokenPrivateOwned {
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
let definition_account_id: AccountId = definition_account_id.parse().unwrap();
|
||||
let holder_account_id: AccountId = holder_account_id.parse().unwrap();
|
||||
|
||||
let (res, [secret_definition, secret_holder]) = Token(wallet_core)
|
||||
.send_mint_transaction_private_owned_account(
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send are {res:#?}");
|
||||
|
||||
let tx_hash = res.tx_hash;
|
||||
let transfer_tx = wallet_core
|
||||
.poll_native_token_transfer(tx_hash.clone())
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![
|
||||
Decode(secret_definition, definition_account_id),
|
||||
Decode(secret_holder, holder_account_id),
|
||||
];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
&acc_decode_data,
|
||||
)?;
|
||||
}
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandPrivate::MintTokenPrivateForeign {
|
||||
definition_account_id,
|
||||
holder_npk,
|
||||
holder_ipk,
|
||||
amount,
|
||||
} => {
|
||||
let definition_account_id: AccountId = definition_account_id.parse().unwrap();
|
||||
|
||||
let holder_npk_res = hex::decode(holder_npk)?;
|
||||
let mut holder_npk = [0; 32];
|
||||
holder_npk.copy_from_slice(&holder_npk_res);
|
||||
let holder_npk = nssa_core::NullifierPublicKey(holder_npk);
|
||||
|
||||
let holder_ipk_res = hex::decode(holder_ipk)?;
|
||||
let mut holder_ipk = [0u8; 33];
|
||||
holder_ipk.copy_from_slice(&holder_ipk_res);
|
||||
let holder_ipk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
holder_ipk.to_vec(),
|
||||
);
|
||||
|
||||
let (res, [secret_definition, _]) = Token(wallet_core)
|
||||
.send_mint_transaction_private_foreign_account(
|
||||
definition_account_id,
|
||||
holder_npk,
|
||||
holder_ipk,
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send are {res:#?}");
|
||||
|
||||
let tx_hash = res.tx_hash;
|
||||
let transfer_tx = wallet_core
|
||||
.poll_native_token_transfer(tx_hash.clone())
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![Decode(secret_definition, definition_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -521,7 +1083,129 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_sender, sender_account_id)];
|
||||
let acc_decode_data = vec![Decode(secret_sender, sender_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
&acc_decode_data,
|
||||
)?;
|
||||
}
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandDeshielded::BurnTokenDeshieldedOwned {
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
let definition_account_id: AccountId = definition_account_id.parse().unwrap();
|
||||
let holder_account_id: AccountId = holder_account_id.parse().unwrap();
|
||||
|
||||
let (res, secret_definition) = Token(wallet_core)
|
||||
.send_burn_transaction_deshielded_owned_account(
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send are {res:#?}");
|
||||
|
||||
let tx_hash = res.tx_hash;
|
||||
let transfer_tx = wallet_core
|
||||
.poll_native_token_transfer(tx_hash.clone())
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![Decode(secret_definition, definition_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
&acc_decode_data,
|
||||
)?;
|
||||
}
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandDeshielded::BurnTokenDeshieldedForeign {
|
||||
definition_npk,
|
||||
definition_ipk,
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
let definition_npk_res = hex::decode(definition_npk)?;
|
||||
let mut definition_npk = [0; 32];
|
||||
definition_npk.copy_from_slice(&definition_npk_res);
|
||||
let definition_npk = nssa_core::NullifierPublicKey(definition_npk);
|
||||
|
||||
let definition_ipk_res = hex::decode(definition_ipk)?;
|
||||
let mut definition_ipk = [0u8; 33];
|
||||
definition_ipk.copy_from_slice(&definition_ipk_res);
|
||||
let definition_ipk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
definition_ipk.to_vec(),
|
||||
);
|
||||
|
||||
let holder_account_id: AccountId = holder_account_id.parse().unwrap();
|
||||
|
||||
let (res, _) = Token(wallet_core)
|
||||
.send_burn_transaction_deshielded_foreign_account(
|
||||
definition_npk,
|
||||
definition_ipk,
|
||||
holder_account_id,
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send are {res:#?}");
|
||||
|
||||
let tx_hash = res.tx_hash;
|
||||
let transfer_tx = wallet_core
|
||||
.poll_native_token_transfer(tx_hash.clone())
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
println!("Transaction data is {:?}", tx.message);
|
||||
}
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandDeshielded::MintTokenDeshielded {
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
let definition_account_id: AccountId = definition_account_id.parse().unwrap();
|
||||
let holder_account_id: AccountId = holder_account_id.parse().unwrap();
|
||||
|
||||
let (res, secret_definition) = Token(wallet_core)
|
||||
.send_mint_transaction_deshielded(
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send are {res:#?}");
|
||||
|
||||
let tx_hash = res.tx_hash;
|
||||
let transfer_tx = wallet_core
|
||||
.poll_native_token_transfer(tx_hash.clone())
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![Decode(secret_definition, definition_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -614,7 +1298,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_recipient, recipient_account_id)];
|
||||
let acc_decode_data = vec![Decode(secret_recipient, recipient_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -626,6 +1310,128 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandShielded::BurnTokenShielded {
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
let definition_account_id: AccountId = definition_account_id.parse().unwrap();
|
||||
let holder_account_id: AccountId = holder_account_id.parse().unwrap();
|
||||
|
||||
let (res, secret_holder) = Token(wallet_core)
|
||||
.send_burn_transaction_shielded(
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send are {res:#?}");
|
||||
|
||||
let tx_hash = res.tx_hash;
|
||||
let transfer_tx = wallet_core
|
||||
.poll_native_token_transfer(tx_hash.clone())
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![Decode(secret_holder, holder_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
&acc_decode_data,
|
||||
)?;
|
||||
}
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandShielded::MintTokenShieldedOwned {
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
let definition_account_id: AccountId = definition_account_id.parse().unwrap();
|
||||
let holder_account_id: AccountId = holder_account_id.parse().unwrap();
|
||||
|
||||
let (res, secret_holder) = Token(wallet_core)
|
||||
.send_mint_transaction_shielded_owned_account(
|
||||
definition_account_id,
|
||||
holder_account_id,
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send are {res:#?}");
|
||||
|
||||
let tx_hash = res.tx_hash;
|
||||
let transfer_tx = wallet_core
|
||||
.poll_native_token_transfer(tx_hash.clone())
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![Decode(secret_holder, holder_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
&acc_decode_data,
|
||||
)?;
|
||||
}
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandShielded::MintTokenShieldedForeign {
|
||||
definition_account_id,
|
||||
holder_npk,
|
||||
holder_ipk,
|
||||
amount,
|
||||
} => {
|
||||
let definition_account_id: AccountId = definition_account_id.parse().unwrap();
|
||||
|
||||
let holder_npk_res = hex::decode(holder_npk)?;
|
||||
let mut holder_npk = [0; 32];
|
||||
holder_npk.copy_from_slice(&holder_npk_res);
|
||||
let holder_npk = nssa_core::NullifierPublicKey(holder_npk);
|
||||
|
||||
let holder_ipk_res = hex::decode(holder_ipk)?;
|
||||
let mut holder_ipk = [0u8; 33];
|
||||
holder_ipk.copy_from_slice(&holder_ipk_res);
|
||||
let holder_ipk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
holder_ipk.to_vec(),
|
||||
);
|
||||
|
||||
let (res, _) = Token(wallet_core)
|
||||
.send_mint_transaction_shielded_foreign_account(
|
||||
definition_account_id,
|
||||
holder_npk,
|
||||
holder_ipk,
|
||||
amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send are {res:#?}");
|
||||
|
||||
let tx_hash = res.tx_hash;
|
||||
let transfer_tx = wallet_core
|
||||
.poll_native_token_transfer(tx_hash.clone())
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
println!("Transaction data is {:?}", tx.message);
|
||||
}
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
}
|
||||
@ -673,8 +1479,8 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand {
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![
|
||||
(secret_definition, definition_account_id),
|
||||
(secret_supply, supply_account_id),
|
||||
Decode(secret_definition, definition_account_id),
|
||||
Decode(secret_supply, supply_account_id),
|
||||
];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
@ -723,7 +1529,7 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_definition, definition_account_id)];
|
||||
let acc_decode_data = vec![Decode(secret_definition, definition_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -771,7 +1577,7 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_supply, supply_account_id)];
|
||||
let acc_decode_data = vec![Decode(secret_supply, supply_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
|
||||
@ -38,6 +38,11 @@ pub mod poller;
|
||||
mod privacy_preserving_tx;
|
||||
pub mod program_facades;
|
||||
|
||||
pub enum AccDecodeData {
|
||||
Skip,
|
||||
Decode(nssa_core::SharedSecretKey, AccountId),
|
||||
}
|
||||
|
||||
pub struct WalletCore {
|
||||
pub storage: WalletChainStore,
|
||||
pub poller: TxPoller,
|
||||
@ -218,24 +223,29 @@ impl WalletCore {
|
||||
pub fn decode_insert_privacy_preserving_transaction_results(
|
||||
&mut self,
|
||||
tx: nssa::privacy_preserving_transaction::PrivacyPreservingTransaction,
|
||||
acc_decode_data: &[(nssa_core::SharedSecretKey, AccountId)],
|
||||
acc_decode_mask: &[AccDecodeData],
|
||||
) -> Result<()> {
|
||||
for (output_index, (secret, acc_account_id)) in acc_decode_data.iter().enumerate() {
|
||||
let acc_ead = tx.message.encrypted_private_post_states[output_index].clone();
|
||||
let acc_comm = tx.message.new_commitments[output_index].clone();
|
||||
for (output_index, acc_decode_data) in acc_decode_mask.iter().enumerate() {
|
||||
match acc_decode_data {
|
||||
AccDecodeData::Decode(secret, acc_account_id) => {
|
||||
let acc_ead = tx.message.encrypted_private_post_states[output_index].clone();
|
||||
let acc_comm = tx.message.new_commitments[output_index].clone();
|
||||
|
||||
let res_acc = nssa_core::EncryptionScheme::decrypt(
|
||||
&acc_ead.ciphertext,
|
||||
secret,
|
||||
&acc_comm,
|
||||
output_index as u32,
|
||||
)
|
||||
.unwrap();
|
||||
let res_acc = nssa_core::EncryptionScheme::decrypt(
|
||||
&acc_ead.ciphertext,
|
||||
secret,
|
||||
&acc_comm,
|
||||
output_index as u32,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("Received new acc {res_acc:#?}");
|
||||
println!("Received new acc {res_acc:#?}");
|
||||
|
||||
self.storage
|
||||
.insert_private_account_data(*acc_account_id, res_acc);
|
||||
self.storage
|
||||
.insert_private_account_data(*acc_account_id, res_acc);
|
||||
}
|
||||
AccDecodeData::Skip => {}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Transaction data is {:?}", tx.message);
|
||||
|
||||
@ -301,6 +301,370 @@ impl Token<'_> {
|
||||
(resp, first)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_burn_transaction(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||
let account_ids = vec![definition_account_id, holder_account_id];
|
||||
let (instruction, program) = token_program_preparation_burn(amount);
|
||||
|
||||
// ToDo: Fix this by updating `nssa::public_transaction::Message::try_new` to get raw bytes
|
||||
let instruction: [u32; 23] = instruction.try_into().unwrap();
|
||||
|
||||
let Ok(nonces) = self.0.get_accounts_nonces(vec![holder_account_id]).await else {
|
||||
return Err(ExecutionFailureKind::SequencerError);
|
||||
};
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let Some(signing_key) = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(&holder_account_id)
|
||||
else {
|
||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||
};
|
||||
let witness_set =
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
|
||||
Ok(self.0.sequencer_client.send_tx_public(tx).await?)
|
||||
}
|
||||
|
||||
pub async fn send_burn_transaction_private_owned_account(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
|
||||
let (instruction_data, program) = token_program_preparation_burn(amount);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(holder_account_id),
|
||||
],
|
||||
&instruction_data,
|
||||
&program,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
let mut iter = secrets.into_iter();
|
||||
let first = iter.next().expect("expected definition's secret");
|
||||
let second = iter.next().expect("expected holder's secret");
|
||||
(resp, [first, second])
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_burn_transaction_private_foreign_account(
|
||||
&self,
|
||||
definition_npk: NullifierPublicKey,
|
||||
definition_ipk: IncomingViewingPublicKey,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
|
||||
let (instruction_data, program) = token_program_preparation_burn(amount);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateForeign {
|
||||
npk: definition_npk,
|
||||
ipk: definition_ipk,
|
||||
},
|
||||
PrivacyPreservingAccount::PrivateOwned(holder_account_id),
|
||||
],
|
||||
&instruction_data,
|
||||
&program,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
let mut iter = secrets.into_iter();
|
||||
let first = iter.next().expect("expected definition's secret");
|
||||
let second = iter.next().expect("expected holder's secret");
|
||||
(resp, [first, second])
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_burn_transaction_deshielded_owned_account(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> {
|
||||
let (instruction_data, program) = token_program_preparation_burn(amount);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
PrivacyPreservingAccount::Public(holder_account_id),
|
||||
],
|
||||
&instruction_data,
|
||||
&program,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
let first = secrets
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("expected definition's secret");
|
||||
(resp, first)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_burn_transaction_shielded(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> {
|
||||
let (instruction_data, program) = token_program_preparation_burn(amount);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::Public(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(holder_account_id),
|
||||
],
|
||||
&instruction_data,
|
||||
&program,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
let first = secrets
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("expected holder's secret");
|
||||
(resp, first)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_burn_transaction_deshielded_foreign_account(
|
||||
&self,
|
||||
definition_npk: NullifierPublicKey,
|
||||
definition_ipk: IncomingViewingPublicKey,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> {
|
||||
let (instruction_data, program) = token_program_preparation_burn(amount);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateForeign {
|
||||
npk: definition_npk,
|
||||
ipk: definition_ipk,
|
||||
},
|
||||
PrivacyPreservingAccount::Public(holder_account_id),
|
||||
],
|
||||
&instruction_data,
|
||||
&program,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
let first = secrets
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("expected definition's secret");
|
||||
(resp, first)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_mint_transaction(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||
let account_ids = vec![definition_account_id, holder_account_id];
|
||||
let (instruction, program) = token_program_preparation_mint(amount);
|
||||
|
||||
// ToDo: Fix this by updating `nssa::public_transaction::Message::try_new` to get raw bytes
|
||||
let instruction: [u32; 23] = instruction.try_into().unwrap();
|
||||
|
||||
let Ok(nonces) = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![definition_account_id])
|
||||
.await
|
||||
else {
|
||||
return Err(ExecutionFailureKind::SequencerError);
|
||||
};
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let Some(signing_key) = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(&definition_account_id)
|
||||
else {
|
||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||
};
|
||||
let witness_set =
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
|
||||
Ok(self.0.sequencer_client.send_tx_public(tx).await?)
|
||||
}
|
||||
|
||||
pub async fn send_mint_transaction_private_owned_account(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
|
||||
let (instruction_data, program) = token_program_preparation_mint(amount);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(holder_account_id),
|
||||
],
|
||||
&instruction_data,
|
||||
&program,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
let mut iter = secrets.into_iter();
|
||||
let first = iter.next().expect("expected definition's secret");
|
||||
let second = iter.next().expect("expected holder's secret");
|
||||
(resp, [first, second])
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_mint_transaction_private_foreign_account(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_npk: NullifierPublicKey,
|
||||
holder_ipk: IncomingViewingPublicKey,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
|
||||
let (instruction_data, program) = token_program_preparation_mint(amount);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateForeign {
|
||||
npk: holder_npk,
|
||||
ipk: holder_ipk,
|
||||
},
|
||||
],
|
||||
&instruction_data,
|
||||
&program,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
let mut iter = secrets.into_iter();
|
||||
let first = iter.next().expect("expected definition's secret");
|
||||
let second = iter.next().expect("expected holder's secret");
|
||||
(resp, [first, second])
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_mint_transaction_deshielded(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> {
|
||||
let (instruction_data, program) = token_program_preparation_mint(amount);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
PrivacyPreservingAccount::Public(holder_account_id),
|
||||
],
|
||||
&instruction_data,
|
||||
&program,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
let first = secrets
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("expected definition's secret");
|
||||
(resp, first)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_mint_transaction_shielded_owned_account(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> {
|
||||
let (instruction_data, program) = token_program_preparation_mint(amount);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::Public(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(holder_account_id),
|
||||
],
|
||||
&instruction_data,
|
||||
&program,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
let first = secrets
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("expected holder's secret");
|
||||
(resp, first)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_mint_transaction_shielded_foreign_account(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_npk: NullifierPublicKey,
|
||||
holder_ipk: IncomingViewingPublicKey,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> {
|
||||
let (instruction_data, program) = token_program_preparation_mint(amount);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::Public(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateForeign {
|
||||
npk: holder_npk,
|
||||
ipk: holder_ipk,
|
||||
},
|
||||
],
|
||||
&instruction_data,
|
||||
&program,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
let first = secrets
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("expected holder's secret");
|
||||
(resp, first)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn token_program_preparation_transfer(amount: u128) -> (InstructionData, Program) {
|
||||
@ -328,3 +692,27 @@ fn token_program_preparation_definition(
|
||||
|
||||
(instruction_data, program)
|
||||
}
|
||||
|
||||
fn token_program_preparation_burn(amount: u128) -> (InstructionData, Program) {
|
||||
// Instruction must be: [0x03 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 ||
|
||||
// 0x00 || 0x00 || 0x00].
|
||||
let mut instruction = [0; 23];
|
||||
instruction[0] = 0x03;
|
||||
instruction[1..17].copy_from_slice(&amount.to_le_bytes());
|
||||
let instruction_data = Program::serialize_instruction(instruction).unwrap();
|
||||
let program = Program::token();
|
||||
|
||||
(instruction_data, program)
|
||||
}
|
||||
|
||||
fn token_program_preparation_mint(amount: u128) -> (InstructionData, Program) {
|
||||
// Instruction must be: [0x04 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 ||
|
||||
// 0x00 || 0x00 || 0x00].
|
||||
let mut instruction = [0; 23];
|
||||
instruction[0] = 0x04;
|
||||
instruction[1..17].copy_from_slice(&amount.to_le_bytes());
|
||||
let instruction_data = Program::serialize_instruction(instruction).unwrap();
|
||||
let program = Program::token();
|
||||
|
||||
(instruction_data, program)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user