spl_token_cli/
clap_app.rs

1#![allow(deprecated)]
2
3use {
4    clap::{
5        crate_description, crate_name, crate_version, App, AppSettings, Arg, ArgGroup, SubCommand,
6    },
7    solana_clap_v3_utils::{
8        fee_payer::fee_payer_arg,
9        input_parsers::Amount,
10        input_validators::{is_pubkey, is_url_or_moniker, is_valid_pubkey, is_valid_signer},
11        memo::memo_arg,
12        nonce::*,
13        offline::{self, *},
14        ArgConstant,
15    },
16    solana_sdk::{instruction::AccountMeta, pubkey::Pubkey},
17    spl_token_2022_interface::instruction::{AuthorityType, MAX_SIGNERS, MIN_SIGNERS},
18    std::{fmt, str::FromStr},
19    strum::IntoEnumIterator,
20    strum_macros::{AsRefStr, EnumIter, EnumString, IntoStaticStr},
21};
22
23pub type Error = Box<dyn std::error::Error + Send + Sync>;
24
25pub const OWNER_ADDRESS_ARG: ArgConstant<'static> = ArgConstant {
26    name: "owner",
27    long: "owner",
28    help: "Address of the primary authority controlling a mint or account. Defaults to the client keypair address.",
29};
30
31pub const OWNER_KEYPAIR_ARG: ArgConstant<'static> = ArgConstant {
32    name: "owner",
33    long: "owner",
34    help: "Keypair of the primary authority controlling a mint or account. Defaults to the client keypair.",
35};
36
37pub const MINT_ADDRESS_ARG: ArgConstant<'static> = ArgConstant {
38    name: "mint_address",
39    long: "mint-address",
40    help: "Address of mint that token account is associated with. Required by --sign-only",
41};
42
43pub const MINT_DECIMALS_ARG: ArgConstant<'static> = ArgConstant {
44    name: "mint_decimals",
45    long: "mint-decimals",
46    help: "Decimals of mint that token account is associated with. Required by --sign-only",
47};
48
49pub const DELEGATE_ADDRESS_ARG: ArgConstant<'static> = ArgConstant {
50    name: "delegate_address",
51    long: "delegate-address",
52    help: "Address of delegate currently assigned to token account. Required by --sign-only",
53};
54
55pub const TRANSFER_LAMPORTS_ARG: ArgConstant<'static> = ArgConstant {
56    name: "transfer_lamports",
57    long: "transfer-lamports",
58    help: "Additional lamports to transfer to make account rent-exempt after reallocation. Required by --sign-only",
59};
60
61pub const MULTISIG_SIGNER_ARG: ArgConstant<'static> = ArgConstant {
62    name: "multisig_signer",
63    long: "multisig-signer",
64    help: "Member signer of a multisig account",
65};
66
67pub const COMPUTE_UNIT_PRICE_ARG: ArgConstant<'static> = ArgConstant {
68    name: "compute_unit_price",
69    long: "--with-compute-unit-price",
70    help: "Set compute unit price for transaction, in increments of 0.000001 lamports per compute unit.",
71};
72
73pub const COMPUTE_UNIT_LIMIT_ARG: ArgConstant<'static> = ArgConstant {
74    name: "compute_unit_limit",
75    long: "--with-compute-unit-limit",
76    help: "Set compute unit limit for transaction, in compute units.",
77};
78
79// The `signer_arg` in clap-v3-utils` specifies the argument as a
80// `PubkeySignature` type, but supporting `PubkeySignature` in the token-cli
81// requires a significant re-structuring of the code. Therefore, hard-code the
82// `signer_arg` and `OfflineArgs` from clap-utils` here and remove
83// it in a subsequent PR.
84fn signer_arg<'a>() -> Arg<'a> {
85    Arg::new(SIGNER_ARG.name)
86        .long(SIGNER_ARG.long)
87        .takes_value(true)
88        .value_name("PUBKEY=SIGNATURE")
89        .requires(BLOCKHASH_ARG.name)
90        .action(clap::ArgAction::Append)
91        .multiple_values(false)
92        .help(SIGNER_ARG.help)
93}
94
95pub trait OfflineArgs {
96    fn offline_args(self) -> Self;
97    fn offline_args_config(self, config: &dyn ArgsConfig) -> Self;
98}
99
100impl OfflineArgs for clap::Command<'_> {
101    fn offline_args_config(self, config: &dyn ArgsConfig) -> Self {
102        self.arg(config.blockhash_arg(blockhash_arg()))
103            .arg(config.sign_only_arg(sign_only_arg()))
104            .arg(config.signer_arg(signer_arg()))
105            .arg(config.dump_transaction_message_arg(dump_transaction_message()))
106    }
107    fn offline_args(self) -> Self {
108        struct NullArgsConfig {}
109        impl ArgsConfig for NullArgsConfig {}
110        self.offline_args_config(&NullArgsConfig {})
111    }
112}
113
114pub static VALID_TOKEN_PROGRAM_IDS: [Pubkey; 2] =
115    [spl_token_2022_interface::ID, spl_token_interface::ID];
116
117#[derive(AsRefStr, Debug, Clone, Copy, PartialEq, EnumString, IntoStaticStr)]
118#[strum(serialize_all = "kebab-case")]
119pub enum CommandName {
120    CreateToken,
121    Close,
122    CloseMint,
123    Bench,
124    CreateAccount,
125    CreateMultisig,
126    Authorize,
127    SetInterestRate,
128    Transfer,
129    Burn,
130    Mint,
131    Freeze,
132    Thaw,
133    Wrap,
134    Unwrap,
135    Approve,
136    Revoke,
137    Balance,
138    Supply,
139    Accounts,
140    Address,
141    AccountInfo,
142    MultisigInfo,
143    Display,
144    Gc,
145    SyncNative,
146    EnableRequiredTransferMemos,
147    DisableRequiredTransferMemos,
148    EnableCpiGuard,
149    DisableCpiGuard,
150    UpdateDefaultAccountState,
151    UpdateMetadataAddress,
152    WithdrawWithheldTokens,
153    SetTransferFee,
154    WithdrawExcessLamports,
155    SetTransferHook,
156    InitializeMetadata,
157    UpdateMetadata,
158    InitializeGroup,
159    UpdateGroupMaxSize,
160    InitializeMember,
161    UpdateConfidentialTransferSettings,
162    ConfigureConfidentialTransferAccount,
163    EnableConfidentialCredits,
164    DisableConfidentialCredits,
165    EnableNonConfidentialCredits,
166    DisableNonConfidentialCredits,
167    DepositConfidentialTokens,
168    WithdrawConfidentialTokens,
169    ApplyPendingBalance,
170    UpdateGroupAddress,
171    UpdateMemberAddress,
172    UpdateUiAmountMultiplier,
173    Pause,
174    Resume,
175}
176impl fmt::Display for CommandName {
177    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178        write!(f, "{:?}", self)
179    }
180}
181#[derive(Debug, Clone, Copy, PartialEq, EnumString, IntoStaticStr)]
182#[strum(serialize_all = "kebab-case")]
183pub enum AccountMetaRole {
184    Readonly,
185    Writable,
186    ReadonlySigner,
187    WritableSigner,
188}
189impl fmt::Display for AccountMetaRole {
190    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191        write!(f, "{:?}", self)
192    }
193}
194pub fn parse_transfer_hook_account<T>(string: T) -> Result<AccountMeta, String>
195where
196    T: AsRef<str> + fmt::Display,
197{
198    match string.as_ref().split(':').collect::<Vec<_>>().as_slice() {
199        [address, role] => {
200            let address = Pubkey::from_str(address).map_err(|e| format!("{e}"))?;
201            let meta = match AccountMetaRole::from_str(role).map_err(|e| format!("{e}"))? {
202                AccountMetaRole::Readonly => AccountMeta::new_readonly(address, false),
203                AccountMetaRole::Writable => AccountMeta::new(address, false),
204                AccountMetaRole::ReadonlySigner => AccountMeta::new_readonly(address, true),
205                AccountMetaRole::WritableSigner => AccountMeta::new(address, true),
206            };
207            Ok(meta)
208        }
209        _ => Err("Transfer hook account must be present as <ADDRESS>:<ROLE>".to_string()),
210    }
211}
212fn validate_transfer_hook_account<T>(string: T) -> Result<(), String>
213where
214    T: AsRef<str> + fmt::Display,
215{
216    match string.as_ref().split(':').collect::<Vec<_>>().as_slice() {
217        [address, role] => {
218            is_valid_pubkey(address)?;
219            AccountMetaRole::from_str(role)
220                .map(|_| ())
221                .map_err(|e| format!("{e}"))
222        }
223        _ => Err("Transfer hook account must be present as <ADDRESS>:<ROLE>".to_string()),
224    }
225}
226#[derive(Debug, Clone, PartialEq, EnumIter, EnumString, IntoStaticStr)]
227#[strum(serialize_all = "kebab-case")]
228pub enum CliAuthorityType {
229    Mint,
230    Freeze,
231    Owner,
232    Close,
233    CloseMint,
234    TransferFeeConfig,
235    WithheldWithdraw,
236    InterestRate,
237    PermanentDelegate,
238    ConfidentialTransferMint,
239    TransferHookProgramId,
240    ConfidentialTransferFee,
241    MetadataPointer,
242    Metadata,
243    GroupPointer,
244    GroupMemberPointer,
245    Group,
246    ScaledUiAmount,
247    Pause,
248}
249impl TryFrom<CliAuthorityType> for AuthorityType {
250    type Error = Error;
251    fn try_from(authority_type: CliAuthorityType) -> Result<Self, Error> {
252        match authority_type {
253            CliAuthorityType::Mint => Ok(AuthorityType::MintTokens),
254            CliAuthorityType::Freeze => Ok(AuthorityType::FreezeAccount),
255            CliAuthorityType::Owner => Ok(AuthorityType::AccountOwner),
256            CliAuthorityType::Close => Ok(AuthorityType::CloseAccount),
257            CliAuthorityType::CloseMint => Ok(AuthorityType::CloseMint),
258            CliAuthorityType::TransferFeeConfig => Ok(AuthorityType::TransferFeeConfig),
259            CliAuthorityType::WithheldWithdraw => Ok(AuthorityType::WithheldWithdraw),
260            CliAuthorityType::InterestRate => Ok(AuthorityType::InterestRate),
261            CliAuthorityType::PermanentDelegate => Ok(AuthorityType::PermanentDelegate),
262            CliAuthorityType::ConfidentialTransferMint => {
263                Ok(AuthorityType::ConfidentialTransferMint)
264            }
265            CliAuthorityType::TransferHookProgramId => Ok(AuthorityType::TransferHookProgramId),
266            CliAuthorityType::ConfidentialTransferFee => {
267                Ok(AuthorityType::ConfidentialTransferFeeConfig)
268            }
269            CliAuthorityType::MetadataPointer => Ok(AuthorityType::MetadataPointer),
270            CliAuthorityType::Metadata => {
271                Err("Metadata authority does not map to a token authority type".into())
272            }
273            CliAuthorityType::GroupPointer => Ok(AuthorityType::GroupPointer),
274            CliAuthorityType::GroupMemberPointer => Ok(AuthorityType::GroupMemberPointer),
275            CliAuthorityType::Group => {
276                Err("Group update authority does not map to a token authority type".into())
277            }
278            CliAuthorityType::ScaledUiAmount => Ok(AuthorityType::ScaledUiAmount),
279            CliAuthorityType::Pause => Ok(AuthorityType::Pause),
280        }
281    }
282}
283
284pub fn owner_address_arg<'a>() -> Arg<'a> {
285    Arg::with_name(OWNER_ADDRESS_ARG.name)
286        .long(OWNER_ADDRESS_ARG.long)
287        .takes_value(true)
288        .value_name("OWNER_ADDRESS")
289        .validator(|s| is_valid_pubkey(s))
290        .help(OWNER_ADDRESS_ARG.help)
291}
292
293pub fn owner_keypair_arg_with_value_name<'a>(value_name: &'static str) -> Arg<'a> {
294    Arg::with_name(OWNER_KEYPAIR_ARG.name)
295        .long(OWNER_KEYPAIR_ARG.long)
296        .takes_value(true)
297        .value_name(value_name)
298        .validator(|s| is_valid_signer(s))
299        .help(OWNER_KEYPAIR_ARG.help)
300}
301
302pub fn owner_keypair_arg<'a>() -> Arg<'a> {
303    owner_keypair_arg_with_value_name("OWNER_KEYPAIR")
304}
305
306pub fn mint_address_arg<'a>() -> Arg<'a> {
307    Arg::with_name(MINT_ADDRESS_ARG.name)
308        .long(MINT_ADDRESS_ARG.long)
309        .takes_value(true)
310        .value_name("MINT_ADDRESS")
311        .validator(|s| is_valid_pubkey(s))
312        .help(MINT_ADDRESS_ARG.help)
313}
314
315pub fn mint_decimals_arg<'a>() -> Arg<'a> {
316    Arg::with_name(MINT_DECIMALS_ARG.name)
317        .long(MINT_DECIMALS_ARG.long)
318        .takes_value(true)
319        .value_name("MINT_DECIMALS")
320        .value_parser(clap::value_parser!(u8))
321        .help(MINT_DECIMALS_ARG.help)
322}
323
324pub trait MintArgs {
325    fn mint_args(self) -> Self;
326}
327
328impl MintArgs for App<'_> {
329    fn mint_args(self) -> Self {
330        self.arg(mint_address_arg().requires(MINT_DECIMALS_ARG.name))
331            .arg(mint_decimals_arg().requires(MINT_ADDRESS_ARG.name))
332    }
333}
334
335pub fn delegate_address_arg<'a>() -> Arg<'a> {
336    Arg::with_name(DELEGATE_ADDRESS_ARG.name)
337        .long(DELEGATE_ADDRESS_ARG.long)
338        .takes_value(true)
339        .value_name("DELEGATE_ADDRESS")
340        .validator(|s| is_valid_pubkey(s))
341        .help(DELEGATE_ADDRESS_ARG.help)
342}
343
344pub fn transfer_lamports_arg<'a>() -> Arg<'a> {
345    Arg::with_name(TRANSFER_LAMPORTS_ARG.name)
346        .long(TRANSFER_LAMPORTS_ARG.long)
347        .takes_value(true)
348        .value_name("LAMPORTS")
349        .value_parser(clap::value_parser!(u64))
350        .help(TRANSFER_LAMPORTS_ARG.help)
351}
352
353pub fn multisig_signer_arg<'a>() -> Arg<'a> {
354    Arg::with_name(MULTISIG_SIGNER_ARG.name)
355        .long(MULTISIG_SIGNER_ARG.long)
356        .validator(|s| is_valid_signer(s))
357        .value_name("MULTISIG_SIGNER")
358        .takes_value(true)
359        .multiple(true)
360        .min_values(0_usize)
361        .max_values(MAX_SIGNERS)
362        .help(MULTISIG_SIGNER_ARG.help)
363}
364
365fn is_multisig_minimum_signers(string: &str) -> Result<(), String> {
366    let v = u8::from_str(string).map_err(|e| e.to_string())? as usize;
367    if v < MIN_SIGNERS {
368        Err(format!("must be at least {}", MIN_SIGNERS))
369    } else if v > MAX_SIGNERS {
370        Err(format!("must be at most {}", MAX_SIGNERS))
371    } else {
372        Ok(())
373    }
374}
375
376fn is_valid_token_program_id<T>(string: T) -> Result<(), String>
377where
378    T: AsRef<str> + fmt::Display,
379{
380    match is_pubkey(string.as_ref()) {
381        Ok(()) => {
382            let program_id = string.as_ref().parse::<Pubkey>().unwrap();
383            if VALID_TOKEN_PROGRAM_IDS.contains(&program_id) {
384                Ok(())
385            } else {
386                Err(format!("Unrecognized token program id: {}", program_id))
387            }
388        }
389        Err(e) => Err(e),
390    }
391}
392
393struct SignOnlyNeedsFullMintSpec {}
394impl offline::ArgsConfig for SignOnlyNeedsFullMintSpec {
395    fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a>) -> Arg<'a> {
396        arg.requires_all(&[MINT_ADDRESS_ARG.name, MINT_DECIMALS_ARG.name])
397    }
398    fn signer_arg<'a, 'b>(&self, arg: Arg<'a>) -> Arg<'a> {
399        arg.requires_all(&[MINT_ADDRESS_ARG.name, MINT_DECIMALS_ARG.name])
400    }
401}
402
403struct SignOnlyNeedsMintDecimals {}
404impl offline::ArgsConfig for SignOnlyNeedsMintDecimals {
405    fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a>) -> Arg<'a> {
406        arg.requires_all(&[MINT_DECIMALS_ARG.name])
407    }
408    fn signer_arg<'a, 'b>(&self, arg: Arg<'a>) -> Arg<'a> {
409        arg.requires_all(&[MINT_DECIMALS_ARG.name])
410    }
411}
412
413struct SignOnlyNeedsMintAddress {}
414impl offline::ArgsConfig for SignOnlyNeedsMintAddress {
415    fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a>) -> Arg<'a> {
416        arg.requires_all(&[MINT_ADDRESS_ARG.name])
417    }
418    fn signer_arg<'a, 'b>(&self, arg: Arg<'a>) -> Arg<'a> {
419        arg.requires_all(&[MINT_ADDRESS_ARG.name])
420    }
421}
422
423struct SignOnlyNeedsDelegateAddress {}
424impl offline::ArgsConfig for SignOnlyNeedsDelegateAddress {
425    fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a>) -> Arg<'a> {
426        arg.requires_all(&[DELEGATE_ADDRESS_ARG.name])
427    }
428    fn signer_arg<'a, 'b>(&self, arg: Arg<'a>) -> Arg<'a> {
429        arg.requires_all(&[DELEGATE_ADDRESS_ARG.name])
430    }
431}
432
433struct SignOnlyNeedsTransferLamports {}
434impl offline::ArgsConfig for SignOnlyNeedsTransferLamports {
435    fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a>) -> Arg<'a> {
436        arg.requires_all(&[TRANSFER_LAMPORTS_ARG.name])
437    }
438    fn signer_arg<'a, 'b>(&self, arg: Arg<'a>) -> Arg<'a> {
439        arg.requires_all(&[TRANSFER_LAMPORTS_ARG.name])
440    }
441}
442
443pub fn minimum_signers_help_string() -> String {
444    format!(
445        "The minimum number of signers required to allow the operation. [{} <= M <= N]",
446        MIN_SIGNERS
447    )
448}
449
450pub fn multisig_member_help_string() -> String {
451    format!(
452        "The public keys for each of the N signing members of this account. [{} <= N <= {}]",
453        MIN_SIGNERS, MAX_SIGNERS
454    )
455}
456
457pub(crate) trait BenchSubCommand {
458    fn bench_subcommand(self) -> Self;
459}
460
461impl BenchSubCommand for App<'_> {
462    fn bench_subcommand(self) -> Self {
463        self.subcommand(
464            SubCommand::with_name("bench")
465                .about("Token benchmarking facilities")
466                .setting(AppSettings::InferSubcommands)
467                .setting(AppSettings::SubcommandRequiredElseHelp)
468                .subcommand(
469                    SubCommand::with_name("create-accounts")
470                        .about("Create multiple token accounts for benchmarking")
471                        .arg(
472                            Arg::with_name("token")
473                                .validator(|s| is_valid_pubkey(s))
474                                .value_name("TOKEN_ADDRESS")
475                                .takes_value(true)
476                                .index(1)
477                                .required(true)
478                                .help("The token that the accounts will hold"),
479                        )
480                        .arg(
481                            Arg::with_name("n")
482                                .value_parser(clap::value_parser!(usize))
483                                .value_name("N")
484                                .takes_value(true)
485                                .index(2)
486                                .required(true)
487                                .help("The number of accounts to create"),
488                        )
489                        .arg(owner_address_arg()),
490                )
491                .subcommand(
492                    SubCommand::with_name("close-accounts")
493                        .about("Close multiple token accounts used for benchmarking")
494                        .arg(
495                            Arg::with_name("token")
496                                .validator(|s| is_valid_pubkey(s))
497                                .value_name("TOKEN_ADDRESS")
498                                .takes_value(true)
499                                .index(1)
500                                .required(true)
501                                .help("The token that the accounts held"),
502                        )
503                        .arg(
504                            Arg::with_name("n")
505                                .value_parser(clap::value_parser!(usize))
506                                .value_name("N")
507                                .takes_value(true)
508                                .index(2)
509                                .required(true)
510                                .help("The number of accounts to close"),
511                        )
512                        .arg(owner_address_arg()),
513                )
514                .subcommand(
515                    SubCommand::with_name("deposit-into")
516                        .about("Deposit tokens into multiple accounts")
517                        .arg(
518                            Arg::with_name("token")
519                                .validator(|s| is_valid_pubkey(s))
520                                .value_name("TOKEN_ADDRESS")
521                                .takes_value(true)
522                                .index(1)
523                                .required(true)
524                                .help("The token that the accounts will hold"),
525                        )
526                        .arg(
527                            Arg::with_name("n")
528                                .value_parser(clap::value_parser!(usize))
529                                .value_name("N")
530                                .takes_value(true)
531                                .index(2)
532                                .required(true)
533                                .help("The number of accounts to deposit into"),
534                        )
535                        .arg(
536                            Arg::with_name("amount")
537                                .value_parser(Amount::parse)
538                                .value_name("TOKEN_AMOUNT")
539                                .takes_value(true)
540                                .index(3)
541                                .required(true)
542                                .help("Amount to deposit into each account, in tokens"),
543                        )
544                        .arg(
545                            Arg::with_name("from")
546                                .long("from")
547                                .validator(|s| is_valid_pubkey(s))
548                                .value_name("SOURCE_TOKEN_ACCOUNT_ADDRESS")
549                                .takes_value(true)
550                                .help("The source token account address [default: associated token account for --owner]")
551                        )
552                        .arg(owner_address_arg()),
553                )
554                .subcommand(
555                    SubCommand::with_name("withdraw-from")
556                        .about("Withdraw tokens from multiple accounts")
557                        .arg(
558                            Arg::with_name("token")
559                                .validator(|s| is_valid_pubkey(s))
560                                .value_name("TOKEN_ADDRESS")
561                                .takes_value(true)
562                                .index(1)
563                                .required(true)
564                                .help("The token that the accounts hold"),
565                        )
566                        .arg(
567                            Arg::with_name("n")
568                                .value_parser(clap::value_parser!(usize))
569                                .value_name("N")
570                                .takes_value(true)
571                                .index(2)
572                                .required(true)
573                                .help("The number of accounts to withdraw from"),
574                        )
575                        .arg(
576                            Arg::with_name("amount")
577                                .value_parser(Amount::parse)
578                                .value_name("TOKEN_AMOUNT")
579                                .takes_value(true)
580                                .index(3)
581                                .required(true)
582                                .help("Amount to withdraw from each account, in tokens"),
583                        )
584                        .arg(
585                            Arg::with_name("to")
586                                .long("to")
587                                .validator(|s| is_valid_pubkey(s))
588                                .value_name("RECIPIENT_TOKEN_ACCOUNT_ADDRESS")
589                                .takes_value(true)
590                                .help("The recipient token account address [default: associated token account for --owner]")
591                        )
592                        .arg(owner_address_arg()),
593                ),
594        )
595    }
596}
597
598pub fn app<'a>(
599    default_decimals: &'a str,
600    minimum_signers_help: &'a str,
601    multisig_member_help: &'a str,
602) -> App<'a> {
603    App::new(crate_name!())
604        .about(crate_description!())
605        .version(crate_version!())
606        .setting(AppSettings::SubcommandRequiredElseHelp)
607        .arg(
608            Arg::with_name("config_file")
609                .short('C')
610                .long("config")
611                .value_name("PATH")
612                .takes_value(true)
613                .global(true)
614                .help("Configuration file to use"),
615        )
616        .arg(
617            Arg::with_name("verbose")
618                .short('v')
619                .long("verbose")
620                .takes_value(false)
621                .global(true)
622                .help("Show additional information"),
623        )
624        .arg(
625            Arg::with_name("output_format")
626                .long("output")
627                .value_name("FORMAT")
628                .global(true)
629                .takes_value(true)
630                .possible_values(["json", "json-compact"])
631                .help("Return information in specified output format"),
632        )
633        .arg(
634            Arg::with_name("program_2022")
635                .long("program-2022")
636                .takes_value(false)
637                .global(true)
638                .conflicts_with("program_id")
639                .help("Use token extension program token 2022 with program id: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"),
640        )
641        .arg(
642            Arg::with_name("program_id")
643                .short('p')
644                .long("program-id")
645                .value_name("ADDRESS")
646                .takes_value(true)
647                .global(true)
648                .conflicts_with("program_2022")
649                .validator(|s| is_valid_token_program_id(s))
650                .help("SPL Token program id"),
651        )
652        .arg(
653            Arg::with_name("json_rpc_url")
654                .short('u')
655                .long("url")
656                .value_name("URL_OR_MONIKER")
657                .takes_value(true)
658                .global(true)
659                .validator(|s| is_url_or_moniker(s))
660                .help(
661                    "URL for Solana's JSON RPC or moniker (or their first letter): \
662                       [mainnet-beta, testnet, devnet, localhost] \
663                    Default from the configuration file."
664                ),
665        )
666        .arg(fee_payer_arg().global(true))
667        .arg(
668            Arg::with_name("use_unchecked_instruction")
669                .long("use-unchecked-instruction")
670                .takes_value(false)
671                .global(true)
672                .hidden(true)
673                .help("Use unchecked instruction if appropriate. Supports transfer, burn, mint, and approve."),
674        )
675        .arg(
676            Arg::with_name(COMPUTE_UNIT_LIMIT_ARG.name)
677                .long(COMPUTE_UNIT_LIMIT_ARG.long)
678                .takes_value(true)
679                .global(true)
680                .value_name("COMPUTE-UNIT-LIMIT")
681                .value_parser(clap::value_parser!(u32))
682                .help(COMPUTE_UNIT_LIMIT_ARG.help)
683        )
684        .arg(
685            Arg::with_name(COMPUTE_UNIT_PRICE_ARG.name)
686                .long(COMPUTE_UNIT_PRICE_ARG.long)
687                .takes_value(true)
688                .global(true)
689                .value_name("COMPUTE-UNIT-PRICE")
690                .value_parser(clap::value_parser!(u64))
691                .help(COMPUTE_UNIT_PRICE_ARG.help)
692        )
693        .bench_subcommand()
694        .subcommand(SubCommand::with_name(CommandName::CreateToken.into()).about("Create a new token")
695                .arg(
696                    Arg::with_name("token_keypair")
697                        .value_name("TOKEN_KEYPAIR")
698                        .validator(|s| is_valid_signer(s))
699                        .takes_value(true)
700                        .index(1)
701                        .help(
702                            "Specify the token keypair. \
703                             This may be a keypair file or the ASK keyword. \
704                             [default: randomly generated keypair]"
705                        ),
706                )
707                .arg(
708                    Arg::with_name("mint_authority")
709                        .long("mint-authority")
710                        .alias("owner")
711                        .value_name("ADDRESS")
712                        .validator(|s| is_valid_pubkey(s))
713                        .takes_value(true)
714                        .help(
715                            "Specify the mint authority address. \
716                             Defaults to the client keypair address."
717                        ),
718                )
719                .arg(
720                    Arg::with_name("decimals")
721                        .long("decimals")
722                        .value_parser(clap::value_parser!(u8))
723                        .value_name("DECIMALS")
724                        .takes_value(true)
725                        .default_value(default_decimals)
726                        .help("Number of base 10 digits to the right of the decimal place"),
727                )
728                .arg(
729                    Arg::with_name("enable_freeze")
730                        .long("enable-freeze")
731                        .takes_value(false)
732                        .help(
733                            "Enable the mint authority to freeze token accounts for this mint"
734                        ),
735                )
736                .arg(
737                    Arg::with_name("enable_close")
738                        .long("enable-close")
739                        .takes_value(false)
740                        .help(
741                            "Enable the mint authority to close this mint"
742                        ),
743                )
744                .arg(
745                    Arg::with_name("interest_rate")
746                        .long("interest-rate")
747                        .value_name("RATE_BPS")
748                        .takes_value(true)
749                        .conflicts_with("ui_amount_multiplier")
750                        .help(
751                            "Specify the interest rate in basis points. \
752                            Rate authority defaults to the mint authority."
753                        ),
754                )
755                .arg(
756                    Arg::with_name("metadata_address")
757                        .long("metadata-address")
758                        .value_name("ADDRESS")
759                        .validator(|s| is_valid_pubkey(s))
760                        .takes_value(true)
761                        .conflicts_with("enable_metadata")
762                        .help(
763                            "Specify address that stores token metadata."
764                        ),
765                )
766                .arg(
767                    Arg::with_name("group_address")
768                        .long("group-address")
769                        .value_name("ADDRESS")
770                        .validator(|s| is_valid_pubkey(s))
771                        .takes_value(true)
772                        .conflicts_with("enable_group")
773                        .help(
774                            "Specify address that stores token group configurations."
775                        ),
776                )
777                .arg(
778                    Arg::with_name("member_address")
779                        .long("member-address")
780                        .value_name("ADDRESS")
781                        .validator(|s| is_valid_pubkey(s))
782                        .takes_value(true)
783                        .conflicts_with("enable_member")
784                        .help(
785                            "Specify address that stores token member configurations."
786                        ),
787                )
788                .arg(
789                    Arg::with_name("enable_non_transferable")
790                        .long("enable-non-transferable")
791                        .alias("enable-nontransferable")
792                        .takes_value(false)
793                        .help(
794                            "Permanently force tokens to be non-transferable. They may still be burned."
795                        ),
796                )
797                .arg(
798                    Arg::with_name("default_account_state")
799                        .long("default-account-state")
800                        .requires("enable_freeze")
801                        .takes_value(true)
802                        .possible_values(["initialized", "frozen"])
803                        .help("Specify that accounts have a default state. \
804                            Note: specifying \"initialized\" adds an extension, which gives \
805                            the option of specifying default frozen accounts in the future. \
806                            This behavior is not the same as the default, which makes it \
807                            impossible to specify a default account state in the future."),
808                )
809                .arg(
810                    Arg::with_name("transfer_fee")
811                        .long("transfer-fee")
812                        .value_names(&["FEE_IN_BASIS_POINTS", "MAXIMUM_FEE"])
813                        .takes_value(true)
814                        .number_of_values(2)
815                        .hidden(true)
816                        .conflicts_with("transfer_fee_basis_points")
817                        .conflicts_with("transfer_fee_maximum_fee")
818                        .help(
819                            "Add a transfer fee to the mint. \
820                            The mint authority can set the fee and withdraw collected fees.",
821                        ),
822                )
823                .arg(
824                    Arg::with_name("transfer_fee_basis_points")
825                        .long("transfer-fee-basis-points")
826                        .value_names(&["FEE_IN_BASIS_POINTS"])
827                        .takes_value(true)
828                        .number_of_values(1)
829                        .conflicts_with("transfer_fee")
830                        .requires("transfer_fee_maximum_fee")
831                        .value_parser(clap::value_parser!(u16))
832                        .help(
833                            "Add transfer fee to the mint. \
834                            The mint authority can set the fee.",
835                        ),
836                )
837                .arg(
838                    Arg::with_name("transfer_fee_maximum_fee")
839                        .long("transfer-fee-maximum-fee")
840                        .value_names(&["MAXIMUM_FEE"])
841                        .takes_value(true)
842                        .number_of_values(1)
843                        .conflicts_with("transfer_fee")
844                        .requires("transfer_fee_basis_points")
845                        .value_parser(Amount::parse)
846                        .help(
847                            "Add a UI amount maximum transfer fee to the mint. \
848                            The mint authority can set and collect fees"
849                        )
850                )
851                .arg(
852                    Arg::with_name("enable_permanent_delegate")
853                        .long("enable-permanent-delegate")
854                        .takes_value(false)
855                        .help(
856                            "Enable the mint authority to be permanent delegate for this mint"
857                        ),
858                )
859                .arg(
860                    Arg::with_name("enable_confidential_transfers")
861                        .long("enable-confidential-transfers")
862                        .value_names(&["APPROVE-POLICY"])
863                        .takes_value(true)
864                        .possible_values(["auto", "manual"])
865                        .help(
866                            "Enable accounts to make confidential transfers. If \"auto\" \
867                            is selected, then accounts are automatically approved to make \
868                            confidential transfers. If \"manual\" is selected, then the \
869                            confidential transfer mint authority must approve each account \
870                            before it can make confidential transfers."
871                        )
872                )
873                .arg(
874                    Arg::with_name("transfer_hook")
875                        .long("transfer-hook")
876                        .value_name("TRANSFER_HOOK_PROGRAM_ID")
877                        .validator(|s| is_valid_pubkey(s))
878                        .takes_value(true)
879                        .help("Set a transfer hook program for this mint. The mint authority can set the program id."),
880                )
881                .arg(
882                    Arg::with_name("enable_transfer_hook")
883                        .long("enable-transfer-hook")
884                        .conflicts_with("transfer_hook")
885                        .takes_value(false)
886                        .help("Enables the transfer hook in the mint. The mint authority can set the program id."),
887                )
888                .arg(
889                    Arg::with_name("enable_metadata")
890                        .long("enable-metadata")
891                        .conflicts_with("metadata_address")
892                        .takes_value(false)
893                        .help("Enables metadata in the mint. The mint authority must initialize the metadata."),
894                )
895                .arg(
896                    Arg::with_name("enable_group")
897                        .long("enable-group")
898                        .conflicts_with("group_address")
899                        .takes_value(false)
900                        .help("Enables group configurations in the mint. The mint authority must initialize the group."),
901                )
902                .arg(
903                    Arg::with_name("enable_member")
904                        .long("enable-member")
905                        .conflicts_with("member_address")
906                        .takes_value(false)
907                        .help("Enables group member configurations in the mint. The mint authority must initialize the member."),
908                )
909                .arg(
910                    Arg::with_name("ui_amount_multiplier")
911                        .long("ui-amount-multiplier")
912                        .value_name("MULTIPLIER")
913                        .takes_value(true)
914                        .conflicts_with("interest_rate")
915                        .help(
916                            "Specify the UI multiplier. \
917                            Multiplier authority defaults to the mint authority."
918                        ),
919                )
920                .arg(
921                    Arg::with_name("enable_pause")
922                        .long("enable-pause")
923                        .takes_value(false)
924                        .help(
925                            "Enable the mint authority to pause mint, burn, and transfer for this mint"
926                        )
927                )
928                .arg(multisig_signer_arg())
929                .nonce_args(true)
930                .arg(memo_arg())
931        )
932        .subcommand(
933            SubCommand::with_name(CommandName::SetInterestRate.into())
934                .about("Set the interest rate for an interest-bearing token")
935                .arg(
936                    Arg::with_name("token")
937                        .validator(|s| is_valid_pubkey(s))
938                        .value_name("TOKEN_MINT_ADDRESS")
939                        .takes_value(true)
940                        .required(true)
941                        .help("The interest-bearing token address"),
942                )
943                .arg(
944                    Arg::with_name("rate")
945                        .value_name("RATE")
946                        .takes_value(true)
947                        .required(true)
948                        .help("The new interest rate in basis points"),
949                )
950                .arg(
951                    Arg::with_name("rate_authority")
952                    .long("rate-authority")
953                    .validator(|s| is_valid_signer(s))
954                    .value_name("SIGNER")
955                    .takes_value(true)
956                    .help(
957                        "Specify the rate authority keypair. \
958                        Defaults to the client keypair address."
959                    )
960                )
961        )
962        .subcommand(
963            SubCommand::with_name(CommandName::SetTransferHook.into())
964                .about("Set the transfer hook program id for a token")
965                .arg(
966                    Arg::with_name("token")
967                        .validator(|s| is_valid_pubkey(s))
968                        .value_name("TOKEN_MINT_ADDRESS")
969                        .takes_value(true)
970                        .required(true)
971                        .index(1)
972                        .help("The token address with an existing transfer hook"),
973                )
974                .arg(
975                    Arg::with_name("new_program_id")
976                        .validator(|s| is_valid_pubkey(s))
977                        .value_name("NEW_PROGRAM_ID")
978                        .takes_value(true)
979                        .required_unless("disable")
980                        .index(2)
981                        .help("The new transfer hook program id to set for this mint"),
982                )
983                .arg(
984                    Arg::with_name("disable")
985                        .long("disable")
986                        .takes_value(false)
987                        .conflicts_with("new_program_id")
988                        .help("Disable transfer hook functionality by setting the program id to None.")
989                )
990                .arg(
991                    Arg::with_name("authority")
992                        .long("authority")
993                        .alias("owner")
994                        .validator(|s| is_valid_signer(s))
995                        .value_name("SIGNER")
996                        .takes_value(true)
997                        .help("Specify the authority keypair. Defaults to the client keypair address.")
998                )
999        )
1000        .subcommand(
1001            SubCommand::with_name(CommandName::InitializeMetadata.into())
1002                .about("Initialize metadata extension on a token mint")
1003                .arg(
1004                    Arg::with_name("token")
1005                        .validator(|s| is_valid_pubkey(s))
1006                        .value_name("TOKEN_MINT_ADDRESS")
1007                        .takes_value(true)
1008                        .required(true)
1009                        .index(1)
1010                        .help("The token address with no metadata present"),
1011                )
1012                .arg(
1013                    Arg::with_name("name")
1014                        .value_name("TOKEN_NAME")
1015                        .takes_value(true)
1016                        .required(true)
1017                        .index(2)
1018                        .help("The name of the token to set in metadata"),
1019                )
1020                .arg(
1021                    Arg::with_name("symbol")
1022                        .value_name("TOKEN_SYMBOL")
1023                        .takes_value(true)
1024                        .required(true)
1025                        .index(3)
1026                        .help("The symbol of the token to set in metadata"),
1027                )
1028                .arg(
1029                    Arg::with_name("uri")
1030                        .value_name("TOKEN_URI")
1031                        .takes_value(true)
1032                        .required(true)
1033                        .index(4)
1034                        .help("The URI of the token to set in metadata"),
1035                )
1036                .arg(
1037                    Arg::with_name("mint_authority")
1038                        .long("mint-authority")
1039                        .alias("owner")
1040                        .value_name("KEYPAIR")
1041                        .validator(|s| is_valid_signer(s))
1042                        .takes_value(true)
1043                        .help(
1044                            "Specify the mint authority keypair. \
1045                             This may be a keypair file or the ASK keyword. \
1046                             Defaults to the client keypair."
1047                        ),
1048                )
1049                .arg(
1050                    Arg::with_name("update_authority")
1051                        .long("update-authority")
1052                        .value_name("ADDRESS")
1053                        .validator(|s| is_valid_pubkey(s))
1054                        .takes_value(true)
1055                        .help(
1056                            "Specify the update authority address. \
1057                             Defaults to the client keypair address."
1058                        ),
1059                )
1060        )
1061        .subcommand(
1062            SubCommand::with_name(CommandName::UpdateMetadata.into())
1063                .about("Update metadata on a token mint that has the extension")
1064                .arg(
1065                    Arg::with_name("token")
1066                        .validator(|s| is_valid_pubkey(s))
1067                        .value_name("TOKEN_MINT_ADDRESS")
1068                        .takes_value(true)
1069                        .required(true)
1070                        .index(1)
1071                        .help("The token address with no metadata present"),
1072                )
1073                .arg(
1074                    Arg::with_name("field")
1075                        .value_name("FIELD_NAME")
1076                        .takes_value(true)
1077                        .required(true)
1078                        .index(2)
1079                        .help("The name of the field to update. Can be a base field (\"name\", \"symbol\", or \"uri\") or any new field to add."),
1080                )
1081                .arg(
1082                    Arg::with_name("value")
1083                        .value_name("VALUE_STRING")
1084                        .takes_value(true)
1085                        .index(3)
1086                        .required_unless("remove")
1087                        .help("The value for the field"),
1088                )
1089                .arg(
1090                    Arg::with_name("remove")
1091                        .long("remove")
1092                        .takes_value(false)
1093                        .conflicts_with("value")
1094                        .help("Remove the key and value for the given field. Does not work with base fields: \"name\", \"symbol\", or \"uri\".")
1095                )
1096                .arg(
1097                    Arg::with_name("authority")
1098                    .long("authority")
1099                    .validator(|s| is_valid_signer(s))
1100                    .value_name("SIGNER")
1101                    .takes_value(true)
1102                    .help("Specify the metadata update authority keypair. Defaults to the client keypair.")
1103                )
1104                .nonce_args(true)
1105                .arg(transfer_lamports_arg())
1106                .offline_args_config(&SignOnlyNeedsTransferLamports{}),
1107        )
1108        .subcommand(
1109            SubCommand::with_name(CommandName::InitializeGroup.into())
1110                .about("Initialize group extension on a token mint")
1111                .arg(
1112                    Arg::with_name("token")
1113                        .validator(|s| is_valid_pubkey(s))
1114                        .value_name("TOKEN_MINT_ADDRESS")
1115                        .takes_value(true)
1116                        .required(true)
1117                        .index(1)
1118                        .help("The token address of the group account."),
1119                )
1120                .arg(
1121                        Arg::with_name("max_size")
1122                        .value_parser(clap::value_parser!(u64))
1123                        .value_name("MAX_SIZE")
1124                        .takes_value(true)
1125                        .required(true)
1126                        .index(2)
1127                        .help("The number of members in the group."),
1128                    )
1129                .arg(
1130                    Arg::with_name("mint_authority")
1131                        .long("mint-authority")
1132                        .alias("owner")
1133                        .value_name("KEYPAIR")
1134                        .validator(|s| is_valid_signer(s))
1135                        .takes_value(true)
1136                        .help(
1137                            "Specify the mint authority keypair. \
1138                             This may be a keypair file or the ASK keyword. \
1139                             Defaults to the client keypair."
1140                        ),
1141                )
1142                .arg(
1143                    Arg::with_name("update_authority")
1144                        .long("update-authority")
1145                        .value_name("ADDRESS")
1146                        .validator(|s| is_valid_pubkey(s))
1147                        .takes_value(true)
1148                        .help(
1149                            "Specify the update authority address. \
1150                             Defaults to the client keypair address."
1151                        ),
1152                )
1153        )
1154        .subcommand(
1155            SubCommand::with_name(CommandName::UpdateGroupMaxSize.into())
1156                .about("Updates the maximum number of members for a group.")
1157                .arg(
1158                    Arg::with_name("token")
1159                        .validator(|s| is_valid_pubkey(s))
1160                        .value_name("TOKEN_MINT_ADDRESS")
1161                        .takes_value(true)
1162                        .required(true)
1163                        .index(1)
1164                        .help("The token address of the group account."),
1165                )
1166                .arg(
1167                        Arg::with_name("new_max_size")
1168                        .value_parser(clap::value_parser!(u64))
1169                        .value_name("NEW_MAX_SIZE")
1170                        .takes_value(true)
1171                        .required(true)
1172                        .index(2)
1173                        .help("The number of members in the group."),
1174                    )
1175                .arg(
1176                    Arg::with_name("update_authority")
1177                        .long("update-authority")
1178                        .value_name("SIGNER")
1179                        .validator(|s| is_valid_signer(s))
1180                        .takes_value(true)
1181                        .help(
1182                            "Specify the update authority address. \
1183                             Defaults to the client keypair address."
1184                        ),
1185                )
1186        )
1187        .subcommand(
1188            SubCommand::with_name(CommandName::InitializeMember.into())
1189                .about("Initialize group member extension on a token mint")
1190                .arg(
1191                    Arg::with_name("token")
1192                        .validator(|s| is_valid_pubkey(s))
1193                        .value_name("TOKEN_MINT_ADDRESS")
1194                        .takes_value(true)
1195                        .required(true)
1196                        .index(1)
1197                        .help("The token address of the member account."),
1198                )
1199                .arg(
1200                    Arg::with_name("group_token")
1201                        .validator(|s| is_valid_pubkey(s))
1202                        .value_name("GROUP_TOKEN_ADDRESS")
1203                        .takes_value(true)
1204                        .required(true)
1205                        .index(2)
1206                        .help("The token address of the group account that the token will join."),
1207                )
1208                .arg(
1209                    Arg::with_name("mint_authority")
1210                        .long("mint-authority")
1211                        .alias("owner")
1212                        .value_name("KEYPAIR")
1213                        .validator(|s| is_valid_signer(s))
1214                        .takes_value(true)
1215                        .help(
1216                            "Specify the mint authority keypair. \
1217                             This may be a keypair file or the ASK keyword. \
1218                             Defaults to the client keypair."
1219                        ),
1220                )
1221                .arg(
1222                    Arg::with_name("group_update_authority")
1223                        .long("group-update-authority")
1224                        .value_name("KEYPAIR")
1225                        .validator(|s| is_valid_signer(s))
1226                        .takes_value(true)
1227                        .help(
1228                            "Specify the update authority keypair. \
1229                             This may be a keypair file or the ASK keyword. \
1230                             Defaults to the client keypair address."
1231                        ),
1232                )
1233        )
1234        .subcommand(
1235            SubCommand::with_name(CommandName::CreateAccount.into())
1236                .about("Create a new token account")
1237                .arg(
1238                    Arg::with_name("token")
1239                        .validator(|s| is_valid_pubkey(s))
1240                        .value_name("TOKEN_MINT_ADDRESS")
1241                        .takes_value(true)
1242                        .index(1)
1243                        .required(true)
1244                        .help("The token that the account will hold"),
1245                )
1246                .arg(
1247                    Arg::with_name("account_keypair")
1248                        .value_name("ACCOUNT_KEYPAIR")
1249                        .validator(|s| is_valid_signer(s))
1250                        .takes_value(true)
1251                        .index(2)
1252                        .help(
1253                            "Specify the account keypair. \
1254                             This may be a keypair file or the ASK keyword. \
1255                             [default: associated token account for --owner]"
1256                        ),
1257                )
1258                .arg(
1259                    Arg::with_name("immutable")
1260                        .long("immutable")
1261                        .takes_value(false)
1262                        .help(
1263                            "Lock the owner of this token account from ever being changed"
1264                        ),
1265                )
1266                .arg(owner_address_arg())
1267                .nonce_args(true)
1268        )
1269        .subcommand(
1270            SubCommand::with_name(CommandName::CreateMultisig.into())
1271                .about("Create a new account describing an M:N multisignature")
1272                .arg(
1273                    Arg::with_name("minimum_signers")
1274                        .value_name("MINIMUM_SIGNERS")
1275                        .validator(is_multisig_minimum_signers)
1276                        .takes_value(true)
1277                        .index(1)
1278                        .required(true)
1279                        .help(minimum_signers_help),
1280                )
1281                .arg(
1282                    Arg::with_name("multisig_member")
1283                        .value_name("MULTISIG_MEMBER_PUBKEY")
1284                        .validator(|s| is_valid_pubkey(s))
1285                        .takes_value(true)
1286                        .index(2)
1287                        .required(true)
1288                        .min_values(MIN_SIGNERS)
1289                        .max_values(MAX_SIGNERS)
1290                        .help(multisig_member_help),
1291                )
1292                .arg(
1293                    Arg::with_name("address_keypair")
1294                        .long("address-keypair")
1295                        .value_name("ADDRESS_KEYPAIR")
1296                        .validator(|s| is_valid_signer(s))
1297                        .takes_value(true)
1298                        .help(
1299                            "Specify the address keypair. \
1300                             This may be a keypair file or the ASK keyword. \
1301                             [default: randomly generated keypair]"
1302                        ),
1303                )
1304                .nonce_args(true)
1305        )
1306        .subcommand(
1307            SubCommand::with_name(CommandName::Authorize.into())
1308                .about("Authorize a new signing keypair to a token or token account")
1309                .arg(
1310                    Arg::with_name("address")
1311                        .validator(|s| is_valid_pubkey(s))
1312                        .value_name("TOKEN_ADDRESS")
1313                        .takes_value(true)
1314                        .index(1)
1315                        .required(true)
1316                        .help("The address of the token mint or account"),
1317                )
1318                .arg(
1319                    Arg::with_name("authority_type")
1320                        .value_name("AUTHORITY_TYPE")
1321                        .takes_value(true)
1322                        .possible_values(CliAuthorityType::iter().map(Into::<&str>::into).collect::<Vec<_>>())
1323                        .index(2)
1324                        .required(true)
1325                        .help("The new authority type. \
1326                            Token mints support `mint`, `freeze`, and mint extension authorities; \
1327                            Token accounts support `owner`, `close`, and account extension \
1328                            authorities."),
1329                )
1330                .arg(
1331                    Arg::with_name("new_authority")
1332                        .validator(|s| is_valid_pubkey(s))
1333                        .value_name("AUTHORITY_ADDRESS")
1334                        .takes_value(true)
1335                        .index(3)
1336                        .required_unless("disable")
1337                        .help("The address of the new authority"),
1338                )
1339                .arg(
1340                    Arg::with_name("authority")
1341                        .long("authority")
1342                        .alias("owner")
1343                        .value_name("KEYPAIR")
1344                        .validator(|s| is_valid_signer(s))
1345                        .takes_value(true)
1346                        .help(
1347                            "Specify the current authority keypair. \
1348                             Defaults to the client keypair."
1349                        ),
1350                )
1351                .arg(
1352                    Arg::with_name("disable")
1353                        .long("disable")
1354                        .takes_value(false)
1355                        .conflicts_with("new_authority")
1356                        .help("Disable mint, freeze, or close functionality by setting authority to None.")
1357                )
1358                .arg(
1359                    Arg::with_name("force")
1360                        .long("force")
1361                        .hidden(true)
1362                        .help("Force re-authorize the wallet's associate token account. Don't use this flag"),
1363                )
1364                .arg(multisig_signer_arg())
1365                .nonce_args(true)
1366                .offline_args(),
1367        )
1368        .subcommand(
1369            SubCommand::with_name(CommandName::Transfer.into())
1370                .about("Transfer tokens between accounts")
1371                .arg(
1372                    Arg::with_name("token")
1373                        .validator(|s| is_valid_pubkey(s))
1374                        .value_name("TOKEN_MINT_ADDRESS")
1375                        .takes_value(true)
1376                        .index(1)
1377                        .required(true)
1378                        .help("Token to transfer"),
1379                )
1380                .arg(
1381                    Arg::with_name("amount")
1382                        .value_parser(Amount::parse)
1383                        .value_name("TOKEN_AMOUNT")
1384                        .takes_value(true)
1385                        .index(2)
1386                        .required(true)
1387                        .help("Amount to send, in tokens; accepts keyword ALL"),
1388                )
1389                .arg(
1390                    Arg::with_name("recipient")
1391                        .validator(|s| is_valid_pubkey(s))
1392                        .value_name("RECIPIENT_WALLET_ADDRESS or RECIPIENT_TOKEN_ACCOUNT_ADDRESS")
1393                        .takes_value(true)
1394                        .index(3)
1395                        .required(true)
1396                        .help("If a token account address is provided, use it as the recipient. \
1397                               Otherwise assume the recipient address is a user wallet and transfer to \
1398                               the associated token account")
1399                )
1400                .arg(
1401                    Arg::with_name("from")
1402                        .validator(|s| is_valid_pubkey(s))
1403                        .value_name("SENDER_TOKEN_ACCOUNT_ADDRESS")
1404                        .takes_value(true)
1405                        .long("from")
1406                        .help("Specify the sending token account \
1407                            [default: owner's associated token account]")
1408                )
1409                .arg(owner_keypair_arg_with_value_name("SENDER_TOKEN_OWNER_KEYPAIR")
1410                        .help(
1411                            "Specify the owner of the sending token account. \
1412                            This may be a keypair file or the ASK keyword. \
1413                            Defaults to the client keypair.",
1414                        ),
1415                )
1416                .arg(
1417                    Arg::with_name("allow_unfunded_recipient")
1418                        .long("allow-unfunded-recipient")
1419                        .takes_value(false)
1420                        .help("Complete the transfer even if the recipient address is not funded")
1421                )
1422                .arg(
1423                    Arg::with_name("allow_empty_recipient")
1424                        .long("allow-empty-recipient")
1425                        .takes_value(false)
1426                        .hidden(true) // Deprecated, use --allow-unfunded-recipient instead
1427                )
1428                .arg(
1429                    Arg::with_name("fund_recipient")
1430                        .long("fund-recipient")
1431                        .takes_value(false)
1432                        .conflicts_with("confidential")
1433                        .help("Create the associated token account for the recipient if doesn't already exist")
1434                )
1435                .arg(
1436                    Arg::with_name("no_wait")
1437                        .long("no-wait")
1438                        .takes_value(false)
1439                        .help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"),
1440                )
1441                .arg(
1442                    Arg::with_name("allow_non_system_account_recipient")
1443                        .long("allow-non-system-account-recipient")
1444                        .takes_value(false)
1445                        .help("Send tokens to the recipient even if the recipient is not a wallet owned by System Program."),
1446                )
1447                .arg(
1448                    Arg::with_name("no_recipient_is_ata_owner")
1449                        .long("no-recipient-is-ata-owner")
1450                        .takes_value(false)
1451                        .requires("sign_only")
1452                        .help("In sign-only mode, specifies that the recipient is the owner of the associated token account rather than an actual token account"),
1453                )
1454                .arg(
1455                    Arg::with_name("recipient_is_ata_owner")
1456                        .long("recipient-is-ata-owner")
1457                        .takes_value(false)
1458                        .hidden(true)
1459                        .conflicts_with("no_recipient_is_ata_owner")
1460                        .requires("sign_only")
1461                        .help("recipient-is-ata-owner is now the default behavior. The option has been deprecated and will be removed in a future release."),
1462                )
1463                .arg(
1464                    Arg::with_name("expected_fee")
1465                        .long("expected-fee")
1466                        .value_parser(Amount::parse)
1467                        .value_name("EXPECTED_FEE")
1468                        .takes_value(true)
1469                        .help("Expected fee amount collected during the transfer"),
1470                )
1471                .arg(
1472                    Arg::with_name("transfer_hook_account")
1473                        .long("transfer-hook-account")
1474                        .validator(|s| validate_transfer_hook_account(s))
1475                        .value_name("PUBKEY:ROLE")
1476                        .takes_value(true)
1477                        .multiple(true)
1478                        .min_values(0_usize)
1479                        .help("Additional pubkey(s) required for a transfer hook and their \
1480                            role, in the format \"<PUBKEY>:<ROLE>\". The role must be \
1481                            \"readonly\", \"writable\". \"readonly-signer\", or \"writable-signer\".\
1482                            Used for offline transaction creation and signing.")
1483                )
1484                .arg(
1485                    Arg::with_name("confidential")
1486                        .long("confidential")
1487                        .takes_value(false)
1488                        .conflicts_with("fund_recipient")
1489                        .help("Send tokens confidentially. Both sender and recipient accounts must \
1490                            be pre-configured for confidential transfers.")
1491                )
1492                .arg(multisig_signer_arg())
1493                .arg(mint_decimals_arg())
1494                .nonce_args(true)
1495                .arg(memo_arg())
1496                .offline_args_config(&SignOnlyNeedsMintDecimals{}),
1497        )
1498        .subcommand(
1499            SubCommand::with_name(CommandName::Burn.into())
1500                .about("Burn tokens from an account")
1501                .arg(
1502                    Arg::with_name("account")
1503                        .validator(|s| is_valid_pubkey(s))
1504                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1505                        .takes_value(true)
1506                        .index(1)
1507                        .required(true)
1508                        .help("The token account address to burn from"),
1509                )
1510                .arg(
1511                    Arg::with_name("amount")
1512                        .value_parser(Amount::parse)
1513                        .value_name("TOKEN_AMOUNT")
1514                        .takes_value(true)
1515                        .index(2)
1516                        .required(true)
1517                        .help("Amount to burn, in tokens; accepts keyword ALL"),
1518                )
1519                .arg(owner_keypair_arg_with_value_name("TOKEN_OWNER_KEYPAIR")
1520                        .help(
1521                            "Specify the burnt token owner account. \
1522                            This may be a keypair file or the ASK keyword. \
1523                            Defaults to the client keypair.",
1524                        ),
1525                )
1526                .arg(multisig_signer_arg())
1527                .mint_args()
1528                .nonce_args(true)
1529                .arg(memo_arg())
1530                .offline_args_config(&SignOnlyNeedsFullMintSpec{}),
1531        )
1532        .subcommand(
1533            SubCommand::with_name(CommandName::Mint.into())
1534                .about("Mint new tokens")
1535                .arg(
1536                    Arg::with_name("token")
1537                        .validator(|s| is_valid_pubkey(s))
1538                        .value_name("TOKEN_MINT_ADDRESS")
1539                        .takes_value(true)
1540                        .index(1)
1541                        .required(true)
1542                        .help("The token to mint"),
1543                )
1544                .arg(
1545                    Arg::with_name("amount")
1546                        .value_parser(Amount::parse)
1547                        .value_name("TOKEN_AMOUNT")
1548                        .takes_value(true)
1549                        .index(2)
1550                        .required(true)
1551                        .help("Amount to mint, in tokens"),
1552                )
1553                .arg(
1554                    Arg::with_name("recipient")
1555                        .validator(|s| is_valid_pubkey(s))
1556                        .value_name("RECIPIENT_TOKEN_ACCOUNT_ADDRESS")
1557                        .takes_value(true)
1558                        .conflicts_with("recipient_owner")
1559                        .index(3)
1560                        .help("The token account address of recipient \
1561                            [default: associated token account for --mint-authority]"),
1562                )
1563                .arg(
1564                    Arg::with_name("recipient_owner")
1565                        .long("recipient-owner")
1566                        .validator(|s| is_valid_pubkey(s))
1567                        .value_name("RECIPIENT_WALLET_ADDRESS")
1568                        .takes_value(true)
1569                        .conflicts_with("recipient")
1570                        .help("The owner of the recipient associated token account"),
1571                )
1572                .arg(
1573                    Arg::with_name("mint_authority")
1574                        .long("mint-authority")
1575                        .alias("owner")
1576                        .value_name("KEYPAIR")
1577                        .validator(|s| is_valid_signer(s))
1578                        .takes_value(true)
1579                        .help(
1580                            "Specify the mint authority keypair. \
1581                             This may be a keypair file or the ASK keyword. \
1582                             Defaults to the client keypair."
1583                        ),
1584                )
1585                .arg(mint_decimals_arg())
1586                .arg(multisig_signer_arg())
1587                .nonce_args(true)
1588                .arg(memo_arg())
1589                .offline_args_config(&SignOnlyNeedsMintDecimals{}),
1590        )
1591        .subcommand(
1592            SubCommand::with_name(CommandName::Freeze.into())
1593                .about("Freeze a token account")
1594                .arg(
1595                    Arg::with_name("account")
1596                        .validator(|s| is_valid_pubkey(s))
1597                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1598                        .takes_value(true)
1599                        .index(1)
1600                        .required(true)
1601                        .help("The address of the token account to freeze"),
1602                )
1603                .arg(
1604                    Arg::with_name("freeze_authority")
1605                        .long("freeze-authority")
1606                        .alias("owner")
1607                        .value_name("KEYPAIR")
1608                        .validator(|s| is_valid_signer(s))
1609                        .takes_value(true)
1610                        .help(
1611                            "Specify the freeze authority keypair. \
1612                             This may be a keypair file or the ASK keyword. \
1613                             Defaults to the client keypair."
1614                        ),
1615                )
1616                .arg(mint_address_arg())
1617                .arg(multisig_signer_arg())
1618                .nonce_args(true)
1619                .offline_args_config(&SignOnlyNeedsMintAddress{}),
1620        )
1621        .subcommand(
1622            SubCommand::with_name(CommandName::Thaw.into())
1623                .about("Thaw a token account")
1624                .arg(
1625                    Arg::with_name("account")
1626                        .validator(|s| is_valid_pubkey(s))
1627                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1628                        .takes_value(true)
1629                        .index(1)
1630                        .required(true)
1631                        .help("The address of the token account to thaw"),
1632                )
1633                .arg(
1634                    Arg::with_name("freeze_authority")
1635                        .long("freeze-authority")
1636                        .alias("owner")
1637                        .value_name("KEYPAIR")
1638                        .validator(|s| is_valid_signer(s))
1639                        .takes_value(true)
1640                        .help(
1641                            "Specify the freeze authority keypair. \
1642                             This may be a keypair file or the ASK keyword. \
1643                             Defaults to the client keypair."
1644                        ),
1645                )
1646                .arg(mint_address_arg())
1647                .arg(multisig_signer_arg())
1648                .nonce_args(true)
1649                .offline_args_config(&SignOnlyNeedsMintAddress{}),
1650        )
1651        .subcommand(
1652            SubCommand::with_name(CommandName::Wrap.into())
1653                .about("Wrap native SOL in a SOL token account")
1654                .arg(
1655                    Arg::with_name("amount")
1656                        .value_parser(Amount::parse)
1657                        .value_name("AMOUNT")
1658                        .takes_value(true)
1659                        .index(1)
1660                        .required(true)
1661                        .help("Amount of SOL to wrap"),
1662                )
1663                .arg(
1664                    Arg::with_name("wallet_keypair")
1665                        .alias("owner")
1666                        .value_name("KEYPAIR")
1667                        .validator(|s| is_valid_signer(s))
1668                        .takes_value(true)
1669                        .index(2)
1670                        .help(
1671                            "Specify the keypair for the wallet which will have its native SOL wrapped. \
1672                             This wallet will be assigned as the owner of the wrapped SOL token account. \
1673                             This may be a keypair file or the ASK keyword. \
1674                             Defaults to the client keypair."
1675                        ),
1676                )
1677                .arg(
1678                    Arg::with_name("create_aux_account")
1679                        .takes_value(false)
1680                        .long("create-aux-account")
1681                        .help("Wrap SOL in an auxiliary account instead of associated token account"),
1682                )
1683                .arg(
1684                    Arg::with_name("immutable")
1685                        .long("immutable")
1686                        .takes_value(false)
1687                        .help(
1688                            "Lock the owner of this token account from ever being changed"
1689                        ),
1690                )
1691                .nonce_args(true)
1692                .offline_args(),
1693        )
1694        .subcommand(
1695            SubCommand::with_name(CommandName::Unwrap.into())
1696                .about("Unwrap a SOL token account")
1697                .arg(
1698                    Arg::with_name("account")
1699                        .validator(|s| is_valid_pubkey(s))
1700                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1701                        .takes_value(true)
1702                        .index(1)
1703                        .help("The address of the auxiliary token account to unwrap \
1704                            [default: associated token account for --owner]"),
1705                )
1706                .arg(
1707                    Arg::with_name("wallet_keypair")
1708                        .value_name("KEYPAIR")
1709                        .validator(|s| is_valid_signer(s))
1710                        .takes_value(true)
1711                        .index(2)
1712                        .help(
1713                            "Specify the keypair for the wallet which owns the wrapped SOL. \
1714                             This wallet will receive the unwrapped SOL. \
1715                             This may be a keypair file or the ASK keyword. \
1716                             Defaults to the client keypair."
1717                        ),
1718                )
1719                .arg(owner_address_arg())
1720                .arg(multisig_signer_arg())
1721                .nonce_args(true)
1722                .offline_args(),
1723        )
1724        .subcommand(
1725            SubCommand::with_name(CommandName::Approve.into())
1726                .about("Approve a delegate for a token account")
1727                .arg(
1728                    Arg::with_name("account")
1729                        .validator(|s| is_valid_pubkey(s))
1730                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1731                        .takes_value(true)
1732                        .index(1)
1733                        .required(true)
1734                        .help("The address of the token account to delegate"),
1735                )
1736                .arg(
1737                    Arg::with_name("amount")
1738                        .value_parser(Amount::parse)
1739                        .value_name("TOKEN_AMOUNT")
1740                        .takes_value(true)
1741                        .index(2)
1742                        .required(true)
1743                        .help("Amount to approve, in tokens"),
1744                )
1745                .arg(
1746                    Arg::with_name("delegate")
1747                        .validator(|s| is_valid_pubkey(s))
1748                        .value_name("DELEGATE_TOKEN_ACCOUNT_ADDRESS")
1749                        .takes_value(true)
1750                        .index(3)
1751                        .required(true)
1752                        .help("The token account address of delegate"),
1753                )
1754                .arg(
1755                    owner_keypair_arg()
1756                )
1757                .arg(multisig_signer_arg())
1758                .mint_args()
1759                .nonce_args(true)
1760                .offline_args_config(&SignOnlyNeedsFullMintSpec{}),
1761        )
1762        .subcommand(
1763            SubCommand::with_name(CommandName::Revoke.into())
1764                .about("Revoke a delegate's authority")
1765                .arg(
1766                    Arg::with_name("account")
1767                        .validator(|s| is_valid_pubkey(s))
1768                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1769                        .takes_value(true)
1770                        .index(1)
1771                        .required(true)
1772                        .help("The address of the token account"),
1773                )
1774                .arg(owner_keypair_arg()
1775                )
1776                .arg(delegate_address_arg())
1777                .arg(multisig_signer_arg())
1778                .nonce_args(true)
1779                .offline_args_config(&SignOnlyNeedsDelegateAddress{}),
1780        )
1781        .subcommand(
1782            SubCommand::with_name(CommandName::Close.into())
1783                .about("Close a token account")
1784                .arg(
1785                    Arg::with_name("token")
1786                        .validator(|s| is_valid_pubkey(s))
1787                        .value_name("TOKEN_MINT_ADDRESS")
1788                        .takes_value(true)
1789                        .index(1)
1790                        .required_unless("address")
1791                        .help("Token of the associated account to close. \
1792                              To close a specific account, use the `--address` parameter instead"),
1793                )
1794                .arg(
1795                    Arg::with_name("recipient")
1796                        .long("recipient")
1797                        .validator(|s| is_valid_pubkey(s))
1798                        .value_name("REFUND_ACCOUNT_ADDRESS")
1799                        .takes_value(true)
1800                        .help("The address of the account to receive remaining SOL [default: --owner]"),
1801                )
1802                .arg(
1803                    Arg::with_name("close_authority")
1804                        .long("close-authority")
1805                        .value_name("KEYPAIR")
1806                        .validator(|s| is_valid_signer(s))
1807                        .takes_value(true)
1808                        .help(
1809                            "Specify the token's close authority if it has one, \
1810                            otherwise specify the token's owner keypair. \
1811                            This may be a keypair file or the ASK keyword. \
1812                            Defaults to the client keypair.",
1813                        ),
1814                )
1815                .arg(
1816                    Arg::with_name("address")
1817                        .long("address")
1818                        .validator(|s| is_valid_pubkey(s))
1819                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1820                        .takes_value(true)
1821                        .conflicts_with("token")
1822                        .help("Specify the token account to close \
1823                            [default: owner's associated token account]"),
1824                )
1825                .arg(owner_address_arg())
1826                .arg(multisig_signer_arg())
1827                .nonce_args(true)
1828                .offline_args(),
1829        )
1830        .subcommand(
1831            SubCommand::with_name(CommandName::CloseMint.into())
1832                .about("Close a token mint")
1833                .arg(
1834                    Arg::with_name("token")
1835                        .validator(|s| is_valid_pubkey(s))
1836                        .value_name("TOKEN_MINT_ADDRESS")
1837                        .takes_value(true)
1838                        .index(1)
1839                        .required(true)
1840                        .help("Token to close"),
1841                )
1842                .arg(
1843                    Arg::with_name("recipient")
1844                        .long("recipient")
1845                        .validator(|s| is_valid_pubkey(s))
1846                        .value_name("REFUND_ACCOUNT_ADDRESS")
1847                        .takes_value(true)
1848                        .help("The address of the account to receive remaining SOL [default: --owner]"),
1849                )
1850                .arg(
1851                    Arg::with_name("close_authority")
1852                        .long("close-authority")
1853                        .value_name("KEYPAIR")
1854                        .validator(|s| is_valid_signer(s))
1855                        .takes_value(true)
1856                        .help(
1857                            "Specify the token's close authority. \
1858                            This may be a keypair file or the ASK keyword. \
1859                            Defaults to the client keypair.",
1860                        ),
1861                )
1862                .arg(owner_address_arg())
1863                .arg(multisig_signer_arg())
1864                .nonce_args(true)
1865                .offline_args(),
1866        )
1867        .subcommand(
1868            SubCommand::with_name(CommandName::Balance.into())
1869                .about("Get token account balance")
1870                .arg(
1871                    Arg::with_name("token")
1872                        .validator(|s| is_valid_pubkey(s))
1873                        .value_name("TOKEN_MINT_ADDRESS")
1874                        .takes_value(true)
1875                        .index(1)
1876                        .required_unless("address")
1877                        .help("Token of associated account. To query a specific account, use the `--address` parameter instead"),
1878                )
1879                .arg(owner_address_arg().conflicts_with("address"))
1880                .arg(
1881                    Arg::with_name("address")
1882                        .validator(|s| is_valid_pubkey(s))
1883                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1884                        .takes_value(true)
1885                        .long("address")
1886                        .conflicts_with("token")
1887                        .help("Specify the token account to query \
1888                            [default: owner's associated token account]"),
1889                ),
1890        )
1891        .subcommand(
1892            SubCommand::with_name(CommandName::Supply.into())
1893                .about("Get token supply")
1894                .arg(
1895                    Arg::with_name("token")
1896                        .validator(|s| is_valid_pubkey(s))
1897                        .value_name("TOKEN_MINT_ADDRESS")
1898                        .takes_value(true)
1899                        .index(1)
1900                        .required(true)
1901                        .help("The token address"),
1902                ),
1903        )
1904        .subcommand(
1905            SubCommand::with_name(CommandName::Accounts.into())
1906                .about("List all token accounts by owner")
1907                .arg(
1908                    Arg::with_name("token")
1909                        .validator(|s| is_valid_pubkey(s))
1910                        .value_name("TOKEN_MINT_ADDRESS")
1911                        .takes_value(true)
1912                        .index(1)
1913                        .help("Limit results to the given token. [Default: list accounts for all tokens]"),
1914                )
1915                .arg(
1916                    Arg::with_name("delegated")
1917                        .long("delegated")
1918                        .takes_value(false)
1919                        .conflicts_with("externally_closeable")
1920                        .help(
1921                            "Limit results to accounts with transfer delegations"
1922                        ),
1923                )
1924                .arg(
1925                    Arg::with_name("externally_closeable")
1926                        .long("externally-closeable")
1927                        .takes_value(false)
1928                        .conflicts_with("delegated")
1929                        .help(
1930                            "Limit results to accounts with external close authorities"
1931                        ),
1932                )
1933                .arg(
1934                    Arg::with_name("addresses_only")
1935                        .long("addresses-only")
1936                        .takes_value(false)
1937                        .conflicts_with("verbose")
1938                        .conflicts_with("output_format")
1939                        .help(
1940                            "Print token account addresses only"
1941                        ),
1942                )
1943                .arg(owner_address_arg())
1944        )
1945        .subcommand(
1946            SubCommand::with_name(CommandName::Address.into())
1947                .about("Get wallet address")
1948                .arg(
1949                    Arg::with_name("token")
1950                        .validator(|s| is_valid_pubkey(s))
1951                        .value_name("TOKEN_MINT_ADDRESS")
1952                        .takes_value(true)
1953                        .long("token")
1954                        .requires("verbose")
1955                        .help("Return the associated token address for the given token. \
1956                               [Default: return the client keypair address]")
1957                )
1958                .arg(
1959                    owner_address_arg()
1960                        .requires("token")
1961                        .help("Return the associated token address for the given owner. \
1962                               [Default: return the associated token address for the client keypair]"),
1963                ),
1964        )
1965        .subcommand(
1966            SubCommand::with_name(CommandName::AccountInfo.into())
1967                .about("Query details of an SPL Token account by address (DEPRECATED: use `spl-token display`)")
1968                .setting(AppSettings::Hidden)
1969                .arg(
1970                    Arg::with_name("token")
1971                        .validator(|s| is_valid_pubkey(s))
1972                        .value_name("TOKEN_MINT_ADDRESS")
1973                        .takes_value(true)
1974                        .index(1)
1975                        .conflicts_with("address")
1976                        .required_unless("address")
1977                        .help("Token of associated account. \
1978                               To query a specific account, use the `--address` parameter instead"),
1979                )
1980                .arg(
1981                    Arg::with_name(OWNER_ADDRESS_ARG.name)
1982                        .takes_value(true)
1983                        .value_name("OWNER_ADDRESS")
1984                        .validator(|s| is_valid_signer(s))
1985                        .help(OWNER_ADDRESS_ARG.help)
1986                        .index(2)
1987                        .conflicts_with("address")
1988                        .help("Owner of the associated account for the specified token. \
1989                               To query a specific account, use the `--address` parameter instead. \
1990                               Defaults to the client keypair."),
1991                )
1992                .arg(
1993                    Arg::with_name("address")
1994                        .validator(|s| is_valid_pubkey(s))
1995                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1996                        .takes_value(true)
1997                        .long("address")
1998                        .conflicts_with("token")
1999                        .help("Specify the token account to query"),
2000                ),
2001        )
2002        .subcommand(
2003            SubCommand::with_name(CommandName::MultisigInfo.into())
2004                .about("Query details of an SPL Token multisig account by address (DEPRECATED: use `spl-token display`)")
2005                .setting(AppSettings::Hidden)
2006                .arg(
2007                    Arg::with_name("address")
2008                    .validator(|s| is_valid_pubkey(s))
2009                    .value_name("MULTISIG_ACCOUNT_ADDRESS")
2010                    .takes_value(true)
2011                    .index(1)
2012                    .required(true)
2013                    .help("The address of the SPL Token multisig account to query"),
2014                ),
2015        )
2016        .subcommand(
2017            SubCommand::with_name(CommandName::Display.into())
2018                .about("Query details of an SPL Token mint, account, or multisig by address")
2019                .arg(
2020                    Arg::with_name("address")
2021                    .validator(|s| is_valid_pubkey(s))
2022                    .value_name("TOKEN_ADDRESS")
2023                    .takes_value(true)
2024                    .index(1)
2025                    .required(true)
2026                    .help("The address of the SPL Token mint, account, or multisig to query"),
2027                ),
2028        )
2029        .subcommand(
2030            SubCommand::with_name(CommandName::Gc.into())
2031                .about("Cleanup unnecessary token accounts")
2032                .arg(owner_keypair_arg())
2033                .arg(
2034                    Arg::with_name("close_empty_associated_accounts")
2035                    .long("close-empty-associated-accounts")
2036                    .takes_value(false)
2037                    .help("close all empty associated token accounts (to get SOL back)")
2038                )
2039        )
2040        .subcommand(
2041            SubCommand::with_name(CommandName::SyncNative.into())
2042                .about("Sync a native SOL token account to its underlying lamports")
2043                .arg(
2044                    owner_address_arg()
2045                        .index(1)
2046                        .conflicts_with("address")
2047                        .help("Owner of the associated account for the native token. \
2048                               To query a specific account, use the `--address` parameter instead. \
2049                               Defaults to the client keypair."),
2050                )
2051                .arg(
2052                    Arg::with_name("address")
2053                        .validator(|s| is_valid_pubkey(s))
2054                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2055                        .takes_value(true)
2056                        .long("address")
2057                        .conflicts_with("owner")
2058                        .help("Specify the specific token account address to sync"),
2059                ),
2060        )
2061        .subcommand(
2062            SubCommand::with_name(CommandName::EnableRequiredTransferMemos.into())
2063                .about("Enable required transfer memos for token account")
2064                .arg(
2065                    Arg::with_name("account")
2066                        .validator(|s| is_valid_pubkey(s))
2067                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2068                        .takes_value(true)
2069                        .index(1)
2070                        .required(true)
2071                        .help("The address of the token account to require transfer memos for")
2072                )
2073                .arg(
2074                    owner_address_arg()
2075                )
2076                .arg(multisig_signer_arg())
2077                .nonce_args(true)
2078        )
2079        .subcommand(
2080            SubCommand::with_name(CommandName::DisableRequiredTransferMemos.into())
2081                .about("Disable required transfer memos for token account")
2082                .arg(
2083                    Arg::with_name("account")
2084                        .validator(|s| is_valid_pubkey(s))
2085                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2086                        .takes_value(true)
2087                        .index(1)
2088                        .required(true)
2089                        .help("The address of the token account to stop requiring transfer memos for"),
2090                )
2091                .arg(
2092                    owner_address_arg()
2093                )
2094                .arg(multisig_signer_arg())
2095                .nonce_args(true)
2096        )
2097        .subcommand(
2098            SubCommand::with_name(CommandName::EnableCpiGuard.into())
2099                .about("Enable CPI Guard for token account")
2100                .arg(
2101                    Arg::with_name("account")
2102                        .validator(|s| is_valid_pubkey(s))
2103                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2104                        .takes_value(true)
2105                        .index(1)
2106                        .required(true)
2107                        .help("The address of the token account to enable CPI Guard for")
2108                )
2109                .arg(
2110                    owner_address_arg()
2111                )
2112                .arg(multisig_signer_arg())
2113                .nonce_args(true)
2114        )
2115        .subcommand(
2116            SubCommand::with_name(CommandName::DisableCpiGuard.into())
2117                .about("Disable CPI Guard for token account")
2118                .arg(
2119                    Arg::with_name("account")
2120                        .validator(|s| is_valid_pubkey(s))
2121                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2122                        .takes_value(true)
2123                        .index(1)
2124                        .required(true)
2125                        .help("The address of the token account to disable CPI Guard for"),
2126                )
2127                .arg(
2128                    owner_address_arg()
2129                )
2130                .arg(multisig_signer_arg())
2131                .nonce_args(true)
2132        )
2133        .subcommand(
2134            SubCommand::with_name(CommandName::UpdateDefaultAccountState.into())
2135                .about("Updates default account state for the mint. Requires the default account state extension.")
2136                .arg(
2137                    Arg::with_name("token")
2138                        .validator(|s| is_valid_pubkey(s))
2139                        .value_name("TOKEN_MINT_ADDRESS")
2140                        .takes_value(true)
2141                        .index(1)
2142                        .required(true)
2143                        .help("The address of the token mint to update default account state"),
2144                )
2145                .arg(
2146                    Arg::with_name("state")
2147                        .value_name("STATE")
2148                        .takes_value(true)
2149                        .possible_values(["initialized", "frozen"])
2150                        .index(2)
2151                        .required(true)
2152                        .help("The new default account state."),
2153                )
2154                .arg(
2155                    Arg::with_name("freeze_authority")
2156                        .long("freeze-authority")
2157                        .value_name("KEYPAIR")
2158                        .validator(|s| is_valid_signer(s))
2159                        .takes_value(true)
2160                        .help(
2161                            "Specify the token's freeze authority. \
2162                            This may be a keypair file or the ASK keyword. \
2163                            Defaults to the client keypair.",
2164                        ),
2165                )
2166                .arg(owner_address_arg())
2167                .arg(multisig_signer_arg())
2168                .nonce_args(true)
2169                .offline_args(),
2170        )
2171        .subcommand(
2172            SubCommand::with_name(CommandName::UpdateMetadataAddress.into())
2173                .about("Updates metadata pointer address for the mint. Requires the metadata pointer extension.")
2174                .arg(
2175                    Arg::with_name("token")
2176                        .validator(|s| is_valid_pubkey(s))
2177                        .value_name("TOKEN_MINT_ADDRESS")
2178                        .takes_value(true)
2179                        .index(1)
2180                        .required(true)
2181                        .help("The address of the token mint to update the metadata pointer address"),
2182                )
2183                .arg(
2184                    Arg::with_name("metadata_address")
2185                        .index(2)
2186                        .validator(|s| is_valid_pubkey(s))
2187                        .value_name("METADATA_ADDRESS")
2188                        .takes_value(true)
2189                        .required_unless("disable")
2190                        .help("Specify address that stores token's metadata-pointer"),
2191                )
2192                .arg(
2193                    Arg::with_name("disable")
2194                        .long("disable")
2195                        .takes_value(false)
2196                        .conflicts_with("metadata_address")
2197                        .help("Unset metadata pointer address.")
2198                )
2199                .arg(
2200                    Arg::with_name("authority")
2201                        .long("authority")
2202                        .value_name("KEYPAIR")
2203                        .validator(|s| is_valid_signer(s))
2204                        .takes_value(true)
2205                        .help(
2206                            "Specify the token's metadata-pointer authority. \
2207                            This may be a keypair file or the ASK keyword. \
2208                            Defaults to the client keypair.",
2209                        ),
2210                )
2211                .arg(multisig_signer_arg())
2212                .nonce_args(true)
2213        )
2214        .subcommand(
2215            SubCommand::with_name(CommandName::UpdateGroupAddress.into())
2216                .about("Updates group pointer address for the mint. Requires the group pointer extension.")
2217                .arg(
2218                    Arg::with_name("token")
2219                        .validator(|s| is_valid_pubkey(s))
2220                        .value_name("TOKEN_MINT_ADDRESS")
2221                        .takes_value(true)
2222                        .index(1)
2223                        .required(true)
2224                        .help("The address of the token mint to update the group pointer address"),
2225                )
2226                .arg(
2227                    Arg::with_name("group_address")
2228                        .index(2)
2229                        .validator(|s| is_valid_pubkey(s))
2230                        .value_name("GROUP_ADDRESS")
2231                        .takes_value(true)
2232                        .required_unless("disable")
2233                        .help("Specify address that stores token's group-pointer"),
2234                )
2235                .arg(
2236                    Arg::with_name("disable")
2237                        .long("disable")
2238                        .takes_value(false)
2239                        .conflicts_with("group_address")
2240                        .help("Unset group pointer address.")
2241                )
2242                .arg(
2243                    Arg::with_name("authority")
2244                        .long("authority")
2245                        .value_name("KEYPAIR")
2246                        .validator(|s| is_valid_signer(s))
2247                        .takes_value(true)
2248                        .help(
2249                            "Specify the token's group-pointer authority. \
2250                            This may be a keypair file or the ASK keyword. \
2251                            Defaults to the client keypair.",
2252                        ),
2253                )
2254                .arg(multisig_signer_arg())
2255                .nonce_args(true)
2256        )
2257        .subcommand(
2258            SubCommand::with_name(CommandName::UpdateMemberAddress.into())
2259                .about("Updates group member pointer address for the mint. Requires the group member pointer extension.")
2260                .arg(
2261                    Arg::with_name("token")
2262                        .validator(|s| is_valid_pubkey(s))
2263                        .value_name("TOKEN_MINT_ADDRESS")
2264                        .takes_value(true)
2265                        .index(1)
2266                        .required(true)
2267                        .help("The address of the token mint to update the group member pointer address"),
2268                )
2269                .arg(
2270                    Arg::with_name("member_address")
2271                        .index(2)
2272                        .validator(|s| is_valid_pubkey(s))
2273                        .value_name("MEMBER_ADDRESS")
2274                        .takes_value(true)
2275                        .required_unless("disable")
2276                        .help("Specify address that stores token's group-member-pointer"),
2277                )
2278                .arg(
2279                    Arg::with_name("disable")
2280                        .long("disable")
2281                        .takes_value(false)
2282                        .conflicts_with("member_address")
2283                        .help("Unset group member pointer address.")
2284                )
2285                .arg(
2286                    Arg::with_name("authority")
2287                        .long("authority")
2288                        .value_name("KEYPAIR")
2289                        .validator(|s| is_valid_signer(s))
2290                        .takes_value(true)
2291                        .help(
2292                            "Specify the token's group-member-pointer authority. \
2293                            This may be a keypair file or the ASK keyword. \
2294                            Defaults to the client keypair.",
2295                        ),
2296                )
2297                .arg(multisig_signer_arg())
2298                .nonce_args(true)
2299        )
2300        .subcommand(
2301            SubCommand::with_name(CommandName::WithdrawWithheldTokens.into())
2302                .about("Withdraw withheld transfer fee tokens from mint and / or account(s)")
2303                .arg(
2304                    Arg::with_name("account")
2305                        .validator(|s| is_valid_pubkey(s))
2306                        .value_name("FEE_RECIPIENT_ADDRESS")
2307                        .takes_value(true)
2308                        .index(1)
2309                        .required(true)
2310                        .help("The token account to send withdrawn fees to."),
2311                )
2312                .arg(
2313                    Arg::with_name("source")
2314                        .validator(|s| is_valid_pubkey(s))
2315                        .value_name("SOURCE_ADDRESS")
2316                        .takes_value(true)
2317                        .multiple(true)
2318                        .min_values(0_usize)
2319                        .index(2)
2320                        .help("The token account(s) to withdraw fees from.")
2321                )
2322                .arg(
2323                    Arg::with_name("include_mint")
2324                        .long("include-mint")
2325                        .takes_value(false)
2326                        .help("Also withdraw withheld tokens from the mint"),
2327                )
2328                .arg(
2329                    Arg::with_name("withdraw_withheld_authority")
2330                        .long("withdraw-withheld-authority")
2331                        .value_name("KEYPAIR")
2332                        .validator(|s| is_valid_signer(s))
2333                        .takes_value(true)
2334                        .help(
2335                            "Specify the withdraw withheld authority keypair. \
2336                             This may be a keypair file or the ASK keyword. \
2337                             Defaults to the client keypair."
2338                        ),
2339                )
2340                .arg(owner_address_arg())
2341                .arg(multisig_signer_arg())
2342                // The account_group ArgGroup ensures <FEE_RECIPIENT_ADDRESS> appears as the first argument in the --help output. Without it, <SOURCE_ADDRESS|--include-mint> would be incorrectly prioritized due to clap’s handling of ArgGroups.
2343                .group(
2344                    ArgGroup::with_name("account_group")
2345                        .arg("account")
2346                        .required(true)
2347                )
2348                .group(
2349                    ArgGroup::with_name("source_or_mint")
2350                        .arg("source")
2351                        .arg("include_mint")
2352                        .multiple(true)
2353                        .required(true)
2354                )
2355        )
2356        .subcommand(
2357            SubCommand::with_name(CommandName::SetTransferFee.into())
2358                .about("Set the transfer fee for a token with a configured transfer fee")
2359                .arg(
2360                    Arg::with_name("token")
2361                        .validator(|s| is_valid_pubkey(s))
2362                        .value_name("TOKEN_MINT_ADDRESS")
2363                        .takes_value(true)
2364                        .required(true)
2365                        .help("The interest-bearing token address"),
2366                )
2367                .arg(
2368                    Arg::with_name("transfer_fee_basis_points")
2369                        .value_name("FEE_IN_BASIS_POINTS")
2370                        .takes_value(true)
2371                        .required(true)
2372                        .help("The new transfer fee in basis points"),
2373                )
2374                .arg(
2375                    Arg::with_name("maximum_fee")
2376                        .value_name("MAXIMUM_FEE")
2377                        .value_parser(Amount::parse)
2378                        .takes_value(true)
2379                        .required(true)
2380                        .help("The new maximum transfer fee in UI amount"),
2381                )
2382                .arg(
2383                    Arg::with_name("transfer_fee_authority")
2384                    .long("transfer-fee-authority")
2385                    .validator(|s| is_valid_signer(s))
2386                    .value_name("SIGNER")
2387                    .takes_value(true)
2388                    .help(
2389                        "Specify the rate authority keypair. \
2390                        Defaults to the client keypair address."
2391                    )
2392                )
2393                .arg(mint_decimals_arg())
2394                .offline_args_config(&SignOnlyNeedsMintDecimals{})
2395        )
2396        .subcommand(
2397            SubCommand::with_name(CommandName::WithdrawExcessLamports.into())
2398                .about("Withdraw lamports from a Token Program owned account")
2399                .arg(
2400                    Arg::with_name("from")
2401                        .validator(|s| is_valid_pubkey(s))
2402                        .value_name("SOURCE_ACCOUNT_ADDRESS")
2403                        .takes_value(true)
2404                        .required(true)
2405                        .help("Specify the address of the account to recover lamports from"),
2406                )
2407                .arg(
2408                    Arg::with_name("recipient")
2409                        .validator(|s| is_valid_pubkey(s))
2410                        .value_name("REFUND_ACCOUNT_ADDRESS")
2411                        .takes_value(true)
2412                        .required(true)
2413                        .help("Specify the address of the account to send lamports to"),
2414                )
2415                .arg(owner_address_arg())
2416                .arg(multisig_signer_arg())
2417        )
2418        .subcommand(
2419            SubCommand::with_name(CommandName::UpdateConfidentialTransferSettings.into())
2420                .about("Update confidential transfer configuration for a token")
2421                .arg(
2422                    Arg::with_name("token")
2423                        .validator(|s| is_valid_pubkey(s))
2424                        .value_name("TOKEN_MINT_ADDRESS")
2425                        .takes_value(true)
2426                        .index(1)
2427                        .required(true)
2428                        .help("The address of the token mint to update confidential transfer configuration for")
2429                )
2430                .arg(
2431                    Arg::with_name("approve_policy")
2432                        .long("approve-policy")
2433                        .value_name("APPROVE_POLICY")
2434                        .takes_value(true)
2435                        .possible_values(["auto", "manual"])
2436                        .help(
2437                            "Policy for enabling accounts to make confidential transfers. If \"auto\" \
2438                            is selected, then accounts are automatically approved to make \
2439                            confidential transfers. If \"manual\" is selected, then the \
2440                            confidential transfer mint authority must approve each account \
2441                            before it can make confidential transfers."
2442                        )
2443                )
2444                .arg(
2445                    Arg::with_name("auditor_pubkey")
2446                        .long("auditor-pubkey")
2447                        .value_name("AUDITOR_PUBKEY")
2448                        .takes_value(true)
2449                        .help(
2450                            "The auditor encryption public key. The corresponding private key for \
2451                            this auditor public key can be used to decrypt all confidential \
2452                            transfers involving tokens from this mint. Currently, the auditor \
2453                            public key can only be specified as a direct *base64* encoding of \
2454                            an ElGamal public key. More methods of specifying the auditor public \
2455                            key will be supported in a future version. To disable auditability \
2456                            feature for the token, use \"none\"."
2457                        )
2458                )
2459                .group(
2460                    ArgGroup::with_name("update_fields").args(&["approve_policy", "auditor_pubkey"])
2461                        .required(true)
2462                        .multiple(true)
2463                )
2464                .arg(
2465                    Arg::with_name("confidential_transfer_authority")
2466                        .long("confidential-transfer-authority")
2467                        .validator(|s| is_valid_signer(s))
2468                        .value_name("SIGNER")
2469                        .takes_value(true)
2470                        .help(
2471                            "Specify the confidential transfer authority keypair. \
2472                            Defaults to the client keypair address."
2473                        )
2474                )
2475                .nonce_args(true)
2476                .offline_args(),
2477        )
2478        .subcommand(
2479            SubCommand::with_name(CommandName::ConfigureConfidentialTransferAccount.into())
2480                .about("Configure confidential transfers for token account")
2481                .arg(
2482                    Arg::with_name("token")
2483                        .validator(|s| is_valid_pubkey(s))
2484                        .value_name("TOKEN_MINT_ADDRESS")
2485                        .takes_value(true)
2486                        .index(1)
2487                        .required_unless("address")
2488                        .help("The token address with confidential transfers enabled"),
2489                )
2490                .arg(
2491                    Arg::with_name("address")
2492                        .long("address")
2493                        .validator(|s| is_valid_pubkey(s))
2494                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2495                        .takes_value(true)
2496                        .conflicts_with("token")
2497                        .help("The address of the token account to configure confidential transfers for \
2498                            [default: owner's associated token account]")
2499                )
2500                .arg(
2501                    owner_address_arg()
2502                )
2503                .arg(
2504                    Arg::with_name("maximum_pending_balance_credit_counter")
2505                        .long("maximum-pending-balance-credit-counter")
2506                        .value_name("MAXIMUM-CREDIT-COUNTER")
2507                        .takes_value(true)
2508                        .help(
2509                            "The maximum pending balance credit counter. \
2510                            This parameter limits the number of confidential transfers that a token account \
2511                            can receive to facilitate decryption of the encrypted balance. \
2512                            Defaults to 65536 (2^16)"
2513                        )
2514                )
2515                .arg(multisig_signer_arg())
2516                .nonce_args(true)
2517        )
2518        .subcommand(
2519            SubCommand::with_name(CommandName::EnableConfidentialCredits.into())
2520                .about("Enable confidential transfers for token account. To enable confidential transfers \
2521                for the first time, use `configure-confidential-transfer-account` instead.")
2522                .arg(
2523                    Arg::with_name("token")
2524                        .validator(|s| is_valid_pubkey(s))
2525                        .value_name("TOKEN_MINT_ADDRESS")
2526                        .takes_value(true)
2527                        .index(1)
2528                        .required_unless("address")
2529                        .help("The token address with confidential transfers enabled"),
2530                )
2531                .arg(
2532                    Arg::with_name("address")
2533                        .long("address")
2534                        .validator(|s| is_valid_pubkey(s))
2535                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2536                        .takes_value(true)
2537                        .conflicts_with("token")
2538                        .help("The address of the token account to enable confidential transfers for \
2539                            [default: owner's associated token account]")
2540                )
2541                .arg(
2542                    owner_address_arg()
2543                )
2544                .arg(multisig_signer_arg())
2545                .nonce_args(true)
2546        )
2547        .subcommand(
2548            SubCommand::with_name(CommandName::DisableConfidentialCredits.into())
2549                .about("Disable confidential transfers for token account")
2550                .arg(
2551                    Arg::with_name("token")
2552                        .validator(|s| is_valid_pubkey(s))
2553                        .value_name("TOKEN_MINT_ADDRESS")
2554                        .takes_value(true)
2555                        .index(1)
2556                        .required_unless("address")
2557                        .help("The token address with confidential transfers enabled"),
2558                )
2559                .arg(
2560                    Arg::with_name("address")
2561                        .long("address")
2562                        .validator(|s| is_valid_pubkey(s))
2563                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2564                        .takes_value(true)
2565                        .conflicts_with("token")
2566                        .help("The address of the token account to disable confidential transfers for \
2567                            [default: owner's associated token account]")
2568                )
2569                .arg(
2570                    owner_address_arg()
2571                )
2572                .arg(multisig_signer_arg())
2573                .nonce_args(true)
2574        )
2575        .subcommand(
2576            SubCommand::with_name(CommandName::EnableNonConfidentialCredits.into())
2577                .about("Enable non-confidential transfers for token account.")
2578                .arg(
2579                    Arg::with_name("token")
2580                        .validator(|s| is_valid_pubkey(s))
2581                        .value_name("TOKEN_MINT_ADDRESS")
2582                        .takes_value(true)
2583                        .index(1)
2584                        .required_unless("address")
2585                        .help("The token address with confidential transfers enabled"),
2586                )
2587                .arg(
2588                    Arg::with_name("address")
2589                        .long("address")
2590                        .validator(|s| is_valid_pubkey(s))
2591                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2592                        .takes_value(true)
2593                        .conflicts_with("token")
2594                        .help("The address of the token account to enable non-confidential transfers for \
2595                            [default: owner's associated token account]")
2596                )
2597                .arg(
2598                    owner_address_arg()
2599                )
2600                .arg(multisig_signer_arg())
2601                .nonce_args(true)
2602        )
2603        .subcommand(
2604            SubCommand::with_name(CommandName::DisableNonConfidentialCredits.into())
2605                .about("Disable non-confidential transfers for token account")
2606                .arg(
2607                    Arg::with_name("token")
2608                        .validator(|s| is_valid_pubkey(s))
2609                        .value_name("TOKEN_MINT_ADDRESS")
2610                        .takes_value(true)
2611                        .index(1)
2612                        .required_unless("address")
2613                        .help("The token address with confidential transfers enabled"),
2614                )
2615                .arg(
2616                    Arg::with_name("address")
2617                        .long("address")
2618                        .validator(|s| is_valid_pubkey(s))
2619                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2620                        .takes_value(true)
2621                        .conflicts_with("token")
2622                        .help("The address of the token account to disable non-confidential transfers for \
2623                            [default: owner's associated token account]")
2624                )
2625                .arg(
2626                    owner_address_arg()
2627                )
2628                .arg(multisig_signer_arg())
2629                .nonce_args(true)
2630        )
2631        .subcommand(
2632            SubCommand::with_name(CommandName::DepositConfidentialTokens.into())
2633                .about("Deposit amounts for confidential transfers")
2634                .arg(
2635                    Arg::with_name("token")
2636                        .validator(|s| is_valid_pubkey(s))
2637                        .value_name("TOKEN_MINT_ADDRESS")
2638                        .takes_value(true)
2639                        .index(1)
2640                        .required(true)
2641                        .help("The token address with confidential transfers enabled"),
2642                )
2643                .arg(
2644                    Arg::with_name("amount")
2645                        .value_parser(Amount::parse)
2646                        .value_name("TOKEN_AMOUNT")
2647                        .takes_value(true)
2648                        .index(2)
2649                        .required(true)
2650                        .help("Amount to deposit; accepts keyword ALL"),
2651                )
2652                .arg(
2653                    Arg::with_name("address")
2654                        .long("address")
2655                        .validator(|s| is_valid_pubkey(s))
2656                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2657                        .takes_value(true)
2658                        .help("The address of the token account to configure confidential transfers for \
2659                            [default: owner's associated token account]")
2660                )
2661                .arg(
2662                    owner_address_arg()
2663                )
2664                .arg(multisig_signer_arg())
2665                .arg(mint_decimals_arg())
2666                .nonce_args(true)
2667        )
2668        .subcommand(
2669            SubCommand::with_name(CommandName::WithdrawConfidentialTokens.into())
2670                .about("Withdraw amounts for confidential transfers")
2671                .arg(
2672                    Arg::with_name("token")
2673                        .validator(|s| is_valid_pubkey(s))
2674                        .value_name("TOKEN_MINT_ADDRESS")
2675                        .takes_value(true)
2676                        .index(1)
2677                        .required(true)
2678                        .help("The token address with confidential transfers enabled"),
2679                )
2680                .arg(
2681                    Arg::with_name("amount")
2682                        .value_parser(Amount::parse)
2683                        .value_name("TOKEN_AMOUNT")
2684                        .takes_value(true)
2685                        .index(2)
2686                        .required(true)
2687                        .help("Amount to deposit; accepts keyword ALL"),
2688                )
2689                .arg(
2690                    Arg::with_name("address")
2691                        .long("address")
2692                        .validator(|s| is_valid_pubkey(s))
2693                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2694                        .takes_value(true)
2695                        .help("The address of the token account to configure confidential transfers for \
2696                            [default: owner's associated token account]")
2697                )
2698                .arg(
2699                    owner_address_arg()
2700                )
2701                .arg(multisig_signer_arg())
2702                .arg(mint_decimals_arg())
2703                .nonce_args(true)
2704        )
2705        .subcommand(
2706            SubCommand::with_name(CommandName::ApplyPendingBalance.into())
2707                .about("Collect confidential tokens from pending to available balance")
2708                .arg(
2709                    Arg::with_name("token")
2710                        .validator(|s| is_valid_pubkey(s))
2711                        .value_name("TOKEN_MINT_ADDRESS")
2712                        .takes_value(true)
2713                        .index(1)
2714                        .required_unless("address")
2715                        .help("The token address with confidential transfers enabled"),
2716                )
2717                .arg(
2718                    Arg::with_name("address")
2719                        .long("address")
2720                        .validator(|s| is_valid_pubkey(s))
2721                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2722                        .takes_value(true)
2723                        .help("The address of the token account to configure confidential transfers for \
2724                            [default: owner's associated token account]")
2725                )
2726                .arg(
2727                    owner_address_arg()
2728                )
2729                .arg(multisig_signer_arg())
2730                .nonce_args(true)
2731        )
2732        .subcommand(
2733            SubCommand::with_name(CommandName::UpdateUiAmountMultiplier.into())
2734                .about("Update UI multiplier")
2735                .arg(
2736                    Arg::with_name("token")
2737                        .validator(|s| is_valid_pubkey(s))
2738                        .value_name("TOKEN_MINT_ADDRESS")
2739                        .takes_value(true)
2740                        .index(1)
2741                        .required(true)
2742                        .help("The token address with scaled UI amount multiplier"),
2743                )
2744                .arg(
2745                    Arg::with_name("multiplier")
2746                        .value_name("MULTIPLIER")
2747                        .takes_value(true)
2748                        .index(2)
2749                        .required(true)
2750                        .help("The new multiplier"),
2751                )
2752                .arg(
2753                    Arg::with_name("timestamp")
2754                        .value_name("TIMESTAMP")
2755                        .takes_value(true)
2756                        .index(3)
2757                        .help("The effective time for the new multiplier, given as a UNIX timestamp \
2758                            [default: current time]",)
2759                )
2760                .arg(
2761                    Arg::with_name("ui_multiplier_authority")
2762                    .long("ui-multiplier-authority")
2763                    .alias("owner")
2764                    .validator(|s| is_valid_signer(s))
2765                    .value_name("SIGNER")
2766                    .takes_value(true)
2767                    .help(
2768                        "Specify the multiplier authority keypair. \
2769                        Defaults to the client keypair address."
2770                    )
2771                )
2772                .arg(multisig_signer_arg())
2773                .nonce_args(true)
2774        )
2775        .subcommand(
2776            SubCommand::with_name(CommandName::Pause.into())
2777                .about("Pause mint, burn, and transfer")
2778                .arg(
2779                    Arg::with_name("token")
2780                        .validator(|s| is_valid_pubkey(s))
2781                        .value_name("TOKEN_MINT_ADDRESS")
2782                        .takes_value(true)
2783                        .index(1)
2784                        .required(true)
2785                        .help("The pausable token address"),
2786                )
2787                .arg(
2788                    Arg::with_name("pause_authority")
2789                    .long("pause-authority")
2790                    .alias("owner")
2791                    .validator(|s| is_valid_signer(s))
2792                    .value_name("SIGNER")
2793                    .takes_value(true)
2794                    .help(
2795                        "Specify the pause authority keypair. \
2796                        Defaults to the client keypair address."
2797                    )
2798                )
2799                .arg(multisig_signer_arg())
2800                .nonce_args(true)
2801        )
2802        .subcommand(
2803            SubCommand::with_name(CommandName::Resume.into())
2804                .about("Resume mint, burn, and transfer")
2805                .arg(
2806                    Arg::with_name("token")
2807                        .validator(|s| is_valid_pubkey(s))
2808                        .value_name("TOKEN_MINT_ADDRESS")
2809                        .takes_value(true)
2810                        .index(1)
2811                        .required(true)
2812                        .help("The pausable token address"),
2813                )
2814                .arg(
2815                    Arg::with_name("pause_authority")
2816                    .long("pause-authority")
2817                    .alias("owner")
2818                    .validator(|s| is_valid_signer(s))
2819                    .value_name("SIGNER")
2820                    .takes_value(true)
2821                    .help(
2822                        "Specify the pause authority keypair. \
2823                        Defaults to the client keypair address."
2824                    )
2825                )
2826                .arg(multisig_signer_arg())
2827                .nonce_args(true)
2828        )
2829}