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
79fn 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) )
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 .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}