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