@@ -3,8 +3,7 @@ use solana_zk_token_sdk::encryption::{auth_encryption::AeCiphertext, elgamal::El
3
3
pub use solana_zk_token_sdk:: zk_token_proof_instruction:: * ;
4
4
use {
5
5
crate :: {
6
- check_program_account, extension:: confidential_transfer:: ConfidentialTransferMint ,
7
- instruction:: TokenInstruction , pod:: * ,
6
+ check_program_account, extension:: confidential_transfer:: * , instruction:: TokenInstruction ,
8
7
} ,
9
8
bytemuck:: { Pod , Zeroable } ,
10
9
num_derive:: { FromPrimitive , ToPrimitive } ,
16
15
sysvar,
17
16
} ,
18
17
solana_zk_token_sdk:: zk_token_elgamal:: pod,
18
+ std:: convert:: TryFrom ,
19
19
} ;
20
20
21
21
/// Confidential Transfer extension instructions
@@ -228,16 +228,89 @@ pub enum ConfidentialTransferInstruction {
228
228
/// None
229
229
///
230
230
DisableBalanceCredits ,
231
+
232
+ /// Transfer all withheld confidential tokens in the mint to an account. Signed by the mint's
233
+ /// withdraw withheld tokens authority.
234
+ ///
235
+ /// Accounts expected by this instruction:
236
+ ///
237
+ /// 0. `[writable]` The token mint. Must include the `TransferFeeConfig` extension.
238
+ /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` and
239
+ /// `ConfidentialTransferAccount` extensions.
240
+ /// 2. `[]` Instructions sysvar
241
+ /// 3. `[signer]` The mint's `withdraw_withheld_authority`.
242
+ /// or:
243
+ /// 3. `[]` The mint's `withdraw_withheld_authority`'s multisignature owner/delegate.
244
+ /// 4. ..3+M `[signer]` M signer accounts.
245
+ ///
246
+ /// Data expected by this instruction:
247
+ /// WithdrawWithheldTokensFromMintData
248
+ ///
249
+ WithdrawWithheldTokensFromMint ,
250
+
251
+ /// Transfer all withheld tokens to an account. Signed by the mint's withdraw withheld tokens
252
+ /// authority. This instruction is susceptible to front-running. Use
253
+ /// `HarvestWithheldTokensToMint` and `WithdrawWithheldTokensFromMint` as an alternative.
254
+ ///
255
+ /// Note on front-running: This instruction requires a zero-knowledge proof verification
256
+ /// instruction that is checked with respect to the account state (the currently withheld
257
+ /// fees). Suppose that a withdraw withheld authority generates the
258
+ /// `WithdrawWithheldTokensFromAccounts` instruction along with a corresponding zero-knowledge
259
+ /// proof for a specified set of accounts, and submits it on chain. If the withheld fees at any
260
+ /// of the specified accounts change before the `WithdrawWithheldTokensFromAccounts` is
261
+ /// executed on chain, the zero-knowledge proof will not verify with respect to the new state,
262
+ /// forcing the transaction to fail.
263
+ ///
264
+ /// If front-running occurs, then users can look up the updated states of the accounts,
265
+ /// generate a new zero-knowledge proof and try again. Alternatively, withdraw withheld
266
+ /// authority can first move the withheld amount to the mint using
267
+ /// `HarvestWithheldTokensToMint` and then move the withheld fees from mint to a specified
268
+ /// destination account using `WithdrawWithheldTokensFromMint`.
269
+ ///
270
+ /// Accounts expected by this instruction:
271
+ ///
272
+ /// 0. `[]` The token mint. Must include the `TransferFeeConfig` extension.
273
+ /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` and
274
+ /// `ConfidentialTransferAccount` extensions.
275
+ /// 2. `[]` Instructions sysvar
276
+ /// 3. `[signer]` The mint's `withdraw_withheld_authority`.
277
+ /// 4. ..3+N `[writable]` The source accounts to withdraw from.
278
+ /// or:
279
+ /// 3. `[]` The mint's `withdraw_withheld_authority`'s multisignature owner/delegate.
280
+ /// 4. ..4+M `[signer]` M signer accounts.
281
+ /// 4+M+1. ..3+M+N `[writable]` The source accounts to withdraw from.
282
+ ///
283
+ /// Data expected by this instruction:
284
+ /// WithdrawWithheldTokensFromAccountsData
285
+ ///
286
+ WithdrawWithheldTokensFromAccounts ,
287
+
288
+ /// Permissionless instruction to transfer all withheld confidential tokens to the mint.
289
+ ///
290
+ /// Succeeds for frozen accounts.
291
+ ///
292
+ /// Accounts provided should include both the `TransferFeeAmount` and
293
+ /// `ConfidentialTransferAccount` extension. If not, the account is skipped.
294
+ ///
295
+ /// Accounts expected by this instruction:
296
+ ///
297
+ /// 0. `[writable]` The mint.
298
+ /// 1. ..1+N `[writable]` The source accounts to harvest from.
299
+ ///
300
+ /// Data expected by this instruction:
301
+ /// None
302
+ ///
303
+ HarvestWithheldTokensToMint ,
231
304
}
232
305
233
306
/// Data expected by `ConfidentialTransferInstruction::ConfigureAccount`
234
307
#[ derive( Clone , Copy , Pod , Zeroable ) ]
235
308
#[ repr( C ) ]
236
309
pub struct ConfigureAccountInstructionData {
237
310
/// The public key associated with the account
238
- pub elgamal_pubkey : pod :: ElGamalPubkey ,
311
+ pub elgamal_pubkey : EncryptionPubkey ,
239
312
/// The decryptable balance (always 0) once the configure account succeeds
240
- pub decryptable_zero_balance : pod :: AeCiphertext ,
313
+ pub decryptable_zero_balance : DecryptableBalance ,
241
314
}
242
315
243
316
/// Data expected by `ConfidentialTransferInstruction::EmptyAccount`
@@ -268,7 +341,7 @@ pub struct WithdrawInstructionData {
268
341
/// Expected number of base 10 digits to the right of the decimal place
269
342
pub decimals : u8 ,
270
343
/// The new decryptable balance if the withrawal succeeds
271
- pub new_decryptable_available_balance : pod :: AeCiphertext ,
344
+ pub new_decryptable_available_balance : DecryptableBalance ,
272
345
/// Relative location of the `ProofInstruction::VerifyWithdraw` instruction to the `Withdraw`
273
346
/// instruction in the transaction
274
347
pub proof_instruction_offset : i8 ,
@@ -279,7 +352,7 @@ pub struct WithdrawInstructionData {
279
352
#[ repr( C ) ]
280
353
pub struct TransferInstructionData {
281
354
/// The new source decryptable balance if the transfer succeeds
282
- pub new_source_decryptable_available_balance : pod :: AeCiphertext ,
355
+ pub new_source_decryptable_available_balance : DecryptableBalance ,
283
356
/// Relative location of the `ProofInstruction::VerifyTransfer` instruction to the
284
357
/// `Transfer` instruction in the transaction
285
358
pub proof_instruction_offset : i8 ,
@@ -296,6 +369,26 @@ pub struct ApplyPendingBalanceData {
296
369
pub new_decryptable_available_balance : pod:: AeCiphertext ,
297
370
}
298
371
372
+ /// Data expected by `ConfidentialTransferInstruction::WithdrawWithheldTokensFromMint`
373
+ #[ derive( Clone , Copy , Pod , Zeroable ) ]
374
+ #[ repr( C ) ]
375
+ pub struct WithdrawWithheldTokensFromMintData {
376
+ /// Relative location of the `ProofInstruction::VerifyWithdrawWithheld` instruction to the
377
+ /// `WithdrawWithheldTokensFromMint` instruction in the transaction
378
+ pub proof_instruction_offset : i8 ,
379
+ }
380
+
381
+ /// Data expected by `ConfidentialTransferInstruction::WithdrawWithheldTokensFromAccounts`
382
+ #[ derive( Clone , Copy , Pod , Zeroable ) ]
383
+ #[ repr( C ) ]
384
+ pub struct WithdrawWithheldTokensFromAccountsData {
385
+ /// Number of token accounts harvested
386
+ pub num_token_accounts : u8 ,
387
+ /// Relative location of the `ProofInstruction::VerifyWithdrawWithheld` instruction to the
388
+ /// `VerifyWithdrawWithheldTokensFromAccounts` instruction in the transaction
389
+ pub proof_instruction_offset : i8 ,
390
+ }
391
+
299
392
pub ( crate ) fn decode_instruction_type (
300
393
input : & [ u8 ] ,
301
394
) -> Result < ConfidentialTransferInstruction , ProgramError > {
@@ -521,7 +614,7 @@ pub fn inner_withdraw(
521
614
mint : & Pubkey ,
522
615
amount : u64 ,
523
616
decimals : u8 ,
524
- new_decryptable_available_balance : pod :: AeCiphertext ,
617
+ new_decryptable_available_balance : DecryptableBalance ,
525
618
authority : & Pubkey ,
526
619
multisig_signers : & [ & Pubkey ] ,
527
620
proof_instruction_offset : i8 ,
@@ -593,7 +686,7 @@ pub fn inner_transfer(
593
686
source_token_account : & Pubkey ,
594
687
destination_token_account : & Pubkey ,
595
688
mint : & Pubkey ,
596
- new_source_decryptable_available_balance : pod :: AeCiphertext ,
689
+ new_source_decryptable_available_balance : DecryptableBalance ,
597
690
authority : & Pubkey ,
598
691
multisig_signers : & [ & Pubkey ] ,
599
692
proof_instruction_offset : i8 ,
@@ -657,7 +750,7 @@ pub fn inner_apply_pending_balance(
657
750
token_program_id : & Pubkey ,
658
751
token_account : & Pubkey ,
659
752
expected_pending_balance_credit_counter : u64 ,
660
- new_decryptable_available_balance : pod :: AeCiphertext ,
753
+ new_decryptable_available_balance : DecryptableBalance ,
661
754
authority : & Pubkey ,
662
755
multisig_signers : & [ & Pubkey ] ,
663
756
) -> Result < Instruction , ProgramError > {
@@ -760,3 +853,144 @@ pub fn disable_balance_credits(
760
853
multisig_signers,
761
854
)
762
855
}
856
+
857
+ /// Create a inner `WithdrawWithheldTokensFromMint` instruction
858
+ ///
859
+ /// This instruction is suitable for use with a cross-program `invoke`
860
+ pub fn inner_withdraw_withheld_tokens_from_mint (
861
+ token_program_id : & Pubkey ,
862
+ mint : & Pubkey ,
863
+ destination : & Pubkey ,
864
+ authority : & Pubkey ,
865
+ multisig_signers : & [ & Pubkey ] ,
866
+ proof_instruction_offset : i8 ,
867
+ ) -> Result < Instruction , ProgramError > {
868
+ check_program_account ( token_program_id) ?;
869
+ let mut accounts = vec ! [
870
+ AccountMeta :: new( * mint, false ) ,
871
+ AccountMeta :: new( * destination, false ) ,
872
+ AccountMeta :: new_readonly( sysvar:: instructions:: id( ) , false ) ,
873
+ AccountMeta :: new_readonly( * authority, multisig_signers. is_empty( ) ) ,
874
+ ] ;
875
+
876
+ for multisig_signer in multisig_signers. iter ( ) {
877
+ accounts. push ( AccountMeta :: new ( * * multisig_signer, false ) ) ;
878
+ }
879
+
880
+ Ok ( encode_instruction (
881
+ token_program_id,
882
+ accounts,
883
+ ConfidentialTransferInstruction :: WithdrawWithheldTokensFromMint ,
884
+ & WithdrawWithheldTokensFromMintData {
885
+ proof_instruction_offset,
886
+ } ,
887
+ ) )
888
+ }
889
+
890
+ /// Create a `WithdrawWithheldTokensFromMint` instruction
891
+ pub fn withdraw_withheld_tokens_from_mint (
892
+ token_program_id : & Pubkey ,
893
+ mint : & Pubkey ,
894
+ destination : & Pubkey ,
895
+ authority : & Pubkey ,
896
+ multisig_signers : & [ & Pubkey ] ,
897
+ proof_data : & WithdrawWithheldTokensData ,
898
+ ) -> Result < Vec < Instruction > , ProgramError > {
899
+ Ok ( vec ! [
900
+ verify_withdraw_withheld_tokens( proof_data) ,
901
+ inner_withdraw_withheld_tokens_from_mint(
902
+ token_program_id,
903
+ mint,
904
+ destination,
905
+ authority,
906
+ multisig_signers,
907
+ -1 ,
908
+ ) ?,
909
+ ] )
910
+ }
911
+
912
+ /// Create a inner `WithdrawWithheldTokensFromMint` instruction
913
+ ///
914
+ /// This instruction is suitable for use with a cross-program `invoke`
915
+ pub fn inner_withdraw_withheld_tokens_from_accounts (
916
+ token_program_id : & Pubkey ,
917
+ mint : & Pubkey ,
918
+ destination : & Pubkey ,
919
+ authority : & Pubkey ,
920
+ multisig_signers : & [ & Pubkey ] ,
921
+ sources : & [ & Pubkey ] ,
922
+ proof_instruction_offset : i8 ,
923
+ ) -> Result < Instruction , ProgramError > {
924
+ check_program_account ( token_program_id) ?;
925
+ let num_token_accounts =
926
+ u8:: try_from ( sources. len ( ) ) . map_err ( |_| ProgramError :: InvalidInstructionData ) ?;
927
+ let mut accounts = vec ! [
928
+ AccountMeta :: new( * mint, false ) ,
929
+ AccountMeta :: new( * destination, false ) ,
930
+ AccountMeta :: new_readonly( sysvar:: instructions:: id( ) , false ) ,
931
+ AccountMeta :: new_readonly( * authority, multisig_signers. is_empty( ) ) ,
932
+ ] ;
933
+
934
+ for multisig_signer in multisig_signers. iter ( ) {
935
+ accounts. push ( AccountMeta :: new ( * * multisig_signer, false ) ) ;
936
+ }
937
+
938
+ for source in sources. iter ( ) {
939
+ accounts. push ( AccountMeta :: new ( * * source, false ) ) ;
940
+ }
941
+
942
+ Ok ( encode_instruction (
943
+ token_program_id,
944
+ accounts,
945
+ ConfidentialTransferInstruction :: WithdrawWithheldTokensFromAccounts ,
946
+ & WithdrawWithheldTokensFromAccountsData {
947
+ proof_instruction_offset,
948
+ num_token_accounts,
949
+ } ,
950
+ ) )
951
+ }
952
+
953
+ /// Create a `WithdrawWithheldTokensFromAccounts` instruction
954
+ pub fn withdraw_withheld_tokens_from_accounts (
955
+ token_program_id : & Pubkey ,
956
+ mint : & Pubkey ,
957
+ destination : & Pubkey ,
958
+ authority : & Pubkey ,
959
+ multisig_signers : & [ & Pubkey ] ,
960
+ sources : & [ & Pubkey ] ,
961
+ proof_data : & WithdrawWithheldTokensData ,
962
+ ) -> Result < Vec < Instruction > , ProgramError > {
963
+ Ok ( vec ! [
964
+ verify_withdraw_withheld_tokens( proof_data) ,
965
+ inner_withdraw_withheld_tokens_from_accounts(
966
+ token_program_id,
967
+ mint,
968
+ destination,
969
+ authority,
970
+ multisig_signers,
971
+ sources,
972
+ -1 ,
973
+ ) ?,
974
+ ] )
975
+ }
976
+
977
+ /// Creates a `HarvestWithheldTokensToMint` instruction
978
+ pub fn harvest_withheld_tokens_to_mint (
979
+ token_program_id : & Pubkey ,
980
+ mint : & Pubkey ,
981
+ sources : & [ & Pubkey ] ,
982
+ ) -> Result < Instruction , ProgramError > {
983
+ check_program_account ( token_program_id) ?;
984
+ let mut accounts = vec ! [ AccountMeta :: new( * mint, false ) ] ;
985
+
986
+ for source in sources. iter ( ) {
987
+ accounts. push ( AccountMeta :: new ( * * source, false ) ) ;
988
+ }
989
+
990
+ Ok ( encode_instruction (
991
+ token_program_id,
992
+ accounts,
993
+ ConfidentialTransferInstruction :: HarvestWithheldTokensToMint ,
994
+ & ( ) ,
995
+ ) )
996
+ }
0 commit comments