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(
1545                    Arg::with_name("permissioned_burn_authority")
1546                        .long("permissioned-burn-authority")
1547                        .value_name("KEYPAIR")
1548                        .validator(|s| is_valid_signer(s))
1549                        .takes_value(true)
1550                        .help(
1551                            "Specify the permissioned burn authority keypair. \
1552                             This may be a keypair file or the ASK keyword."
1553                        ),
1554                )
1555                .arg(multisig_signer_arg())
1556                .mint_args()
1557                .nonce_args(true)
1558                .arg(memo_arg())
1559                .offline_args_config(&SignOnlyNeedsFullMintSpec{}),
1560        )
1561        .subcommand(
1562            SubCommand::with_name(CommandName::Mint.into())
1563                .about("Mint new tokens")
1564                .arg(
1565                    Arg::with_name("token")
1566                        .validator(|s| is_valid_pubkey(s))
1567                        .value_name("TOKEN_MINT_ADDRESS")
1568                        .takes_value(true)
1569                        .index(1)
1570                        .required(true)
1571                        .help("The token to mint"),
1572                )
1573                .arg(
1574                    Arg::with_name("amount")
1575                        .value_parser(Amount::parse)
1576                        .value_name("TOKEN_AMOUNT")
1577                        .takes_value(true)
1578                        .index(2)
1579                        .required(true)
1580                        .help("Amount to mint, in tokens"),
1581                )
1582                .arg(
1583                    Arg::with_name("recipient")
1584                        .validator(|s| is_valid_pubkey(s))
1585                        .value_name("RECIPIENT_TOKEN_ACCOUNT_ADDRESS")
1586                        .takes_value(true)
1587                        .conflicts_with("recipient_owner")
1588                        .index(3)
1589                        .help("The token account address of recipient \
1590                            [default: associated token account for --mint-authority]"),
1591                )
1592                .arg(
1593                    Arg::with_name("recipient_owner")
1594                        .long("recipient-owner")
1595                        .validator(|s| is_valid_pubkey(s))
1596                        .value_name("RECIPIENT_WALLET_ADDRESS")
1597                        .takes_value(true)
1598                        .conflicts_with("recipient")
1599                        .help("The owner of the recipient associated token account"),
1600                )
1601                .arg(
1602                    Arg::with_name("mint_authority")
1603                        .long("mint-authority")
1604                        .alias("owner")
1605                        .value_name("KEYPAIR")
1606                        .validator(|s| is_valid_signer(s))
1607                        .takes_value(true)
1608                        .help(
1609                            "Specify the mint authority keypair. \
1610                             This may be a keypair file or the ASK keyword. \
1611                             Defaults to the client keypair."
1612                        ),
1613                )
1614                .arg(mint_decimals_arg())
1615                .arg(multisig_signer_arg())
1616                .nonce_args(true)
1617                .arg(memo_arg())
1618                .offline_args_config(&SignOnlyNeedsMintDecimals{}),
1619        )
1620        .subcommand(
1621            SubCommand::with_name(CommandName::Freeze.into())
1622                .about("Freeze a token account")
1623                .arg(
1624                    Arg::with_name("account")
1625                        .validator(|s| is_valid_pubkey(s))
1626                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1627                        .takes_value(true)
1628                        .index(1)
1629                        .required(true)
1630                        .help("The address of the token account to freeze"),
1631                )
1632                .arg(
1633                    Arg::with_name("freeze_authority")
1634                        .long("freeze-authority")
1635                        .alias("owner")
1636                        .value_name("KEYPAIR")
1637                        .validator(|s| is_valid_signer(s))
1638                        .takes_value(true)
1639                        .help(
1640                            "Specify the freeze authority keypair. \
1641                             This may be a keypair file or the ASK keyword. \
1642                             Defaults to the client keypair."
1643                        ),
1644                )
1645                .arg(mint_address_arg())
1646                .arg(multisig_signer_arg())
1647                .nonce_args(true)
1648                .offline_args_config(&SignOnlyNeedsMintAddress{}),
1649        )
1650        .subcommand(
1651            SubCommand::with_name(CommandName::Thaw.into())
1652                .about("Thaw a token account")
1653                .arg(
1654                    Arg::with_name("account")
1655                        .validator(|s| is_valid_pubkey(s))
1656                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1657                        .takes_value(true)
1658                        .index(1)
1659                        .required(true)
1660                        .help("The address of the token account to thaw"),
1661                )
1662                .arg(
1663                    Arg::with_name("freeze_authority")
1664                        .long("freeze-authority")
1665                        .alias("owner")
1666                        .value_name("KEYPAIR")
1667                        .validator(|s| is_valid_signer(s))
1668                        .takes_value(true)
1669                        .help(
1670                            "Specify the freeze authority keypair. \
1671                             This may be a keypair file or the ASK keyword. \
1672                             Defaults to the client keypair."
1673                        ),
1674                )
1675                .arg(mint_address_arg())
1676                .arg(multisig_signer_arg())
1677                .nonce_args(true)
1678                .offline_args_config(&SignOnlyNeedsMintAddress{}),
1679        )
1680        .subcommand(
1681            SubCommand::with_name(CommandName::Wrap.into())
1682                .about("Wrap native SOL in a SOL token account")
1683                .arg(
1684                    Arg::with_name("amount")
1685                        .value_parser(Amount::parse)
1686                        .value_name("AMOUNT")
1687                        .takes_value(true)
1688                        .index(1)
1689                        .required(true)
1690                        .help("Amount of SOL to wrap"),
1691                )
1692                .arg(
1693                    Arg::with_name("wallet_keypair")
1694                        .alias("owner")
1695                        .value_name("KEYPAIR")
1696                        .validator(|s| is_valid_signer(s))
1697                        .takes_value(true)
1698                        .index(2)
1699                        .help(
1700                            "Specify the keypair for the wallet which will have its native SOL wrapped. \
1701                             This wallet will be assigned as the owner of the wrapped SOL token account. \
1702                             This may be a keypair file or the ASK keyword. \
1703                             Defaults to the client keypair."
1704                        ),
1705                )
1706                .arg(
1707                    Arg::with_name("create_aux_account")
1708                        .takes_value(false)
1709                        .long("create-aux-account")
1710                        .help("Wrap SOL in an auxiliary account instead of associated token account"),
1711                )
1712                .arg(
1713                    Arg::with_name("immutable")
1714                        .long("immutable")
1715                        .takes_value(false)
1716                        .help(
1717                            "Lock the owner of this token account from ever being changed"
1718                        ),
1719                )
1720                .nonce_args(true)
1721                .offline_args(),
1722        )
1723        .subcommand(
1724            SubCommand::with_name(CommandName::Unwrap.into())
1725                .about("Unwrap a SOL token account")
1726                .arg(
1727                    Arg::with_name("account")
1728                        .validator(|s| is_valid_pubkey(s))
1729                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1730                        .takes_value(true)
1731                        .index(1)
1732                        .help("The address of the auxiliary token account to unwrap \
1733                            [default: associated token account for --owner]"),
1734                )
1735                .arg(
1736                    Arg::with_name("wallet_keypair")
1737                        .value_name("KEYPAIR")
1738                        .validator(|s| is_valid_signer(s))
1739                        .takes_value(true)
1740                        .index(2)
1741                        .help(
1742                            "Specify the keypair for the wallet which owns the wrapped SOL. \
1743                             This wallet will receive the unwrapped SOL. \
1744                             This may be a keypair file or the ASK keyword. \
1745                             Defaults to the client keypair."
1746                        ),
1747                )
1748                .arg(owner_address_arg())
1749                .arg(multisig_signer_arg())
1750                .nonce_args(true)
1751                .offline_args(),
1752        )
1753        .subcommand(
1754            SubCommand::with_name(CommandName::UnwrapSol.into())
1755                .about("Unwrap SOL from a wrapped SOL token account")
1756                .arg(
1757                    Arg::with_name("amount")
1758                        .value_parser(Amount::parse)
1759                        .value_name("TOKEN_AMOUNT")
1760                        .takes_value(true)
1761                        .index(1)
1762                        .required(true)
1763                        .help("Amount to unwrap, in SOL; accepts keyword ALL"),
1764                )
1765                .arg(
1766                    Arg::with_name("recipient")
1767                        .validator(|s| is_valid_pubkey(s))
1768                        .value_name("RECIPIENT_ACCOUNT_ADDRESS")
1769                        .takes_value(true)
1770                        .index(2)
1771                        .help("Specify the address to recieve the unwrapped SOL. \
1772                               Defaults to the owner address.")
1773                )
1774                .arg(
1775                    Arg::with_name("from")
1776                        .validator(|s| is_valid_pubkey(s))
1777                        .value_name("NATIVE_TOKEN_ACCOUNT_ADDRESS")
1778                        .takes_value(true)
1779                        .long("from")
1780                        .help("Specify the token account that contains the wrapped SOL. \
1781                            [default: owner's associated token account]")
1782                )
1783                .arg(owner_keypair_arg_with_value_name("NATIVE_TOKEN_OWNER_KEYPAIR")
1784                        .help(
1785                            "Specify the keypair for the wallet which owns the wrapped SOL. \
1786                             This may be a keypair file or the ASK keyword. \
1787                             Defaults to the client keypair.",
1788                        ),
1789                )
1790                .arg(
1791                    Arg::with_name("allow_unfunded_recipient")
1792                        .long("allow-unfunded-recipient")
1793                        .takes_value(false)
1794                        .help("Complete the transfer even if the recipient address is not funded")
1795                )
1796                .arg(multisig_signer_arg())
1797                .nonce_args(true)
1798                .offline_args(),
1799        )
1800        .subcommand(
1801            SubCommand::with_name(CommandName::Approve.into())
1802                .about("Approve a delegate for a token account")
1803                .arg(
1804                    Arg::with_name("account")
1805                        .validator(|s| is_valid_pubkey(s))
1806                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1807                        .takes_value(true)
1808                        .index(1)
1809                        .required(true)
1810                        .help("The address of the token account to delegate"),
1811                )
1812                .arg(
1813                    Arg::with_name("amount")
1814                        .value_parser(Amount::parse)
1815                        .value_name("TOKEN_AMOUNT")
1816                        .takes_value(true)
1817                        .index(2)
1818                        .required(true)
1819                        .help("Amount to approve, in tokens"),
1820                )
1821                .arg(
1822                    Arg::with_name("delegate")
1823                        .validator(|s| is_valid_pubkey(s))
1824                        .value_name("DELEGATE_TOKEN_ACCOUNT_ADDRESS")
1825                        .takes_value(true)
1826                        .index(3)
1827                        .required(true)
1828                        .help("The token account address of delegate"),
1829                )
1830                .arg(
1831                    owner_keypair_arg()
1832                )
1833                .arg(multisig_signer_arg())
1834                .mint_args()
1835                .nonce_args(true)
1836                .offline_args_config(&SignOnlyNeedsFullMintSpec{}),
1837        )
1838        .subcommand(
1839            SubCommand::with_name(CommandName::Revoke.into())
1840                .about("Revoke a delegate's authority")
1841                .arg(
1842                    Arg::with_name("account")
1843                        .validator(|s| is_valid_pubkey(s))
1844                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1845                        .takes_value(true)
1846                        .index(1)
1847                        .required(true)
1848                        .help("The address of the token account"),
1849                )
1850                .arg(owner_keypair_arg()
1851                )
1852                .arg(delegate_address_arg())
1853                .arg(multisig_signer_arg())
1854                .nonce_args(true)
1855                .offline_args_config(&SignOnlyNeedsDelegateAddress{}),
1856        )
1857        .subcommand(
1858            SubCommand::with_name(CommandName::Close.into())
1859                .about("Close a token account")
1860                .arg(
1861                    Arg::with_name("token")
1862                        .validator(|s| is_valid_pubkey(s))
1863                        .value_name("TOKEN_MINT_ADDRESS")
1864                        .takes_value(true)
1865                        .index(1)
1866                        .required_unless("address")
1867                        .help("Token of the associated account to close. \
1868                              To close a specific account, use the `--address` parameter instead"),
1869                )
1870                .arg(
1871                    Arg::with_name("recipient")
1872                        .long("recipient")
1873                        .validator(|s| is_valid_pubkey(s))
1874                        .value_name("REFUND_ACCOUNT_ADDRESS")
1875                        .takes_value(true)
1876                        .help("The address of the account to receive remaining SOL [default: --owner]"),
1877                )
1878                .arg(
1879                    Arg::with_name("close_authority")
1880                        .long("close-authority")
1881                        .value_name("KEYPAIR")
1882                        .validator(|s| is_valid_signer(s))
1883                        .takes_value(true)
1884                        .help(
1885                            "Specify the token's close authority if it has one, \
1886                            otherwise specify the token's owner keypair. \
1887                            This may be a keypair file or the ASK keyword. \
1888                            Defaults to the client keypair.",
1889                        ),
1890                )
1891                .arg(
1892                    Arg::with_name("address")
1893                        .long("address")
1894                        .validator(|s| is_valid_pubkey(s))
1895                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1896                        .takes_value(true)
1897                        .conflicts_with("token")
1898                        .help("Specify the token account to close \
1899                            [default: owner's associated token account]"),
1900                )
1901                .arg(owner_address_arg())
1902                .arg(multisig_signer_arg())
1903                .nonce_args(true)
1904                .offline_args(),
1905        )
1906        .subcommand(
1907            SubCommand::with_name(CommandName::CloseMint.into())
1908                .about("Close a token mint")
1909                .arg(
1910                    Arg::with_name("token")
1911                        .validator(|s| is_valid_pubkey(s))
1912                        .value_name("TOKEN_MINT_ADDRESS")
1913                        .takes_value(true)
1914                        .index(1)
1915                        .required(true)
1916                        .help("Token to close"),
1917                )
1918                .arg(
1919                    Arg::with_name("recipient")
1920                        .long("recipient")
1921                        .validator(|s| is_valid_pubkey(s))
1922                        .value_name("REFUND_ACCOUNT_ADDRESS")
1923                        .takes_value(true)
1924                        .help("The address of the account to receive remaining SOL [default: --owner]"),
1925                )
1926                .arg(
1927                    Arg::with_name("close_authority")
1928                        .long("close-authority")
1929                        .value_name("KEYPAIR")
1930                        .validator(|s| is_valid_signer(s))
1931                        .takes_value(true)
1932                        .help(
1933                            "Specify the token's close authority. \
1934                            This may be a keypair file or the ASK keyword. \
1935                            Defaults to the client keypair.",
1936                        ),
1937                )
1938                .arg(owner_address_arg())
1939                .arg(multisig_signer_arg())
1940                .nonce_args(true)
1941                .offline_args(),
1942        )
1943        .subcommand(
1944            SubCommand::with_name(CommandName::Balance.into())
1945                .about("Get token account balance")
1946                .arg(
1947                    Arg::with_name("token")
1948                        .validator(|s| is_valid_pubkey(s))
1949                        .value_name("TOKEN_MINT_ADDRESS")
1950                        .takes_value(true)
1951                        .index(1)
1952                        .required_unless("address")
1953                        .help("Token of associated account. To query a specific account, use the `--address` parameter instead"),
1954                )
1955                .arg(owner_address_arg().conflicts_with("address"))
1956                .arg(
1957                    Arg::with_name("address")
1958                        .validator(|s| is_valid_pubkey(s))
1959                        .value_name("TOKEN_ACCOUNT_ADDRESS")
1960                        .takes_value(true)
1961                        .long("address")
1962                        .conflicts_with("token")
1963                        .help("Specify the token account to query \
1964                            [default: owner's associated token account]"),
1965                ),
1966        )
1967        .subcommand(
1968            SubCommand::with_name(CommandName::Supply.into())
1969                .about("Get token supply")
1970                .arg(
1971                    Arg::with_name("token")
1972                        .validator(|s| is_valid_pubkey(s))
1973                        .value_name("TOKEN_MINT_ADDRESS")
1974                        .takes_value(true)
1975                        .index(1)
1976                        .required(true)
1977                        .help("The token address"),
1978                ),
1979        )
1980        .subcommand(
1981            SubCommand::with_name(CommandName::Accounts.into())
1982                .about("List all token accounts by owner")
1983                .arg(
1984                    Arg::with_name("token")
1985                        .validator(|s| is_valid_pubkey(s))
1986                        .value_name("TOKEN_MINT_ADDRESS")
1987                        .takes_value(true)
1988                        .index(1)
1989                        .help("Limit results to the given token. [Default: list accounts for all tokens]"),
1990                )
1991                .arg(
1992                    Arg::with_name("delegated")
1993                        .long("delegated")
1994                        .takes_value(false)
1995                        .conflicts_with("externally_closeable")
1996                        .help(
1997                            "Limit results to accounts with transfer delegations"
1998                        ),
1999                )
2000                .arg(
2001                    Arg::with_name("externally_closeable")
2002                        .long("externally-closeable")
2003                        .takes_value(false)
2004                        .conflicts_with("delegated")
2005                        .help(
2006                            "Limit results to accounts with external close authorities"
2007                        ),
2008                )
2009                .arg(
2010                    Arg::with_name("addresses_only")
2011                        .long("addresses-only")
2012                        .takes_value(false)
2013                        .conflicts_with("verbose")
2014                        .conflicts_with("output_format")
2015                        .help(
2016                            "Print token account addresses only"
2017                        ),
2018                )
2019                .arg(owner_address_arg())
2020        )
2021        .subcommand(
2022            SubCommand::with_name(CommandName::Address.into())
2023                .about("Get wallet address")
2024                .arg(
2025                    Arg::with_name("token")
2026                        .validator(|s| is_valid_pubkey(s))
2027                        .value_name("TOKEN_MINT_ADDRESS")
2028                        .takes_value(true)
2029                        .long("token")
2030                        .requires("verbose")
2031                        .help("Return the associated token address for the given token. \
2032                               [Default: return the client keypair address]")
2033                )
2034                .arg(
2035                    owner_address_arg()
2036                        .requires("token")
2037                        .help("Return the associated token address for the given owner. \
2038                               [Default: return the associated token address for the client keypair]"),
2039                ),
2040        )
2041        .subcommand(
2042            SubCommand::with_name(CommandName::AccountInfo.into())
2043                .about("Query details of an SPL Token account by address (DEPRECATED: use `spl-token display`)")
2044                .setting(AppSettings::Hidden)
2045                .arg(
2046                    Arg::with_name("token")
2047                        .validator(|s| is_valid_pubkey(s))
2048                        .value_name("TOKEN_MINT_ADDRESS")
2049                        .takes_value(true)
2050                        .index(1)
2051                        .conflicts_with("address")
2052                        .required_unless("address")
2053                        .help("Token of associated account. \
2054                               To query a specific account, use the `--address` parameter instead"),
2055                )
2056                .arg(
2057                    Arg::with_name(OWNER_ADDRESS_ARG.name)
2058                        .takes_value(true)
2059                        .value_name("OWNER_ADDRESS")
2060                        .validator(|s| is_valid_signer(s))
2061                        .help(OWNER_ADDRESS_ARG.help)
2062                        .index(2)
2063                        .conflicts_with("address")
2064                        .help("Owner of the associated account for the specified token. \
2065                               To query a specific account, use the `--address` parameter instead. \
2066                               Defaults to the client keypair."),
2067                )
2068                .arg(
2069                    Arg::with_name("address")
2070                        .validator(|s| is_valid_pubkey(s))
2071                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2072                        .takes_value(true)
2073                        .long("address")
2074                        .conflicts_with("token")
2075                        .help("Specify the token account to query"),
2076                ),
2077        )
2078        .subcommand(
2079            SubCommand::with_name(CommandName::MultisigInfo.into())
2080                .about("Query details of an SPL Token multisig account by address (DEPRECATED: use `spl-token display`)")
2081                .setting(AppSettings::Hidden)
2082                .arg(
2083                    Arg::with_name("address")
2084                    .validator(|s| is_valid_pubkey(s))
2085                    .value_name("MULTISIG_ACCOUNT_ADDRESS")
2086                    .takes_value(true)
2087                    .index(1)
2088                    .required(true)
2089                    .help("The address of the SPL Token multisig account to query"),
2090                ),
2091        )
2092        .subcommand(
2093            SubCommand::with_name(CommandName::Display.into())
2094                .about("Query details of an SPL Token mint, account, or multisig by address")
2095                .arg(
2096                    Arg::with_name("address")
2097                    .validator(|s| is_valid_pubkey(s))
2098                    .value_name("TOKEN_ADDRESS")
2099                    .takes_value(true)
2100                    .index(1)
2101                    .required(true)
2102                    .help("The address of the SPL Token mint, account, or multisig to query"),
2103                ),
2104        )
2105        .subcommand(
2106            SubCommand::with_name(CommandName::Gc.into())
2107                .about("Cleanup unnecessary token accounts")
2108                .arg(owner_keypair_arg())
2109                .arg(
2110                    Arg::with_name("close_empty_associated_accounts")
2111                    .long("close-empty-associated-accounts")
2112                    .takes_value(false)
2113                    .help("close all empty associated token accounts (to get SOL back)")
2114                )
2115        )
2116        .subcommand(
2117            SubCommand::with_name(CommandName::SyncNative.into())
2118                .about("Sync a native SOL token account to its underlying lamports")
2119                .arg(
2120                    owner_address_arg()
2121                        .index(1)
2122                        .conflicts_with("address")
2123                        .help("Owner of the associated account for the native token. \
2124                               To query a specific account, use the `--address` parameter instead. \
2125                               Defaults to the client keypair."),
2126                )
2127                .arg(
2128                    Arg::with_name("address")
2129                        .validator(|s| is_valid_pubkey(s))
2130                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2131                        .takes_value(true)
2132                        .long("address")
2133                        .conflicts_with("owner")
2134                        .help("Specify the specific token account address to sync"),
2135                ),
2136        )
2137        .subcommand(
2138            SubCommand::with_name(CommandName::EnableRequiredTransferMemos.into())
2139                .about("Enable required transfer memos for token account")
2140                .arg(
2141                    Arg::with_name("account")
2142                        .validator(|s| is_valid_pubkey(s))
2143                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2144                        .takes_value(true)
2145                        .index(1)
2146                        .required(true)
2147                        .help("The address of the token account to require transfer memos for")
2148                )
2149                .arg(
2150                    owner_address_arg()
2151                )
2152                .arg(multisig_signer_arg())
2153                .nonce_args(true)
2154        )
2155        .subcommand(
2156            SubCommand::with_name(CommandName::DisableRequiredTransferMemos.into())
2157                .about("Disable required transfer memos for token account")
2158                .arg(
2159                    Arg::with_name("account")
2160                        .validator(|s| is_valid_pubkey(s))
2161                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2162                        .takes_value(true)
2163                        .index(1)
2164                        .required(true)
2165                        .help("The address of the token account to stop requiring transfer memos for"),
2166                )
2167                .arg(
2168                    owner_address_arg()
2169                )
2170                .arg(multisig_signer_arg())
2171                .nonce_args(true)
2172        )
2173        .subcommand(
2174            SubCommand::with_name(CommandName::EnableCpiGuard.into())
2175                .about("Enable CPI Guard for token account")
2176                .arg(
2177                    Arg::with_name("account")
2178                        .validator(|s| is_valid_pubkey(s))
2179                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2180                        .takes_value(true)
2181                        .index(1)
2182                        .required(true)
2183                        .help("The address of the token account to enable CPI Guard for")
2184                )
2185                .arg(
2186                    owner_address_arg()
2187                )
2188                .arg(multisig_signer_arg())
2189                .nonce_args(true)
2190        )
2191        .subcommand(
2192            SubCommand::with_name(CommandName::DisableCpiGuard.into())
2193                .about("Disable CPI Guard for token account")
2194                .arg(
2195                    Arg::with_name("account")
2196                        .validator(|s| is_valid_pubkey(s))
2197                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2198                        .takes_value(true)
2199                        .index(1)
2200                        .required(true)
2201                        .help("The address of the token account to disable CPI Guard for"),
2202                )
2203                .arg(
2204                    owner_address_arg()
2205                )
2206                .arg(multisig_signer_arg())
2207                .nonce_args(true)
2208        )
2209        .subcommand(
2210            SubCommand::with_name(CommandName::UpdateDefaultAccountState.into())
2211                .about("Updates default account state for the mint. Requires the default account state extension.")
2212                .arg(
2213                    Arg::with_name("token")
2214                        .validator(|s| is_valid_pubkey(s))
2215                        .value_name("TOKEN_MINT_ADDRESS")
2216                        .takes_value(true)
2217                        .index(1)
2218                        .required(true)
2219                        .help("The address of the token mint to update default account state"),
2220                )
2221                .arg(
2222                    Arg::with_name("state")
2223                        .value_name("STATE")
2224                        .takes_value(true)
2225                        .possible_values(["initialized", "frozen"])
2226                        .index(2)
2227                        .required(true)
2228                        .help("The new default account state."),
2229                )
2230                .arg(
2231                    Arg::with_name("freeze_authority")
2232                        .long("freeze-authority")
2233                        .value_name("KEYPAIR")
2234                        .validator(|s| is_valid_signer(s))
2235                        .takes_value(true)
2236                        .help(
2237                            "Specify the token's freeze authority. \
2238                            This may be a keypair file or the ASK keyword. \
2239                            Defaults to the client keypair.",
2240                        ),
2241                )
2242                .arg(owner_address_arg())
2243                .arg(multisig_signer_arg())
2244                .nonce_args(true)
2245                .offline_args(),
2246        )
2247        .subcommand(
2248            SubCommand::with_name(CommandName::UpdateMetadataAddress.into())
2249                .about("Updates metadata pointer address for the mint. Requires the metadata pointer extension.")
2250                .arg(
2251                    Arg::with_name("token")
2252                        .validator(|s| is_valid_pubkey(s))
2253                        .value_name("TOKEN_MINT_ADDRESS")
2254                        .takes_value(true)
2255                        .index(1)
2256                        .required(true)
2257                        .help("The address of the token mint to update the metadata pointer address"),
2258                )
2259                .arg(
2260                    Arg::with_name("metadata_address")
2261                        .index(2)
2262                        .validator(|s| is_valid_pubkey(s))
2263                        .value_name("METADATA_ADDRESS")
2264                        .takes_value(true)
2265                        .required_unless("disable")
2266                        .help("Specify address that stores token's metadata-pointer"),
2267                )
2268                .arg(
2269                    Arg::with_name("disable")
2270                        .long("disable")
2271                        .takes_value(false)
2272                        .conflicts_with("metadata_address")
2273                        .help("Unset metadata pointer address.")
2274                )
2275                .arg(
2276                    Arg::with_name("authority")
2277                        .long("authority")
2278                        .value_name("KEYPAIR")
2279                        .validator(|s| is_valid_signer(s))
2280                        .takes_value(true)
2281                        .help(
2282                            "Specify the token's metadata-pointer authority. \
2283                            This may be a keypair file or the ASK keyword. \
2284                            Defaults to the client keypair.",
2285                        ),
2286                )
2287                .arg(multisig_signer_arg())
2288                .nonce_args(true)
2289        )
2290        .subcommand(
2291            SubCommand::with_name(CommandName::UpdateGroupAddress.into())
2292                .about("Updates group pointer address for the mint. Requires the group pointer extension.")
2293                .arg(
2294                    Arg::with_name("token")
2295                        .validator(|s| is_valid_pubkey(s))
2296                        .value_name("TOKEN_MINT_ADDRESS")
2297                        .takes_value(true)
2298                        .index(1)
2299                        .required(true)
2300                        .help("The address of the token mint to update the group pointer address"),
2301                )
2302                .arg(
2303                    Arg::with_name("group_address")
2304                        .index(2)
2305                        .validator(|s| is_valid_pubkey(s))
2306                        .value_name("GROUP_ADDRESS")
2307                        .takes_value(true)
2308                        .required_unless("disable")
2309                        .help("Specify address that stores token's group-pointer"),
2310                )
2311                .arg(
2312                    Arg::with_name("disable")
2313                        .long("disable")
2314                        .takes_value(false)
2315                        .conflicts_with("group_address")
2316                        .help("Unset group pointer address.")
2317                )
2318                .arg(
2319                    Arg::with_name("authority")
2320                        .long("authority")
2321                        .value_name("KEYPAIR")
2322                        .validator(|s| is_valid_signer(s))
2323                        .takes_value(true)
2324                        .help(
2325                            "Specify the token's group-pointer authority. \
2326                            This may be a keypair file or the ASK keyword. \
2327                            Defaults to the client keypair.",
2328                        ),
2329                )
2330                .arg(multisig_signer_arg())
2331                .nonce_args(true)
2332        )
2333        .subcommand(
2334            SubCommand::with_name(CommandName::UpdateMemberAddress.into())
2335                .about("Updates group member pointer address for the mint. Requires the group member pointer extension.")
2336                .arg(
2337                    Arg::with_name("token")
2338                        .validator(|s| is_valid_pubkey(s))
2339                        .value_name("TOKEN_MINT_ADDRESS")
2340                        .takes_value(true)
2341                        .index(1)
2342                        .required(true)
2343                        .help("The address of the token mint to update the group member pointer address"),
2344                )
2345                .arg(
2346                    Arg::with_name("member_address")
2347                        .index(2)
2348                        .validator(|s| is_valid_pubkey(s))
2349                        .value_name("MEMBER_ADDRESS")
2350                        .takes_value(true)
2351                        .required_unless("disable")
2352                        .help("Specify address that stores token's group-member-pointer"),
2353                )
2354                .arg(
2355                    Arg::with_name("disable")
2356                        .long("disable")
2357                        .takes_value(false)
2358                        .conflicts_with("member_address")
2359                        .help("Unset group member pointer address.")
2360                )
2361                .arg(
2362                    Arg::with_name("authority")
2363                        .long("authority")
2364                        .value_name("KEYPAIR")
2365                        .validator(|s| is_valid_signer(s))
2366                        .takes_value(true)
2367                        .help(
2368                            "Specify the token's group-member-pointer authority. \
2369                            This may be a keypair file or the ASK keyword. \
2370                            Defaults to the client keypair.",
2371                        ),
2372                )
2373                .arg(multisig_signer_arg())
2374                .nonce_args(true)
2375        )
2376        .subcommand(
2377            SubCommand::with_name(CommandName::WithdrawWithheldTokens.into())
2378                .about("Withdraw withheld transfer fee tokens from mint and / or account(s)")
2379                .arg(
2380                    Arg::with_name("account")
2381                        .validator(|s| is_valid_pubkey(s))
2382                        .value_name("FEE_RECIPIENT_ADDRESS")
2383                        .takes_value(true)
2384                        .index(1)
2385                        .required(true)
2386                        .help("The token account to send withdrawn fees to."),
2387                )
2388                .arg(
2389                    Arg::with_name("source")
2390                        .validator(|s| is_valid_pubkey(s))
2391                        .value_name("SOURCE_ADDRESS")
2392                        .takes_value(true)
2393                        .multiple(true)
2394                        .min_values(0_usize)
2395                        .index(2)
2396                        .help("The token account(s) to withdraw fees from.")
2397                )
2398                .arg(
2399                    Arg::with_name("include_mint")
2400                        .long("include-mint")
2401                        .takes_value(false)
2402                        .help("Also withdraw withheld tokens from the mint"),
2403                )
2404                .arg(
2405                    Arg::with_name("withdraw_withheld_authority")
2406                        .long("withdraw-withheld-authority")
2407                        .value_name("KEYPAIR")
2408                        .validator(|s| is_valid_signer(s))
2409                        .takes_value(true)
2410                        .help(
2411                            "Specify the withdraw withheld authority keypair. \
2412                             This may be a keypair file or the ASK keyword. \
2413                             Defaults to the client keypair."
2414                        ),
2415                )
2416                .arg(owner_address_arg())
2417                .arg(multisig_signer_arg())
2418                // 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.
2419                .group(
2420                    ArgGroup::with_name("account_group")
2421                        .arg("account")
2422                        .required(true)
2423                )
2424                .group(
2425                    ArgGroup::with_name("source_or_mint")
2426                        .arg("source")
2427                        .arg("include_mint")
2428                        .multiple(true)
2429                        .required(true)
2430                )
2431        )
2432        .subcommand(
2433            SubCommand::with_name(CommandName::SetTransferFee.into())
2434                .about("Set the transfer fee for a token with a configured transfer fee")
2435                .arg(
2436                    Arg::with_name("token")
2437                        .validator(|s| is_valid_pubkey(s))
2438                        .value_name("TOKEN_MINT_ADDRESS")
2439                        .takes_value(true)
2440                        .required(true)
2441                        .help("The interest-bearing token address"),
2442                )
2443                .arg(
2444                    Arg::with_name("transfer_fee_basis_points")
2445                        .value_name("FEE_IN_BASIS_POINTS")
2446                        .takes_value(true)
2447                        .required(true)
2448                        .help("The new transfer fee in basis points"),
2449                )
2450                .arg(
2451                    Arg::with_name("maximum_fee")
2452                        .value_name("MAXIMUM_FEE")
2453                        .value_parser(Amount::parse)
2454                        .takes_value(true)
2455                        .required(true)
2456                        .help("The new maximum transfer fee in UI amount"),
2457                )
2458                .arg(
2459                    Arg::with_name("transfer_fee_authority")
2460                    .long("transfer-fee-authority")
2461                    .validator(|s| is_valid_signer(s))
2462                    .value_name("SIGNER")
2463                    .takes_value(true)
2464                    .help(
2465                        "Specify the rate authority keypair. \
2466                        Defaults to the client keypair address."
2467                    )
2468                )
2469                .arg(mint_decimals_arg())
2470                .offline_args_config(&SignOnlyNeedsMintDecimals{})
2471        )
2472        .subcommand(
2473            SubCommand::with_name(CommandName::WithdrawExcessLamports.into())
2474                .about("Withdraw lamports from a Token Program owned account")
2475                .arg(
2476                    Arg::with_name("from")
2477                        .validator(|s| is_valid_pubkey(s))
2478                        .value_name("SOURCE_ACCOUNT_ADDRESS")
2479                        .takes_value(true)
2480                        .required(true)
2481                        .help("Specify the address of the account to recover lamports from"),
2482                )
2483                .arg(
2484                    Arg::with_name("recipient")
2485                        .validator(|s| is_valid_pubkey(s))
2486                        .value_name("REFUND_ACCOUNT_ADDRESS")
2487                        .takes_value(true)
2488                        .required(true)
2489                        .help("Specify the address of the account to send lamports to"),
2490                )
2491                .arg(owner_address_arg())
2492                .arg(multisig_signer_arg())
2493        )
2494        .subcommand(
2495            SubCommand::with_name(CommandName::UpdateConfidentialTransferSettings.into())
2496                .about("Update confidential transfer configuration for a token")
2497                .arg(
2498                    Arg::with_name("token")
2499                        .validator(|s| is_valid_pubkey(s))
2500                        .value_name("TOKEN_MINT_ADDRESS")
2501                        .takes_value(true)
2502                        .index(1)
2503                        .required(true)
2504                        .help("The address of the token mint to update confidential transfer configuration for")
2505                )
2506                .arg(
2507                    Arg::with_name("approve_policy")
2508                        .long("approve-policy")
2509                        .value_name("APPROVE_POLICY")
2510                        .takes_value(true)
2511                        .possible_values(["auto", "manual"])
2512                        .help(
2513                            "Policy for enabling accounts to make confidential transfers. If \"auto\" \
2514                            is selected, then accounts are automatically approved to make \
2515                            confidential transfers. If \"manual\" is selected, then the \
2516                            confidential transfer mint authority must approve each account \
2517                            before it can make confidential transfers."
2518                        )
2519                )
2520                .arg(
2521                    Arg::with_name("auditor_pubkey")
2522                        .long("auditor-pubkey")
2523                        .value_name("AUDITOR_PUBKEY")
2524                        .takes_value(true)
2525                        .help(
2526                            "The auditor encryption public key. The corresponding private key for \
2527                            this auditor public key can be used to decrypt all confidential \
2528                            transfers involving tokens from this mint. Currently, the auditor \
2529                            public key can only be specified as a direct *base64* encoding of \
2530                            an ElGamal public key. More methods of specifying the auditor public \
2531                            key will be supported in a future version. To disable auditability \
2532                            feature for the token, use \"none\"."
2533                        )
2534                )
2535                .group(
2536                    ArgGroup::with_name("update_fields").args(&["approve_policy", "auditor_pubkey"])
2537                        .required(true)
2538                        .multiple(true)
2539                )
2540                .arg(
2541                    Arg::with_name("confidential_transfer_authority")
2542                        .long("confidential-transfer-authority")
2543                        .validator(|s| is_valid_signer(s))
2544                        .value_name("SIGNER")
2545                        .takes_value(true)
2546                        .help(
2547                            "Specify the confidential transfer authority keypair. \
2548                            Defaults to the client keypair address."
2549                        )
2550                )
2551                .nonce_args(true)
2552                .offline_args(),
2553        )
2554        .subcommand(
2555            SubCommand::with_name(CommandName::ConfigureConfidentialTransferAccount.into())
2556                .about("Configure confidential transfers for token account")
2557                .arg(
2558                    Arg::with_name("token")
2559                        .validator(|s| is_valid_pubkey(s))
2560                        .value_name("TOKEN_MINT_ADDRESS")
2561                        .takes_value(true)
2562                        .index(1)
2563                        .required_unless("address")
2564                        .help("The token address with confidential transfers enabled"),
2565                )
2566                .arg(
2567                    Arg::with_name("address")
2568                        .long("address")
2569                        .validator(|s| is_valid_pubkey(s))
2570                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2571                        .takes_value(true)
2572                        .conflicts_with("token")
2573                        .help("The address of the token account to configure confidential transfers for \
2574                            [default: owner's associated token account]")
2575                )
2576                .arg(
2577                    owner_address_arg()
2578                )
2579                .arg(
2580                    Arg::with_name("maximum_pending_balance_credit_counter")
2581                        .long("maximum-pending-balance-credit-counter")
2582                        .value_name("MAXIMUM-CREDIT-COUNTER")
2583                        .takes_value(true)
2584                        .help(
2585                            "The maximum pending balance credit counter. \
2586                            This parameter limits the number of confidential transfers that a token account \
2587                            can receive to facilitate decryption of the encrypted balance. \
2588                            Defaults to 65536 (2^16)"
2589                        )
2590                )
2591                .arg(multisig_signer_arg())
2592                .nonce_args(true)
2593        )
2594        .subcommand(
2595            SubCommand::with_name(CommandName::EnableConfidentialCredits.into())
2596                .about("Enable confidential transfers for token account. To enable confidential transfers \
2597                for the first time, use `configure-confidential-transfer-account` instead.")
2598                .arg(
2599                    Arg::with_name("token")
2600                        .validator(|s| is_valid_pubkey(s))
2601                        .value_name("TOKEN_MINT_ADDRESS")
2602                        .takes_value(true)
2603                        .index(1)
2604                        .required_unless("address")
2605                        .help("The token address with confidential transfers enabled"),
2606                )
2607                .arg(
2608                    Arg::with_name("address")
2609                        .long("address")
2610                        .validator(|s| is_valid_pubkey(s))
2611                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2612                        .takes_value(true)
2613                        .conflicts_with("token")
2614                        .help("The address of the token account to enable confidential transfers for \
2615                            [default: owner's associated token account]")
2616                )
2617                .arg(
2618                    owner_address_arg()
2619                )
2620                .arg(multisig_signer_arg())
2621                .nonce_args(true)
2622        )
2623        .subcommand(
2624            SubCommand::with_name(CommandName::DisableConfidentialCredits.into())
2625                .about("Disable confidential transfers for token account")
2626                .arg(
2627                    Arg::with_name("token")
2628                        .validator(|s| is_valid_pubkey(s))
2629                        .value_name("TOKEN_MINT_ADDRESS")
2630                        .takes_value(true)
2631                        .index(1)
2632                        .required_unless("address")
2633                        .help("The token address with confidential transfers enabled"),
2634                )
2635                .arg(
2636                    Arg::with_name("address")
2637                        .long("address")
2638                        .validator(|s| is_valid_pubkey(s))
2639                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2640                        .takes_value(true)
2641                        .conflicts_with("token")
2642                        .help("The address of the token account to disable confidential transfers for \
2643                            [default: owner's associated token account]")
2644                )
2645                .arg(
2646                    owner_address_arg()
2647                )
2648                .arg(multisig_signer_arg())
2649                .nonce_args(true)
2650        )
2651        .subcommand(
2652            SubCommand::with_name(CommandName::EnableNonConfidentialCredits.into())
2653                .about("Enable non-confidential transfers for token account.")
2654                .arg(
2655                    Arg::with_name("token")
2656                        .validator(|s| is_valid_pubkey(s))
2657                        .value_name("TOKEN_MINT_ADDRESS")
2658                        .takes_value(true)
2659                        .index(1)
2660                        .required_unless("address")
2661                        .help("The token address with confidential transfers enabled"),
2662                )
2663                .arg(
2664                    Arg::with_name("address")
2665                        .long("address")
2666                        .validator(|s| is_valid_pubkey(s))
2667                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2668                        .takes_value(true)
2669                        .conflicts_with("token")
2670                        .help("The address of the token account to enable non-confidential transfers for \
2671                            [default: owner's associated token account]")
2672                )
2673                .arg(
2674                    owner_address_arg()
2675                )
2676                .arg(multisig_signer_arg())
2677                .nonce_args(true)
2678        )
2679        .subcommand(
2680            SubCommand::with_name(CommandName::DisableNonConfidentialCredits.into())
2681                .about("Disable non-confidential transfers for token account")
2682                .arg(
2683                    Arg::with_name("token")
2684                        .validator(|s| is_valid_pubkey(s))
2685                        .value_name("TOKEN_MINT_ADDRESS")
2686                        .takes_value(true)
2687                        .index(1)
2688                        .required_unless("address")
2689                        .help("The token address with confidential transfers enabled"),
2690                )
2691                .arg(
2692                    Arg::with_name("address")
2693                        .long("address")
2694                        .validator(|s| is_valid_pubkey(s))
2695                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2696                        .takes_value(true)
2697                        .conflicts_with("token")
2698                        .help("The address of the token account to disable non-confidential transfers for \
2699                            [default: owner's associated token account]")
2700                )
2701                .arg(
2702                    owner_address_arg()
2703                )
2704                .arg(multisig_signer_arg())
2705                .nonce_args(true)
2706        )
2707        .subcommand(
2708            SubCommand::with_name(CommandName::DepositConfidentialTokens.into())
2709                .about("Deposit amounts for confidential transfers")
2710                .arg(
2711                    Arg::with_name("token")
2712                        .validator(|s| is_valid_pubkey(s))
2713                        .value_name("TOKEN_MINT_ADDRESS")
2714                        .takes_value(true)
2715                        .index(1)
2716                        .required(true)
2717                        .help("The token address with confidential transfers enabled"),
2718                )
2719                .arg(
2720                    Arg::with_name("amount")
2721                        .value_parser(Amount::parse)
2722                        .value_name("TOKEN_AMOUNT")
2723                        .takes_value(true)
2724                        .index(2)
2725                        .required(true)
2726                        .help("Amount to deposit; accepts keyword ALL"),
2727                )
2728                .arg(
2729                    Arg::with_name("address")
2730                        .long("address")
2731                        .validator(|s| is_valid_pubkey(s))
2732                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2733                        .takes_value(true)
2734                        .help("The address of the token account to configure confidential transfers for \
2735                            [default: owner's associated token account]")
2736                )
2737                .arg(
2738                    owner_address_arg()
2739                )
2740                .arg(multisig_signer_arg())
2741                .arg(mint_decimals_arg())
2742                .nonce_args(true)
2743        )
2744        .subcommand(
2745            SubCommand::with_name(CommandName::WithdrawConfidentialTokens.into())
2746                .about("Withdraw amounts for confidential transfers")
2747                .arg(
2748                    Arg::with_name("token")
2749                        .validator(|s| is_valid_pubkey(s))
2750                        .value_name("TOKEN_MINT_ADDRESS")
2751                        .takes_value(true)
2752                        .index(1)
2753                        .required(true)
2754                        .help("The token address with confidential transfers enabled"),
2755                )
2756                .arg(
2757                    Arg::with_name("amount")
2758                        .value_parser(Amount::parse)
2759                        .value_name("TOKEN_AMOUNT")
2760                        .takes_value(true)
2761                        .index(2)
2762                        .required(true)
2763                        .help("Amount to deposit; accepts keyword ALL"),
2764                )
2765                .arg(
2766                    Arg::with_name("address")
2767                        .long("address")
2768                        .validator(|s| is_valid_pubkey(s))
2769                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2770                        .takes_value(true)
2771                        .help("The address of the token account to configure confidential transfers for \
2772                            [default: owner's associated token account]")
2773                )
2774                .arg(
2775                    owner_address_arg()
2776                )
2777                .arg(multisig_signer_arg())
2778                .arg(mint_decimals_arg())
2779                .nonce_args(true)
2780        )
2781        .subcommand(
2782            SubCommand::with_name(CommandName::ApplyPendingBalance.into())
2783                .about("Collect confidential tokens from pending to available balance")
2784                .arg(
2785                    Arg::with_name("token")
2786                        .validator(|s| is_valid_pubkey(s))
2787                        .value_name("TOKEN_MINT_ADDRESS")
2788                        .takes_value(true)
2789                        .index(1)
2790                        .required_unless("address")
2791                        .help("The token address with confidential transfers enabled"),
2792                )
2793                .arg(
2794                    Arg::with_name("address")
2795                        .long("address")
2796                        .validator(|s| is_valid_pubkey(s))
2797                        .value_name("TOKEN_ACCOUNT_ADDRESS")
2798                        .takes_value(true)
2799                        .help("The address of the token account to configure confidential transfers for \
2800                            [default: owner's associated token account]")
2801                )
2802                .arg(
2803                    owner_address_arg()
2804                )
2805                .arg(multisig_signer_arg())
2806                .nonce_args(true)
2807        )
2808        .subcommand(
2809            SubCommand::with_name(CommandName::UpdateUiAmountMultiplier.into())
2810                .about("Update UI multiplier")
2811                .arg(
2812                    Arg::with_name("token")
2813                        .validator(|s| is_valid_pubkey(s))
2814                        .value_name("TOKEN_MINT_ADDRESS")
2815                        .takes_value(true)
2816                        .index(1)
2817                        .required(true)
2818                        .help("The token address with scaled UI amount multiplier"),
2819                )
2820                .arg(
2821                    Arg::with_name("multiplier")
2822                        .value_name("MULTIPLIER")
2823                        .takes_value(true)
2824                        .index(2)
2825                        .required(true)
2826                        .help("The new multiplier"),
2827                )
2828                .arg(
2829                    Arg::with_name("timestamp")
2830                        .value_name("TIMESTAMP")
2831                        .takes_value(true)
2832                        .index(3)
2833                        .help("The effective time for the new multiplier, given as a UNIX timestamp \
2834                            [default: current time]",)
2835                )
2836                .arg(
2837                    Arg::with_name("ui_multiplier_authority")
2838                    .long("ui-multiplier-authority")
2839                    .alias("owner")
2840                    .validator(|s| is_valid_signer(s))
2841                    .value_name("SIGNER")
2842                    .takes_value(true)
2843                    .help(
2844                        "Specify the multiplier authority keypair. \
2845                        Defaults to the client keypair address."
2846                    )
2847                )
2848                .arg(multisig_signer_arg())
2849                .nonce_args(true)
2850        )
2851        .subcommand(
2852            SubCommand::with_name(CommandName::Pause.into())
2853                .about("Pause mint, burn, and transfer")
2854                .arg(
2855                    Arg::with_name("token")
2856                        .validator(|s| is_valid_pubkey(s))
2857                        .value_name("TOKEN_MINT_ADDRESS")
2858                        .takes_value(true)
2859                        .index(1)
2860                        .required(true)
2861                        .help("The pausable token address"),
2862                )
2863                .arg(
2864                    Arg::with_name("pause_authority")
2865                    .long("pause-authority")
2866                    .alias("owner")
2867                    .validator(|s| is_valid_signer(s))
2868                    .value_name("SIGNER")
2869                    .takes_value(true)
2870                    .help(
2871                        "Specify the pause authority keypair. \
2872                        Defaults to the client keypair address."
2873                    )
2874                )
2875                .arg(multisig_signer_arg())
2876                .nonce_args(true)
2877        )
2878        .subcommand(
2879            SubCommand::with_name(CommandName::Resume.into())
2880                .about("Resume mint, burn, and transfer")
2881                .arg(
2882                    Arg::with_name("token")
2883                        .validator(|s| is_valid_pubkey(s))
2884                        .value_name("TOKEN_MINT_ADDRESS")
2885                        .takes_value(true)
2886                        .index(1)
2887                        .required(true)
2888                        .help("The pausable token address"),
2889                )
2890                .arg(
2891                    Arg::with_name("pause_authority")
2892                    .long("pause-authority")
2893                    .alias("owner")
2894                    .validator(|s| is_valid_signer(s))
2895                    .value_name("SIGNER")
2896                    .takes_value(true)
2897                    .help(
2898                        "Specify the pause authority keypair. \
2899                        Defaults to the client keypair address."
2900                    )
2901                )
2902                .arg(multisig_signer_arg())
2903                .nonce_args(true)
2904        )
2905}