Skip to main content

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