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}