#[cfg(not(target_arch = "bpf"))]
use safe_zk_token_sdk::encryption::{auth_encryption::AeCiphertext, elgamal::ElGamalPubkey};
pub use safe_zk_token_sdk::zk_token_proof_instruction::*;
use {
crate::{
check_program_account,
extension::confidential_transfer::*,
instruction::{encode_instruction, TokenInstruction},
},
bytemuck::{Pod, Zeroable},
num_enum::{IntoPrimitive, TryFromPrimitive},
safecoin_program::{
instruction::{AccountMeta, Instruction},
program_error::ProgramError,
pubkey::Pubkey,
sysvar,
},
safe_zk_token_sdk::zk_token_elgamal::pod,
std::convert::TryFrom,
};
#[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)]
#[repr(u8)]
pub enum ConfidentialTransferInstruction {
InitializeMint,
UpdateMint,
ConfigureAccount,
ApproveAccount,
EmptyAccount,
Deposit,
Withdraw,
Transfer,
ApplyPendingBalance,
EnableBalanceCredits,
DisableBalanceCredits,
WithdrawWithheldTokensFromMint,
WithdrawWithheldTokensFromAccounts,
HarvestWithheldTokensToMint,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct ConfigureAccountInstructionData {
pub encryption_pubkey: EncryptionPubkey,
pub decryptable_zero_balance: DecryptableBalance,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct EmptyAccountInstructionData {
pub proof_instruction_offset: i8,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct DepositInstructionData {
pub amount: PodU64,
pub decimals: u8,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct WithdrawInstructionData {
pub amount: PodU64,
pub decimals: u8,
pub new_decryptable_available_balance: DecryptableBalance,
pub proof_instruction_offset: i8,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct TransferInstructionData {
pub new_source_decryptable_available_balance: DecryptableBalance,
pub proof_instruction_offset: i8,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct ApplyPendingBalanceData {
pub expected_pending_balance_credit_counter: PodU64,
pub new_decryptable_available_balance: pod::AeCiphertext,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct WithdrawWithheldTokensFromMintData {
pub proof_instruction_offset: i8,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct WithdrawWithheldTokensFromAccountsData {
pub num_token_accounts: u8,
pub proof_instruction_offset: i8,
}
pub fn initialize_mint(
token_program_id: &Pubkey,
mint: &Pubkey,
ct_mint: &ConfidentialTransferMint,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let accounts = vec![AccountMeta::new(*mint, false)];
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::InitializeMint,
ct_mint,
))
}
pub fn update_mint(
token_program_id: &Pubkey,
mint: &Pubkey,
new_ct_mint: &ConfidentialTransferMint,
authority: &Pubkey,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let accounts = vec![
AccountMeta::new(*mint, false),
AccountMeta::new_readonly(*authority, true),
AccountMeta::new_readonly(
new_ct_mint.authority,
new_ct_mint.authority != Pubkey::default(),
),
];
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::UpdateMint,
new_ct_mint,
))
}
#[cfg(not(target_arch = "bpf"))]
pub fn configure_account(
token_program_id: &Pubkey,
token_account: &Pubkey,
mint: &Pubkey,
encryption_pubkey: ElGamalPubkey,
decryptable_zero_balance: AeCiphertext,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
AccountMeta::new(*token_account, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
];
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::ConfigureAccount,
&ConfigureAccountInstructionData {
encryption_pubkey: encryption_pubkey.into(),
decryptable_zero_balance: decryptable_zero_balance.into(),
},
))
}
pub fn approve_account(
token_program_id: &Pubkey,
account_to_approve: &Pubkey,
mint: &Pubkey,
authority: &Pubkey,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let accounts = vec![
AccountMeta::new(*account_to_approve, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(*authority, true),
];
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::ApproveAccount,
&(),
))
}
pub fn inner_empty_account(
token_program_id: &Pubkey,
token_account: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
proof_instruction_offset: i8,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
AccountMeta::new(*token_account, false),
AccountMeta::new_readonly(sysvar::instructions::id(), false),
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
];
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::EmptyAccount,
&EmptyAccountInstructionData {
proof_instruction_offset,
},
))
}
pub fn empty_account(
token_program_id: &Pubkey,
token_account: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
proof_data: &CloseAccountData,
) -> Result<Vec<Instruction>, ProgramError> {
Ok(vec![
verify_close_account(proof_data),
inner_empty_account(
token_program_id,
token_account,
authority,
multisig_signers,
-1,
)?, ])
}
#[allow(clippy::too_many_arguments)]
pub fn deposit(
token_program_id: &Pubkey,
source_token_account: &Pubkey,
mint: &Pubkey,
destination_token_account: &Pubkey,
amount: u64,
decimals: u8,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
AccountMeta::new(*source_token_account, false),
AccountMeta::new(*destination_token_account, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
];
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::Deposit,
&DepositInstructionData {
amount: amount.into(),
decimals,
},
))
}
#[allow(clippy::too_many_arguments)]
pub fn inner_withdraw(
token_program_id: &Pubkey,
source_token_account: &Pubkey,
destination_token_account: &Pubkey,
mint: &Pubkey,
amount: u64,
decimals: u8,
new_decryptable_available_balance: DecryptableBalance,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
proof_instruction_offset: i8,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
AccountMeta::new(*source_token_account, false),
AccountMeta::new(*destination_token_account, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(sysvar::instructions::id(), false),
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
];
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::Withdraw,
&WithdrawInstructionData {
amount: amount.into(),
decimals,
new_decryptable_available_balance,
proof_instruction_offset,
},
))
}
#[allow(clippy::too_many_arguments)]
#[cfg(not(target_arch = "bpf"))]
pub fn withdraw(
token_program_id: &Pubkey,
source_token_account: &Pubkey,
destination_token_account: &Pubkey,
mint: &Pubkey,
amount: u64,
decimals: u8,
new_decryptable_available_balance: AeCiphertext,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
proof_data: &WithdrawData,
) -> Result<Vec<Instruction>, ProgramError> {
Ok(vec![
verify_withdraw(proof_data),
inner_withdraw(
token_program_id,
source_token_account,
destination_token_account,
mint,
amount,
decimals,
new_decryptable_available_balance.into(),
authority,
multisig_signers,
-1,
)?, ])
}
#[allow(clippy::too_many_arguments)]
pub fn inner_transfer(
token_program_id: &Pubkey,
source_token_account: &Pubkey,
destination_token_account: &Pubkey,
mint: &Pubkey,
new_source_decryptable_available_balance: DecryptableBalance,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
proof_instruction_offset: i8,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
AccountMeta::new(*source_token_account, false),
AccountMeta::new(*destination_token_account, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(sysvar::instructions::id(), false),
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
];
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::Transfer,
&TransferInstructionData {
new_source_decryptable_available_balance,
proof_instruction_offset,
},
))
}
#[allow(clippy::too_many_arguments)]
#[cfg(not(target_arch = "bpf"))]
pub fn transfer(
token_program_id: &Pubkey,
source_token_account: &Pubkey,
destination_token_account: &Pubkey,
mint: &Pubkey,
new_source_decryptable_available_balance: AeCiphertext,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
proof_data: &TransferData,
) -> Result<Vec<Instruction>, ProgramError> {
Ok(vec![
verify_transfer(proof_data),
inner_transfer(
token_program_id,
source_token_account,
destination_token_account,
mint,
new_source_decryptable_available_balance.into(),
authority,
multisig_signers,
-1,
)?, ])
}
pub fn inner_apply_pending_balance(
token_program_id: &Pubkey,
token_account: &Pubkey,
expected_pending_balance_credit_counter: u64,
new_decryptable_available_balance: DecryptableBalance,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
AccountMeta::new(*token_account, false),
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
];
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::ApplyPendingBalance,
&ApplyPendingBalanceData {
expected_pending_balance_credit_counter: expected_pending_balance_credit_counter.into(),
new_decryptable_available_balance,
},
))
}
#[cfg(not(target_arch = "bpf"))]
pub fn apply_pending_balance(
token_program_id: &Pubkey,
token_account: &Pubkey,
pending_balance_instructions: u64,
new_decryptable_available_balance: AeCiphertext,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
inner_apply_pending_balance(
token_program_id,
token_account,
pending_balance_instructions,
new_decryptable_available_balance.into(),
authority,
multisig_signers,
) }
fn enable_or_disable_balance_credits(
instruction: ConfidentialTransferInstruction,
token_program_id: &Pubkey,
token_account: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
AccountMeta::new(*token_account, false),
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
];
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
instruction,
&(),
))
}
pub fn enable_balance_credits(
token_program_id: &Pubkey,
token_account: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
enable_or_disable_balance_credits(
ConfidentialTransferInstruction::EnableBalanceCredits,
token_program_id,
token_account,
authority,
multisig_signers,
)
}
pub fn disable_balance_credits(
token_program_id: &Pubkey,
token_account: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
enable_or_disable_balance_credits(
ConfidentialTransferInstruction::DisableBalanceCredits,
token_program_id,
token_account,
authority,
multisig_signers,
)
}
pub fn inner_withdraw_withheld_tokens_from_mint(
token_program_id: &Pubkey,
mint: &Pubkey,
destination: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
proof_instruction_offset: i8,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
AccountMeta::new(*mint, false),
AccountMeta::new(*destination, false),
AccountMeta::new_readonly(sysvar::instructions::id(), false),
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
];
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new(**multisig_signer, false));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::WithdrawWithheldTokensFromMint,
&WithdrawWithheldTokensFromMintData {
proof_instruction_offset,
},
))
}
pub fn withdraw_withheld_tokens_from_mint(
token_program_id: &Pubkey,
mint: &Pubkey,
destination: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
proof_data: &WithdrawWithheldTokensData,
) -> Result<Vec<Instruction>, ProgramError> {
Ok(vec![
verify_withdraw_withheld_tokens(proof_data),
inner_withdraw_withheld_tokens_from_mint(
token_program_id,
mint,
destination,
authority,
multisig_signers,
-1,
)?,
])
}
pub fn inner_withdraw_withheld_tokens_from_accounts(
token_program_id: &Pubkey,
mint: &Pubkey,
destination: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
sources: &[&Pubkey],
proof_instruction_offset: i8,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let num_token_accounts =
u8::try_from(sources.len()).map_err(|_| ProgramError::InvalidInstructionData)?;
let mut accounts = vec![
AccountMeta::new(*mint, false),
AccountMeta::new(*destination, false),
AccountMeta::new_readonly(sysvar::instructions::id(), false),
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
];
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new(**multisig_signer, false));
}
for source in sources.iter() {
accounts.push(AccountMeta::new(**source, false));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::WithdrawWithheldTokensFromAccounts,
&WithdrawWithheldTokensFromAccountsData {
proof_instruction_offset,
num_token_accounts,
},
))
}
pub fn withdraw_withheld_tokens_from_accounts(
token_program_id: &Pubkey,
mint: &Pubkey,
destination: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
sources: &[&Pubkey],
proof_data: &WithdrawWithheldTokensData,
) -> Result<Vec<Instruction>, ProgramError> {
Ok(vec![
verify_withdraw_withheld_tokens(proof_data),
inner_withdraw_withheld_tokens_from_accounts(
token_program_id,
mint,
destination,
authority,
multisig_signers,
sources,
-1,
)?,
])
}
pub fn harvest_withheld_tokens_to_mint(
token_program_id: &Pubkey,
mint: &Pubkey,
sources: &[&Pubkey],
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![AccountMeta::new(*mint, false)];
for source in sources.iter() {
accounts.push(AccountMeta::new(**source, false));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::HarvestWithheldTokensToMint,
&(),
))
}