safe_token_2022/extension/confidential_transfer/instruction.rs
1#[cfg(not(target_os = "solana"))]
2use safe_zk_token_sdk::encryption::auth_encryption::AeCiphertext;
3pub use safe_zk_token_sdk::zk_token_proof_instruction::*;
4use {
5 crate::{
6 check_program_account,
7 extension::confidential_transfer::*,
8 instruction::{encode_instruction, TokenInstruction},
9 },
10 bytemuck::{Pod, Zeroable},
11 num_enum::{IntoPrimitive, TryFromPrimitive},
12 solana_program::{
13 instruction::{AccountMeta, Instruction},
14 program_error::ProgramError,
15 pubkey::Pubkey,
16 sysvar,
17 },
18 safe_zk_token_sdk::zk_token_elgamal::pod,
19 std::convert::TryFrom,
20};
21
22/// Confidential Transfer extension instructions
23#[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)]
24#[repr(u8)]
25pub enum ConfidentialTransferInstruction {
26 /// Initializes confidential transfers for a mint.
27 ///
28 /// The `ConfidentialTransferInstruction::InitializeMint` instruction requires no signers
29 /// and MUST be included within the same Transaction as `TokenInstruction::InitializeMint`.
30 /// Otherwise another party can initialize the configuration.
31 ///
32 /// The instruction fails if the `TokenInstruction::InitializeMint` instruction has already
33 /// executed for the mint.
34 ///
35 /// Note that the `withdraw_withheld_authority_encryption_pubkey` cannot be updated after it is
36 /// initialized.
37 ///
38 /// Accounts expected by this instruction:
39 ///
40 /// 0. `[writable]` The SPL Token mint.
41 ///
42 /// Data expected by this instruction:
43 /// `InitializeMintData`
44 ///
45 InitializeMint,
46
47 /// Updates the confidential transfer mint configuration for a mint.
48 ///
49 /// Use `TokenInstruction::SetAuthority` to update the confidential transfer mint authority.
50 ///
51 /// The `withdraw_withheld_authority_encryption_pubkey` and `withheld_amount` ciphertext are
52 /// not updatable.
53 ///
54 /// Accounts expected by this instruction:
55 ///
56 /// 0. `[writable]` The SPL Token mint.
57 /// 1. `[signer]` Confidential transfer mint authority.
58 ///
59 /// Data expected by this instruction:
60 /// `UpdateMintData`
61 ///
62 UpdateMint,
63
64 /// Configures confidential transfers for a token account.
65 ///
66 /// The instruction fails if the confidential transfers are already configured, or if the mint
67 /// was not initialized with confidential transfer support.
68 ///
69 /// The instruction fails if the `TokenInstruction::InitializeAccount` instruction has not yet
70 /// successfully executed for the token account.
71 ///
72 /// Upon success, confidential and non-confidential deposits and transfers are enabled. Use the
73 /// `DisableConfidentialCredits` and `DisableNonConfidentialCredits` instructions to disable.
74 ///
75 /// In order for this instruction to be successfully processed, it must be accompanied by the
76 /// `VerifyPubkey` instruction of the `zk_token_proof` program in the same transaction.
77 ///
78 /// Accounts expected by this instruction:
79 ///
80 /// * Single owner/delegate
81 /// 0. `[writeable]` The SPL Token account.
82 /// 1. `[]` The corresponding SPL Token mint.
83 /// 2. `[]` Instructions sysvar.
84 /// 3. `[signer]` The single source account owner.
85 ///
86 /// * Multisignature owner/delegate
87 /// 0. `[writeable]` The SPL Token account.
88 /// 1. `[]` The corresponding SPL Token mint.
89 /// 2. `[]` The multisig source account owner.
90 /// 3. `[]` Instructions sysvar.
91 /// 4.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
92 ///
93 /// Data expected by this instruction:
94 /// `ConfigureAccountInstructionData`
95 ///
96 ConfigureAccount,
97
98 /// Approves a token account for confidential transfers.
99 ///
100 /// Approval is only required when the `ConfidentialTransferMint::approve_new_accounts`
101 /// field is set in the SPL Token mint. This instruction must be executed after the account
102 /// owner configures their account for confidential transfers with
103 /// `ConfidentialTransferInstruction::ConfigureAccount`.
104 ///
105 /// Accounts expected by this instruction:
106 ///
107 /// 0. `[writable]` The SPL Token account to approve.
108 /// 1. `[]` The SPL Token mint.
109 /// 2. `[signer]` Confidential transfer auditor authority.
110 ///
111 /// Data expected by this instruction:
112 /// None
113 ///
114 ApproveAccount,
115
116 /// Prepare a token account for closing. The account must not hold any confidential tokens in
117 /// its pending or available balances. Use
118 /// `ConfidentialTransferInstruction::DisableConfidentialCredits` to block balance credit
119 /// changes first if necessary.
120 ///
121 /// Note that a newly configured account is always empty, so this instruction is not required
122 /// prior to account closing if no instructions beyond
123 /// `ConfidentialTransferInstruction::ConfigureAccount` have affected the token account.
124 ///
125 /// In order for this instruction to be successfully processed, it must be accompanied by the
126 /// `VerifyCloseAccount` instruction of the `zk_token_proof` program in the same transaction.
127 ///
128 /// * Single owner/delegate
129 /// 0. `[writable]` The SPL Token account.
130 /// 1. `[]` Instructions sysvar.
131 /// 2. `[signer]` The single account owner.
132 ///
133 /// * Multisignature owner/delegate
134 /// 0. `[writable]` The SPL Token account.
135 /// 1. `[]` Instructions sysvar.
136 /// 2. `[]` The multisig account owner.
137 /// 3.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
138 ///
139 /// Data expected by this instruction:
140 /// `EmptyAccountInstructionData`
141 ///
142 EmptyAccount,
143
144 /// Deposit SPL Tokens into the pending balance of a confidential token account.
145 ///
146 /// The account owner can then invoke the `ApplyPendingBalance` instruction to roll the deposit
147 /// into their available balance at a time of their choosing.
148 ///
149 /// Fails if the source or destination accounts are frozen.
150 /// Fails if the associated mint is extended as `NonTransferable`.
151 ///
152 /// Accounts expected by this instruction:
153 ///
154 /// * Single owner/delegate
155 /// 0. `[writable]` The SPL Token account.
156 /// 1. `[]` The token mint.
157 /// 2. `[signer]` The single account owner or delegate.
158 ///
159 /// * Multisignature owner/delegate
160 /// 0. `[writable]` The SPL Token account.
161 /// 1. `[]` The token mint.
162 /// 2. `[]` The multisig account owner or delegate.
163 /// 3.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
164 ///
165 /// Data expected by this instruction:
166 /// `DepositInstructionData`
167 ///
168 Deposit,
169
170 /// Withdraw SPL Tokens from the available balance of a confidential token account.
171 ///
172 /// Fails if the source or destination accounts are frozen.
173 /// Fails if the associated mint is extended as `NonTransferable`.
174 ///
175 /// In order for this instruction to be successfully processed, it must be accompanied by the
176 /// `VerifyWithdraw` instruction of the `zk_token_proof` program in the same transaction.
177 ///
178 /// Accounts expected by this instruction:
179 ///
180 /// * Single owner/delegate
181 /// 0. `[writable]` The SPL Token account.
182 /// 1. `[]` The token mint.
183 /// 2. `[]` Instructions sysvar.
184 /// 3. `[signer]` The single source account owner.
185 ///
186 /// * Multisignature owner/delegate
187 /// 0. `[writable]` The SPL Token account.
188 /// 1. `[]` The token mint.
189 /// 2. `[]` Instructions sysvar.
190 /// 3. `[]` The multisig source account owner.
191 /// 4.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
192 ///
193 /// Data expected by this instruction:
194 /// `WithdrawInstructionData`
195 ///
196 Withdraw,
197
198 /// Transfer tokens confidentially.
199 ///
200 /// In order for this instruction to be successfully processed, it must be accompanied by
201 /// either the `VerifyTransfer` or `VerifyTransferWithFee` instruction of the `zk_token_proof`
202 /// program in the same transaction.
203 ///
204 /// Fails if the associated mint is extended as `NonTransferable`.
205 ///
206 /// * Single owner/delegate
207 /// 1. `[writable]` The source SPL Token account.
208 /// 2. `[writable]` The destination SPL Token account.
209 /// 3. `[]` The token mint.
210 /// 4. `[]` Instructions sysvar.
211 /// 5. `[signer]` The single source account owner.
212 ///
213 /// * Multisignature owner/delegate
214 /// 1. `[writable]` The source SPL Token account.
215 /// 2. `[writable]` The destination SPL Token account.
216 /// 3. `[]` The token mint.
217 /// 4. `[]` Instructions sysvar.
218 /// 5. `[]` The multisig source account owner.
219 /// 6.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
220 ///
221 /// Data expected by this instruction:
222 /// `TransferInstructionData`
223 ///
224 Transfer,
225
226 /// Applies the pending balance to the available balance, based on the history of `Deposit`
227 /// and/or `Transfer` instructions.
228 ///
229 /// After submitting `ApplyPendingBalance`, the client should compare
230 /// `ConfidentialTransferAccount::expected_pending_balance_credit_counter` with
231 /// `ConfidentialTransferAccount::actual_applied_pending_balance_instructions`. If they are
232 /// equal then the `ConfidentialTransferAccount::decryptable_available_balance` is consistent
233 /// with `ConfidentialTransferAccount::available_balance`. If they differ then there is more
234 /// pending balance to be applied.
235 ///
236 /// Account expected by this instruction:
237 ///
238 /// * Single owner/delegate
239 /// 0. `[writable]` The SPL Token account.
240 /// 1. `[signer]` The single account owner.
241 ///
242 /// * Multisignature owner/delegate
243 /// 0. `[writable]` The SPL Token account.
244 /// 1. `[]` The multisig account owner.
245 /// 2.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
246 ///
247 /// Data expected by this instruction:
248 /// `ApplyPendingBalanceData`
249 ///
250 ApplyPendingBalance,
251
252 /// Configure a confidential extension account to accept incoming confidential transfers.
253 ///
254 /// Accounts expected by this instruction:
255 ///
256 /// * Single owner/delegate
257 /// 0. `[writable]` The SPL Token account.
258 /// 1. `[signer]` Single authority.
259 ///
260 /// * Multisignature owner/delegate
261 /// 0. `[writable]` The SPL Token account.
262 /// 1. `[]` Multisig authority.
263 /// 2.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
264 ///
265 /// Data expected by this instruction:
266 /// None
267 ///
268 EnableConfidentialCredits,
269
270 /// Configure a confidential extension account to reject any incoming confidential transfers.
271 ///
272 /// If the `allow_non_confidential_credits` field is `true`, then the base account can still
273 /// receive non-confidential transfers.
274 ///
275 /// This instruction can be used to disable confidential payments after a token account has
276 /// already been extended for confidential transfers.
277 ///
278 /// Accounts expected by this instruction:
279 ///
280 /// * Single owner/delegate
281 /// 0. `[writable]` The SPL Token account.
282 /// 1. `[signer]` The single account owner.
283 ///
284 /// * Multisignature owner/delegate
285 /// 0. `[writable]` The SPL Token account.
286 /// 1. `[]` The multisig account owner.
287 /// 2.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
288 ///
289 /// Data expected by this instruction:
290 /// None
291 ///
292 DisableConfidentialCredits,
293
294 /// Configure an account with the confidential extension to accept incoming non-confidential
295 /// transfers.
296 ///
297 /// Accounts expected by this instruction:
298 ///
299 /// * Single owner/delegate
300 /// 0. `[writable]` The SPL Token account.
301 /// 1. `[signer]` The single account owner.
302 ///
303 /// * Multisignature owner/delegate
304 /// 0. `[writable]` The SPL Token account.
305 /// 1. `[]` The multisig account owner.
306 /// 2.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
307 ///
308 /// Data expected by this instruction:
309 /// None
310 ///
311 EnableNonConfidentialCredits,
312
313 /// Configure an account with the confidential extension to reject any incoming
314 /// non-confidential transfers.
315 ///
316 /// This instruction can be used to configure a confidential extension account to exclusively
317 /// receive confidential payments.
318 ///
319 /// Accounts expected by this instruction:
320 ///
321 /// * Single owner/delegate
322 /// 0. `[writable]` The SPL Token account.
323 /// 1. `[signer]` The single account owner.
324 ///
325 /// * Multisignature owner/delegate
326 /// 0. `[writable]` The SPL Token account.
327 /// 1. `[]` The multisig account owner.
328 /// 2.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
329 ///
330 /// Data expected by this instruction:
331 /// None
332 ///
333 DisableNonConfidentialCredits,
334
335 /// Transfer all withheld confidential tokens in the mint to an account. Signed by the mint's
336 /// withdraw withheld tokens authority.
337 ///
338 /// In order for this instruction to be successfully processed, it must be accompanied by the
339 /// `VerifyWithdrawWithheldTokens` instruction of the `zk_token_proof` program in the same
340 /// transaction.
341 ///
342 /// Accounts expected by this instruction:
343 ///
344 /// * Single owner/delegate
345 /// 0. `[writable]` The token mint. Must include the `TransferFeeConfig` extension.
346 /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` and
347 /// `ConfidentialTransferAccount` extensions.
348 /// 2. `[]` Instructions sysvar.
349 /// 3. `[signer]` The mint's `withdraw_withheld_authority`.
350 ///
351 /// * Multisignature owner/delegate
352 /// 0. `[writable]` The token mint. Must include the `TransferFeeConfig` extension.
353 /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` and
354 /// `ConfidentialTransferAccount` extensions.
355 /// 2. `[]` Instructions sysvar.
356 /// 3. `[]` The mint's multisig `withdraw_withheld_authority`.
357 /// 4. ..3+M `[signer]` M signer accounts.
358 ///
359 /// Data expected by this instruction:
360 /// WithdrawWithheldTokensFromMintData
361 ///
362 WithdrawWithheldTokensFromMint,
363
364 /// Transfer all withheld tokens to an account. Signed by the mint's withdraw withheld tokens
365 /// authority. This instruction is susceptible to front-running. Use
366 /// `HarvestWithheldTokensToMint` and `WithdrawWithheldTokensFromMint` as an alternative.
367 ///
368 /// Note on front-running: This instruction requires a zero-knowledge proof verification
369 /// instruction that is checked with respect to the account state (the currently withheld
370 /// fees). Suppose that a withdraw withheld authority generates the
371 /// `WithdrawWithheldTokensFromAccounts` instruction along with a corresponding zero-knowledge
372 /// proof for a specified set of accounts, and submits it on chain. If the withheld fees at any
373 /// of the specified accounts change before the `WithdrawWithheldTokensFromAccounts` is
374 /// executed on chain, the zero-knowledge proof will not verify with respect to the new state,
375 /// forcing the transaction to fail.
376 ///
377 /// If front-running occurs, then users can look up the updated states of the accounts,
378 /// generate a new zero-knowledge proof and try again. Alternatively, withdraw withheld
379 /// authority can first move the withheld amount to the mint using
380 /// `HarvestWithheldTokensToMint` and then move the withheld fees from mint to a specified
381 /// destination account using `WithdrawWithheldTokensFromMint`.
382 ///
383 /// In order for this instruction to be successfully processed, it must be accompanied by the
384 /// `VerifyWithdrawWithheldTokens` instruction of the `zk_token_proof` program in the same
385 /// transaction.
386 ///
387 /// Accounts expected by this instruction:
388 ///
389 /// * Single owner/delegate
390 /// 0. `[]` The token mint. Must include the `TransferFeeConfig` extension.
391 /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` and
392 /// `ConfidentialTransferAccount` extensions.
393 /// 2. `[]` Instructions sysvar.
394 /// 3. `[signer]` The mint's `withdraw_withheld_authority`.
395 /// 4. ..3+N `[writable]` The source accounts to withdraw from.
396 ///
397 /// * Multisignature owner/delegate
398 /// 0. `[]` The token mint. Must include the `TransferFeeConfig` extension.
399 /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` and
400 /// `ConfidentialTransferAccount` extensions.
401 /// 2. `[]` Instructions sysvar.
402 /// 3. `[]` The mint's multisig `withdraw_withheld_authority`.
403 /// 4. ..4+M `[signer]` M signer accounts.
404 /// 4+M+1. ..3+M+N `[writable]` The source accounts to withdraw from.
405 ///
406 /// Data expected by this instruction:
407 /// WithdrawWithheldTokensFromAccountsData
408 ///
409 WithdrawWithheldTokensFromAccounts,
410
411 /// Permissionless instruction to transfer all withheld confidential tokens to the mint.
412 ///
413 /// Succeeds for frozen accounts.
414 ///
415 /// Accounts provided should include both the `TransferFeeAmount` and
416 /// `ConfidentialTransferAccount` extension. If not, the account is skipped.
417 ///
418 /// Accounts expected by this instruction:
419 ///
420 /// 0. `[writable]` The mint.
421 /// 1. ..1+N `[writable]` The source accounts to harvest from.
422 ///
423 /// Data expected by this instruction:
424 /// None
425 ///
426 HarvestWithheldTokensToMint,
427}
428
429/// Data expected by `ConfidentialTransferInstruction::InitializeMint`
430#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
431#[repr(C)]
432pub struct InitializeMintData {
433 /// Authority to modify the `ConfidentialTransferMint` configuration and to approve new
434 /// accounts.
435 pub authority: OptionalNonZeroPubkey,
436 /// Determines if newly configured accounts must be approved by the `authority` before they may
437 /// be used by the user.
438 pub auto_approve_new_accounts: PodBool,
439 /// New authority to decode any transfer amount in a confidential transfer.
440 pub auditor_encryption_pubkey: OptionalNonZeroEncryptionPubkey,
441 /// Authority to withdraw withheld fees that are associated with accounts.
442 pub withdraw_withheld_authority_encryption_pubkey: OptionalNonZeroEncryptionPubkey,
443}
444
445/// Data expected by `ConfidentialTransferInstruction::UpdateMint`
446#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
447#[repr(C)]
448pub struct UpdateMintData {
449 /// Determines if newly configured accounts must be approved by the `authority` before they may
450 /// be used by the user.
451 pub auto_approve_new_accounts: PodBool,
452 /// New authority to decode any transfer amount in a confidential transfer.
453 pub auditor_encryption_pubkey: OptionalNonZeroEncryptionPubkey,
454}
455
456/// Data expected by `ConfidentialTransferInstruction::ConfigureAccount`
457#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
458#[repr(C)]
459pub struct ConfigureAccountInstructionData {
460 /// The decryptable balance (always 0) once the configure account succeeds
461 pub decryptable_zero_balance: DecryptableBalance,
462 /// The maximum number of despots and transfers that an account can receiver before the
463 /// `ApplyPendingBalance` is executed
464 pub maximum_pending_balance_credit_counter: PodU64,
465 /// Relative location of the `ProofInstruction::VerifyPubkey` instruction to the
466 /// `ConfigureAccount` instruction in the transaction
467 pub proof_instruction_offset: i8,
468}
469
470/// Data expected by `ConfidentialTransferInstruction::EmptyAccount`
471#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
472#[repr(C)]
473pub struct EmptyAccountInstructionData {
474 /// Relative location of the `ProofInstruction::VerifyCloseAccount` instruction to the
475 /// `EmptyAccount` instruction in the transaction
476 pub proof_instruction_offset: i8,
477}
478
479/// Data expected by `ConfidentialTransferInstruction::Deposit`
480#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
481#[repr(C)]
482pub struct DepositInstructionData {
483 /// The amount of tokens to deposit
484 pub amount: PodU64,
485 /// Expected number of base 10 digits to the right of the decimal place
486 pub decimals: u8,
487}
488
489/// Data expected by `ConfidentialTransferInstruction::Withdraw`
490#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
491#[repr(C)]
492pub struct WithdrawInstructionData {
493 /// The amount of tokens to withdraw
494 pub amount: PodU64,
495 /// Expected number of base 10 digits to the right of the decimal place
496 pub decimals: u8,
497 /// The new decryptable balance if the withdrawal succeeds
498 pub new_decryptable_available_balance: DecryptableBalance,
499 /// Relative location of the `ProofInstruction::VerifyWithdraw` instruction to the `Withdraw`
500 /// instruction in the transaction
501 pub proof_instruction_offset: i8,
502}
503
504/// Data expected by `ConfidentialTransferInstruction::Transfer`
505#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
506#[repr(C)]
507pub struct TransferInstructionData {
508 /// The new source decryptable balance if the transfer succeeds
509 pub new_source_decryptable_available_balance: DecryptableBalance,
510 /// Relative location of the `ProofInstruction::VerifyTransfer` instruction to the
511 /// `Transfer` instruction in the transaction
512 pub proof_instruction_offset: i8,
513}
514
515/// Data expected by `ConfidentialTransferInstruction::ApplyPendingBalance`
516#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
517#[repr(C)]
518pub struct ApplyPendingBalanceData {
519 /// The expected number of pending balance credits since the last successful
520 /// `ApplyPendingBalance` instruction
521 pub expected_pending_balance_credit_counter: PodU64,
522 /// The new decryptable balance if the pending balance is applied successfully
523 pub new_decryptable_available_balance: pod::AeCiphertext,
524}
525
526/// Data expected by `ConfidentialTransferInstruction::WithdrawWithheldTokensFromMint`
527#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
528#[repr(C)]
529pub struct WithdrawWithheldTokensFromMintData {
530 /// Relative location of the `ProofInstruction::VerifyWithdrawWithheld` instruction to the
531 /// `WithdrawWithheldTokensFromMint` instruction in the transaction
532 pub proof_instruction_offset: i8,
533}
534
535/// Data expected by `ConfidentialTransferInstruction::WithdrawWithheldTokensFromAccounts`
536#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
537#[repr(C)]
538pub struct WithdrawWithheldTokensFromAccountsData {
539 /// Number of token accounts harvested
540 pub num_token_accounts: u8,
541 /// Relative location of the `ProofInstruction::VerifyWithdrawWithheld` instruction to the
542 /// `VerifyWithdrawWithheldTokensFromAccounts` instruction in the transaction
543 pub proof_instruction_offset: i8,
544}
545
546/// Create a `InitializeMint` instruction
547#[cfg(not(target_os = "solana"))]
548pub fn initialize_mint(
549 token_program_id: &Pubkey,
550 mint: &Pubkey,
551 authority: Option<Pubkey>,
552 auto_approve_new_accounts: bool,
553 auditor_encryption_pubkey: Option<EncryptionPubkey>,
554 withdraw_withheld_authority_encryption_pubkey: Option<EncryptionPubkey>,
555) -> Result<Instruction, ProgramError> {
556 check_program_account(token_program_id)?;
557 let accounts = vec![AccountMeta::new(*mint, false)];
558
559 Ok(encode_instruction(
560 token_program_id,
561 accounts,
562 TokenInstruction::ConfidentialTransferExtension,
563 ConfidentialTransferInstruction::InitializeMint,
564 &InitializeMintData {
565 authority: authority.try_into()?,
566 auto_approve_new_accounts: auto_approve_new_accounts.into(),
567 auditor_encryption_pubkey: auditor_encryption_pubkey.try_into()?,
568 withdraw_withheld_authority_encryption_pubkey:
569 withdraw_withheld_authority_encryption_pubkey.try_into()?,
570 },
571 ))
572}
573
574/// Create a `UpdateMint` instruction
575#[cfg(not(target_os = "solana"))]
576pub fn update_mint(
577 token_program_id: &Pubkey,
578 mint: &Pubkey,
579 authority: &Pubkey,
580 auto_approve_new_accounts: bool,
581 auditor_encryption_pubkey: Option<EncryptionPubkey>,
582) -> Result<Instruction, ProgramError> {
583 check_program_account(token_program_id)?;
584
585 let accounts = vec![
586 AccountMeta::new(*mint, false),
587 AccountMeta::new_readonly(*authority, true),
588 ];
589
590 Ok(encode_instruction(
591 token_program_id,
592 accounts,
593 TokenInstruction::ConfidentialTransferExtension,
594 ConfidentialTransferInstruction::UpdateMint,
595 &UpdateMintData {
596 auto_approve_new_accounts: auto_approve_new_accounts.into(),
597 auditor_encryption_pubkey: auditor_encryption_pubkey.try_into()?,
598 },
599 ))
600}
601
602/// Create a `ConfigureAccount` instruction
603///
604/// This instruction is suitable for use with a cross-program `invoke`
605#[allow(clippy::too_many_arguments)]
606#[cfg(not(target_os = "solana"))]
607pub fn inner_configure_account(
608 token_program_id: &Pubkey,
609 token_account: &Pubkey,
610 mint: &Pubkey,
611 decryptable_zero_balance: AeCiphertext,
612 maximum_pending_balance_credit_counter: u64,
613 authority: &Pubkey,
614 multisig_signers: &[&Pubkey],
615 proof_instruction_offset: i8,
616) -> Result<Instruction, ProgramError> {
617 check_program_account(token_program_id)?;
618 let mut accounts = vec![
619 AccountMeta::new(*token_account, false),
620 AccountMeta::new_readonly(*mint, false),
621 AccountMeta::new_readonly(sysvar::instructions::id(), false),
622 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
623 ];
624
625 for multisig_signer in multisig_signers.iter() {
626 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
627 }
628
629 Ok(encode_instruction(
630 token_program_id,
631 accounts,
632 TokenInstruction::ConfidentialTransferExtension,
633 ConfidentialTransferInstruction::ConfigureAccount,
634 &ConfigureAccountInstructionData {
635 decryptable_zero_balance: decryptable_zero_balance.into(),
636 maximum_pending_balance_credit_counter: maximum_pending_balance_credit_counter.into(),
637 proof_instruction_offset,
638 },
639 ))
640}
641
642/// Create a `ConfigureAccount` instruction
643#[allow(clippy::too_many_arguments)]
644#[cfg(not(target_os = "solana"))]
645pub fn configure_account(
646 token_program_id: &Pubkey,
647 token_account: &Pubkey,
648 mint: &Pubkey,
649 decryptable_zero_balance: AeCiphertext,
650 maximum_pending_balance_credit_counter: u64,
651 authority: &Pubkey,
652 multisig_signers: &[&Pubkey],
653 proof_data: &PubkeyValidityData,
654) -> Result<Vec<Instruction>, ProgramError> {
655 Ok(vec![
656 inner_configure_account(
657 token_program_id,
658 token_account,
659 mint,
660 decryptable_zero_balance,
661 maximum_pending_balance_credit_counter,
662 authority,
663 multisig_signers,
664 1,
665 )?,
666 verify_pubkey_validity(proof_data),
667 ])
668}
669
670/// Create an `ApproveAccount` instruction
671pub fn approve_account(
672 token_program_id: &Pubkey,
673 account_to_approve: &Pubkey,
674 mint: &Pubkey,
675 authority: &Pubkey,
676) -> Result<Instruction, ProgramError> {
677 check_program_account(token_program_id)?;
678 let accounts = vec![
679 AccountMeta::new(*account_to_approve, false),
680 AccountMeta::new_readonly(*mint, false),
681 AccountMeta::new_readonly(*authority, true),
682 ];
683 Ok(encode_instruction(
684 token_program_id,
685 accounts,
686 TokenInstruction::ConfidentialTransferExtension,
687 ConfidentialTransferInstruction::ApproveAccount,
688 &(),
689 ))
690}
691
692/// Create an inner `EmptyAccount` instruction
693///
694/// This instruction is suitable for use with a cross-program `invoke`
695pub fn inner_empty_account(
696 token_program_id: &Pubkey,
697 token_account: &Pubkey,
698 authority: &Pubkey,
699 multisig_signers: &[&Pubkey],
700 proof_instruction_offset: i8,
701) -> Result<Instruction, ProgramError> {
702 check_program_account(token_program_id)?;
703 let mut accounts = vec![
704 AccountMeta::new(*token_account, false),
705 AccountMeta::new_readonly(sysvar::instructions::id(), false),
706 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
707 ];
708
709 for multisig_signer in multisig_signers.iter() {
710 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
711 }
712
713 Ok(encode_instruction(
714 token_program_id,
715 accounts,
716 TokenInstruction::ConfidentialTransferExtension,
717 ConfidentialTransferInstruction::EmptyAccount,
718 &EmptyAccountInstructionData {
719 proof_instruction_offset,
720 },
721 ))
722}
723
724/// Create a `EmptyAccount` instruction
725pub fn empty_account(
726 token_program_id: &Pubkey,
727 token_account: &Pubkey,
728 authority: &Pubkey,
729 multisig_signers: &[&Pubkey],
730 proof_data: &CloseAccountData,
731) -> Result<Vec<Instruction>, ProgramError> {
732 Ok(vec![
733 inner_empty_account(
734 token_program_id,
735 token_account,
736 authority,
737 multisig_signers,
738 1,
739 )?, // calls check_program_account
740 verify_close_account(proof_data),
741 ])
742}
743
744/// Create a `Deposit` instruction
745#[allow(clippy::too_many_arguments)]
746pub fn deposit(
747 token_program_id: &Pubkey,
748 token_account: &Pubkey,
749 mint: &Pubkey,
750 amount: u64,
751 decimals: u8,
752 authority: &Pubkey,
753 multisig_signers: &[&Pubkey],
754) -> Result<Instruction, ProgramError> {
755 check_program_account(token_program_id)?;
756 let mut accounts = vec![
757 AccountMeta::new(*token_account, false),
758 AccountMeta::new_readonly(*mint, false),
759 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
760 ];
761
762 for multisig_signer in multisig_signers.iter() {
763 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
764 }
765
766 Ok(encode_instruction(
767 token_program_id,
768 accounts,
769 TokenInstruction::ConfidentialTransferExtension,
770 ConfidentialTransferInstruction::Deposit,
771 &DepositInstructionData {
772 amount: amount.into(),
773 decimals,
774 },
775 ))
776}
777
778/// Create a inner `Withdraw` instruction
779///
780/// This instruction is suitable for use with a cross-program `invoke`
781#[allow(clippy::too_many_arguments)]
782pub fn inner_withdraw(
783 token_program_id: &Pubkey,
784 token_account: &Pubkey,
785 mint: &Pubkey,
786 amount: u64,
787 decimals: u8,
788 new_decryptable_available_balance: DecryptableBalance,
789 authority: &Pubkey,
790 multisig_signers: &[&Pubkey],
791 proof_instruction_offset: i8,
792) -> Result<Instruction, ProgramError> {
793 check_program_account(token_program_id)?;
794 let mut accounts = vec![
795 AccountMeta::new(*token_account, false),
796 AccountMeta::new_readonly(*mint, false),
797 AccountMeta::new_readonly(sysvar::instructions::id(), false),
798 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
799 ];
800
801 for multisig_signer in multisig_signers.iter() {
802 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
803 }
804
805 Ok(encode_instruction(
806 token_program_id,
807 accounts,
808 TokenInstruction::ConfidentialTransferExtension,
809 ConfidentialTransferInstruction::Withdraw,
810 &WithdrawInstructionData {
811 amount: amount.into(),
812 decimals,
813 new_decryptable_available_balance,
814 proof_instruction_offset,
815 },
816 ))
817}
818
819/// Create a `Withdraw` instruction
820#[allow(clippy::too_many_arguments)]
821#[cfg(not(target_os = "solana"))]
822pub fn withdraw(
823 token_program_id: &Pubkey,
824 token_account: &Pubkey,
825 mint: &Pubkey,
826 amount: u64,
827 decimals: u8,
828 new_decryptable_available_balance: AeCiphertext,
829 authority: &Pubkey,
830 multisig_signers: &[&Pubkey],
831 proof_data: &WithdrawData,
832) -> Result<Vec<Instruction>, ProgramError> {
833 Ok(vec![
834 inner_withdraw(
835 token_program_id,
836 token_account,
837 mint,
838 amount,
839 decimals,
840 new_decryptable_available_balance.into(),
841 authority,
842 multisig_signers,
843 1,
844 )?, // calls check_program_account
845 verify_withdraw(proof_data),
846 ])
847}
848
849/// Create a inner `Transfer` instruction
850///
851/// This instruction is suitable for use with a cross-program `invoke`
852#[allow(clippy::too_many_arguments)]
853pub fn inner_transfer(
854 token_program_id: &Pubkey,
855 source_token_account: &Pubkey,
856 destination_token_account: &Pubkey,
857 mint: &Pubkey,
858 new_source_decryptable_available_balance: DecryptableBalance,
859 authority: &Pubkey,
860 multisig_signers: &[&Pubkey],
861 proof_instruction_offset: i8,
862) -> Result<Instruction, ProgramError> {
863 check_program_account(token_program_id)?;
864 let mut accounts = vec![
865 AccountMeta::new(*source_token_account, false),
866 AccountMeta::new(*destination_token_account, false),
867 AccountMeta::new_readonly(*mint, false),
868 AccountMeta::new_readonly(sysvar::instructions::id(), false),
869 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
870 ];
871
872 for multisig_signer in multisig_signers.iter() {
873 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
874 }
875
876 Ok(encode_instruction(
877 token_program_id,
878 accounts,
879 TokenInstruction::ConfidentialTransferExtension,
880 ConfidentialTransferInstruction::Transfer,
881 &TransferInstructionData {
882 new_source_decryptable_available_balance,
883 proof_instruction_offset,
884 },
885 ))
886}
887
888/// Create a `Transfer` instruction with regular (no-fee) proof
889#[allow(clippy::too_many_arguments)]
890#[cfg(not(target_os = "solana"))]
891pub fn transfer(
892 token_program_id: &Pubkey,
893 source_token_account: &Pubkey,
894 destination_token_account: &Pubkey,
895 mint: &Pubkey,
896 new_source_decryptable_available_balance: AeCiphertext,
897 authority: &Pubkey,
898 multisig_signers: &[&Pubkey],
899 proof_data: &TransferData,
900) -> Result<Vec<Instruction>, ProgramError> {
901 Ok(vec![
902 inner_transfer(
903 token_program_id,
904 source_token_account,
905 destination_token_account,
906 mint,
907 new_source_decryptable_available_balance.into(),
908 authority,
909 multisig_signers,
910 1,
911 )?, // calls check_program_account
912 verify_transfer(proof_data),
913 ])
914}
915
916/// Create a `Transfer` instruction with fee proof
917#[allow(clippy::too_many_arguments)]
918#[cfg(not(target_os = "solana"))]
919pub fn transfer_with_fee(
920 token_program_id: &Pubkey,
921 source_token_account: &Pubkey,
922 destination_token_account: &Pubkey,
923 mint: &Pubkey,
924 new_source_decryptable_available_balance: AeCiphertext,
925 authority: &Pubkey,
926 multisig_signers: &[&Pubkey],
927 proof_data: &TransferWithFeeData,
928) -> Result<Vec<Instruction>, ProgramError> {
929 Ok(vec![
930 inner_transfer(
931 token_program_id,
932 source_token_account,
933 destination_token_account,
934 mint,
935 new_source_decryptable_available_balance.into(),
936 authority,
937 multisig_signers,
938 1,
939 )?, // calls check_program_account
940 verify_transfer_with_fee(proof_data),
941 ])
942}
943
944/// Create a inner `ApplyPendingBalance` instruction
945///
946/// This instruction is suitable for use with a cross-program `invoke`
947pub fn inner_apply_pending_balance(
948 token_program_id: &Pubkey,
949 token_account: &Pubkey,
950 expected_pending_balance_credit_counter: u64,
951 new_decryptable_available_balance: DecryptableBalance,
952 authority: &Pubkey,
953 multisig_signers: &[&Pubkey],
954) -> Result<Instruction, ProgramError> {
955 check_program_account(token_program_id)?;
956 let mut accounts = vec![
957 AccountMeta::new(*token_account, false),
958 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
959 ];
960
961 for multisig_signer in multisig_signers.iter() {
962 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
963 }
964
965 Ok(encode_instruction(
966 token_program_id,
967 accounts,
968 TokenInstruction::ConfidentialTransferExtension,
969 ConfidentialTransferInstruction::ApplyPendingBalance,
970 &ApplyPendingBalanceData {
971 expected_pending_balance_credit_counter: expected_pending_balance_credit_counter.into(),
972 new_decryptable_available_balance,
973 },
974 ))
975}
976
977/// Create a `ApplyPendingBalance` instruction
978#[cfg(not(target_os = "solana"))]
979pub fn apply_pending_balance(
980 token_program_id: &Pubkey,
981 token_account: &Pubkey,
982 pending_balance_instructions: u64,
983 new_decryptable_available_balance: AeCiphertext,
984 authority: &Pubkey,
985 multisig_signers: &[&Pubkey],
986) -> Result<Instruction, ProgramError> {
987 inner_apply_pending_balance(
988 token_program_id,
989 token_account,
990 pending_balance_instructions,
991 new_decryptable_available_balance.into(),
992 authority,
993 multisig_signers,
994 ) // calls check_program_account
995}
996
997fn enable_or_disable_balance_credits(
998 instruction: ConfidentialTransferInstruction,
999 token_program_id: &Pubkey,
1000 token_account: &Pubkey,
1001 authority: &Pubkey,
1002 multisig_signers: &[&Pubkey],
1003) -> Result<Instruction, ProgramError> {
1004 check_program_account(token_program_id)?;
1005 let mut accounts = vec![
1006 AccountMeta::new(*token_account, false),
1007 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
1008 ];
1009
1010 for multisig_signer in multisig_signers.iter() {
1011 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
1012 }
1013
1014 Ok(encode_instruction(
1015 token_program_id,
1016 accounts,
1017 TokenInstruction::ConfidentialTransferExtension,
1018 instruction,
1019 &(),
1020 ))
1021}
1022
1023/// Create a `EnableConfidentialCredits` instruction
1024pub fn enable_confidential_credits(
1025 token_program_id: &Pubkey,
1026 token_account: &Pubkey,
1027 authority: &Pubkey,
1028 multisig_signers: &[&Pubkey],
1029) -> Result<Instruction, ProgramError> {
1030 enable_or_disable_balance_credits(
1031 ConfidentialTransferInstruction::EnableConfidentialCredits,
1032 token_program_id,
1033 token_account,
1034 authority,
1035 multisig_signers,
1036 )
1037}
1038
1039/// Create a `DisableConfidentialCredits` instruction
1040pub fn disable_confidential_credits(
1041 token_program_id: &Pubkey,
1042 token_account: &Pubkey,
1043 authority: &Pubkey,
1044 multisig_signers: &[&Pubkey],
1045) -> Result<Instruction, ProgramError> {
1046 enable_or_disable_balance_credits(
1047 ConfidentialTransferInstruction::DisableConfidentialCredits,
1048 token_program_id,
1049 token_account,
1050 authority,
1051 multisig_signers,
1052 )
1053}
1054
1055/// Create a `EnableNonConfidentialCredits` instruction
1056pub fn enable_non_confidential_credits(
1057 token_program_id: &Pubkey,
1058 token_account: &Pubkey,
1059 authority: &Pubkey,
1060 multisig_signers: &[&Pubkey],
1061) -> Result<Instruction, ProgramError> {
1062 enable_or_disable_balance_credits(
1063 ConfidentialTransferInstruction::EnableNonConfidentialCredits,
1064 token_program_id,
1065 token_account,
1066 authority,
1067 multisig_signers,
1068 )
1069}
1070
1071/// Create a `DisableNonConfidentialCredits` instruction
1072pub fn disable_non_confidential_credits(
1073 token_program_id: &Pubkey,
1074 token_account: &Pubkey,
1075 authority: &Pubkey,
1076 multisig_signers: &[&Pubkey],
1077) -> Result<Instruction, ProgramError> {
1078 enable_or_disable_balance_credits(
1079 ConfidentialTransferInstruction::DisableNonConfidentialCredits,
1080 token_program_id,
1081 token_account,
1082 authority,
1083 multisig_signers,
1084 )
1085}
1086
1087/// Create a inner `WithdrawWithheldTokensFromMint` instruction
1088///
1089/// This instruction is suitable for use with a cross-program `invoke`
1090pub fn inner_withdraw_withheld_tokens_from_mint(
1091 token_program_id: &Pubkey,
1092 mint: &Pubkey,
1093 destination: &Pubkey,
1094 authority: &Pubkey,
1095 multisig_signers: &[&Pubkey],
1096 proof_instruction_offset: i8,
1097) -> Result<Instruction, ProgramError> {
1098 check_program_account(token_program_id)?;
1099 let mut accounts = vec![
1100 AccountMeta::new(*mint, false),
1101 AccountMeta::new(*destination, false),
1102 AccountMeta::new_readonly(sysvar::instructions::id(), false),
1103 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
1104 ];
1105
1106 for multisig_signer in multisig_signers.iter() {
1107 accounts.push(AccountMeta::new(**multisig_signer, false));
1108 }
1109
1110 Ok(encode_instruction(
1111 token_program_id,
1112 accounts,
1113 TokenInstruction::ConfidentialTransferExtension,
1114 ConfidentialTransferInstruction::WithdrawWithheldTokensFromMint,
1115 &WithdrawWithheldTokensFromMintData {
1116 proof_instruction_offset,
1117 },
1118 ))
1119}
1120
1121/// Create a `WithdrawWithheldTokensFromMint` instruction
1122pub fn withdraw_withheld_tokens_from_mint(
1123 token_program_id: &Pubkey,
1124 mint: &Pubkey,
1125 destination: &Pubkey,
1126 authority: &Pubkey,
1127 multisig_signers: &[&Pubkey],
1128 proof_data: &WithdrawWithheldTokensData,
1129) -> Result<Vec<Instruction>, ProgramError> {
1130 Ok(vec![
1131 inner_withdraw_withheld_tokens_from_mint(
1132 token_program_id,
1133 mint,
1134 destination,
1135 authority,
1136 multisig_signers,
1137 1,
1138 )?,
1139 verify_withdraw_withheld_tokens(proof_data),
1140 ])
1141}
1142
1143/// Create a inner `WithdrawWithheldTokensFromMint` instruction
1144///
1145/// This instruction is suitable for use with a cross-program `invoke`
1146pub fn inner_withdraw_withheld_tokens_from_accounts(
1147 token_program_id: &Pubkey,
1148 mint: &Pubkey,
1149 destination: &Pubkey,
1150 authority: &Pubkey,
1151 multisig_signers: &[&Pubkey],
1152 sources: &[&Pubkey],
1153 proof_instruction_offset: i8,
1154) -> Result<Instruction, ProgramError> {
1155 check_program_account(token_program_id)?;
1156 let num_token_accounts =
1157 u8::try_from(sources.len()).map_err(|_| ProgramError::InvalidInstructionData)?;
1158 let mut accounts = vec![
1159 AccountMeta::new(*mint, false),
1160 AccountMeta::new(*destination, false),
1161 AccountMeta::new_readonly(sysvar::instructions::id(), false),
1162 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
1163 ];
1164
1165 for multisig_signer in multisig_signers.iter() {
1166 accounts.push(AccountMeta::new(**multisig_signer, false));
1167 }
1168
1169 for source in sources.iter() {
1170 accounts.push(AccountMeta::new(**source, false));
1171 }
1172
1173 Ok(encode_instruction(
1174 token_program_id,
1175 accounts,
1176 TokenInstruction::ConfidentialTransferExtension,
1177 ConfidentialTransferInstruction::WithdrawWithheldTokensFromAccounts,
1178 &WithdrawWithheldTokensFromAccountsData {
1179 proof_instruction_offset,
1180 num_token_accounts,
1181 },
1182 ))
1183}
1184
1185/// Create a `WithdrawWithheldTokensFromAccounts` instruction
1186pub fn withdraw_withheld_tokens_from_accounts(
1187 token_program_id: &Pubkey,
1188 mint: &Pubkey,
1189 destination: &Pubkey,
1190 authority: &Pubkey,
1191 multisig_signers: &[&Pubkey],
1192 sources: &[&Pubkey],
1193 proof_data: &WithdrawWithheldTokensData,
1194) -> Result<Vec<Instruction>, ProgramError> {
1195 Ok(vec![
1196 inner_withdraw_withheld_tokens_from_accounts(
1197 token_program_id,
1198 mint,
1199 destination,
1200 authority,
1201 multisig_signers,
1202 sources,
1203 1,
1204 )?,
1205 verify_withdraw_withheld_tokens(proof_data),
1206 ])
1207}
1208
1209/// Creates a `HarvestWithheldTokensToMint` instruction
1210pub fn harvest_withheld_tokens_to_mint(
1211 token_program_id: &Pubkey,
1212 mint: &Pubkey,
1213 sources: &[&Pubkey],
1214) -> Result<Instruction, ProgramError> {
1215 check_program_account(token_program_id)?;
1216 let mut accounts = vec![AccountMeta::new(*mint, false)];
1217
1218 for source in sources.iter() {
1219 accounts.push(AccountMeta::new(**source, false));
1220 }
1221
1222 Ok(encode_instruction(
1223 token_program_id,
1224 accounts,
1225 TokenInstruction::ConfidentialTransferExtension,
1226 ConfidentialTransferInstruction::HarvestWithheldTokensToMint,
1227 &(),
1228 ))
1229}