|
| 1 | +use { |
| 2 | + crate::{ |
| 3 | + common::{parse_pubkey, process_transaction}, |
| 4 | + config::Config, |
| 5 | + output::{format_output, println_display}, |
| 6 | + CommandResult, |
| 7 | + }, |
| 8 | + clap::{ArgMatches, Args}, |
| 9 | + mpl_token_metadata::accounts::Metadata as MetaplexMetadata, |
| 10 | + serde_derive::{Deserialize, Serialize}, |
| 11 | + serde_with::{serde_as, DisplayFromStr}, |
| 12 | + solana_cli_output::{display::writeln_name_value, QuietDisplay, VerboseDisplay}, |
| 13 | + solana_pubkey::Pubkey, |
| 14 | + solana_remote_wallet::remote_wallet::RemoteWalletManager, |
| 15 | + solana_signature::Signature, |
| 16 | + solana_signer::Signer, |
| 17 | + solana_transaction::Transaction, |
| 18 | + spl_token_wrap::{ |
| 19 | + get_wrapped_mint_address, get_wrapped_mint_authority, |
| 20 | + instruction::sync_metadata_to_token_2022, |
| 21 | + }, |
| 22 | + std::{ |
| 23 | + fmt::{Display, Formatter}, |
| 24 | + rc::Rc, |
| 25 | + }, |
| 26 | +}; |
| 27 | + |
| 28 | +#[derive(Clone, Debug, Args)] |
| 29 | +pub struct SyncMetadataToToken2022Args { |
| 30 | + /// The address of the unwrapped mint whose metadata will be synced from |
| 31 | + #[clap(value_parser = parse_pubkey)] |
| 32 | + pub unwrapped_mint: Pubkey, |
| 33 | + |
| 34 | + /// Specify that the source metadata is from a `Metaplex` Token Metadata |
| 35 | + /// account. The CLI will derive the PDA automatically. |
| 36 | + #[clap(long)] |
| 37 | + pub metaplex: bool, |
| 38 | + |
| 39 | + /// Optional source metadata account when the unwrapped mint's metadata |
| 40 | + /// pointer points to an external account or third-party program |
| 41 | + #[clap(long, value_parser = parse_pubkey, conflicts_with = "metaplex", requires = "program-id")] |
| 42 | + pub metadata_account: Option<Pubkey>, |
| 43 | + |
| 44 | + /// Optional owner program for the source metadata account, when owned by a |
| 45 | + /// third-party program |
| 46 | + #[clap(long, value_parser = parse_pubkey)] |
| 47 | + pub program_id: Option<Pubkey>, |
| 48 | +} |
| 49 | + |
| 50 | +#[serde_as] |
| 51 | +#[derive(Debug, Serialize, Deserialize)] |
| 52 | +#[serde(rename_all = "camelCase")] |
| 53 | +pub struct SyncMetadataToToken2022Output { |
| 54 | + #[serde_as(as = "DisplayFromStr")] |
| 55 | + pub unwrapped_mint: Pubkey, |
| 56 | + |
| 57 | + #[serde_as(as = "DisplayFromStr")] |
| 58 | + pub wrapped_mint: Pubkey, |
| 59 | + |
| 60 | + #[serde_as(as = "DisplayFromStr")] |
| 61 | + pub wrapped_mint_authority: Pubkey, |
| 62 | + |
| 63 | + #[serde_as(as = "Option<DisplayFromStr>")] |
| 64 | + pub source_metadata: Option<Pubkey>, |
| 65 | + |
| 66 | + #[serde_as(as = "Option<DisplayFromStr>")] |
| 67 | + pub owner_program: Option<Pubkey>, |
| 68 | + |
| 69 | + pub signatures: Vec<Signature>, |
| 70 | +} |
| 71 | + |
| 72 | +impl Display for SyncMetadataToToken2022Output { |
| 73 | + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
| 74 | + writeln_name_value(f, "Unwrapped mint:", &self.unwrapped_mint.to_string())?; |
| 75 | + writeln_name_value(f, "Wrapped mint:", &self.wrapped_mint.to_string())?; |
| 76 | + writeln_name_value( |
| 77 | + f, |
| 78 | + "Wrapped mint authority:", |
| 79 | + &self.wrapped_mint_authority.to_string(), |
| 80 | + )?; |
| 81 | + if let Some(src) = self.source_metadata { |
| 82 | + writeln_name_value(f, "Source metadata:", &src.to_string())?; |
| 83 | + } |
| 84 | + if let Some(owner) = self.owner_program { |
| 85 | + writeln_name_value(f, "Owner program:", &owner.to_string())?; |
| 86 | + } |
| 87 | + |
| 88 | + writeln!(f, "Signers:")?; |
| 89 | + for signature in &self.signatures { |
| 90 | + writeln!(f, " {signature}")?; |
| 91 | + } |
| 92 | + |
| 93 | + Ok(()) |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +impl QuietDisplay for SyncMetadataToToken2022Output { |
| 98 | + fn write_str(&self, _: &mut dyn std::fmt::Write) -> std::fmt::Result { |
| 99 | + Ok(()) |
| 100 | + } |
| 101 | +} |
| 102 | +impl VerboseDisplay for SyncMetadataToToken2022Output {} |
| 103 | + |
| 104 | +pub async fn command_sync_metadata_to_token2022( |
| 105 | + config: &Config, |
| 106 | + args: SyncMetadataToToken2022Args, |
| 107 | + _matches: &ArgMatches, |
| 108 | + _wallet_manager: &mut Option<Rc<RemoteWalletManager>>, |
| 109 | +) -> CommandResult { |
| 110 | + let payer = config.fee_payer()?; |
| 111 | + |
| 112 | + let wrapped_mint = get_wrapped_mint_address(&args.unwrapped_mint, &spl_token_2022::id()); |
| 113 | + let wrapped_mint_authority = get_wrapped_mint_authority(&wrapped_mint); |
| 114 | + |
| 115 | + let source_metadata = if args.metaplex { |
| 116 | + let (metaplex_pda, _) = MetaplexMetadata::find_pda(&args.unwrapped_mint); |
| 117 | + Some(metaplex_pda) |
| 118 | + } else { |
| 119 | + args.metadata_account |
| 120 | + }; |
| 121 | + |
| 122 | + println_display( |
| 123 | + config, |
| 124 | + format!( |
| 125 | + "Syncing metadata to Token-2022 mint {} from {}", |
| 126 | + wrapped_mint, args.unwrapped_mint |
| 127 | + ), |
| 128 | + ); |
| 129 | + |
| 130 | + let instruction = sync_metadata_to_token_2022( |
| 131 | + &spl_token_wrap::id(), |
| 132 | + &wrapped_mint, |
| 133 | + &wrapped_mint_authority, |
| 134 | + &args.unwrapped_mint, |
| 135 | + source_metadata.as_ref(), |
| 136 | + args.program_id.as_ref(), |
| 137 | + ); |
| 138 | + |
| 139 | + let blockhash = config.rpc_client.get_latest_blockhash().await?; |
| 140 | + |
| 141 | + let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey())); |
| 142 | + transaction.partial_sign(&[payer.clone()], blockhash); |
| 143 | + |
| 144 | + process_transaction(config, transaction.clone()).await?; |
| 145 | + |
| 146 | + let output = SyncMetadataToToken2022Output { |
| 147 | + unwrapped_mint: args.unwrapped_mint, |
| 148 | + wrapped_mint, |
| 149 | + wrapped_mint_authority, |
| 150 | + source_metadata, |
| 151 | + owner_program: args.program_id, |
| 152 | + signatures: transaction.signatures, |
| 153 | + }; |
| 154 | + |
| 155 | + Ok(format_output(config, output)) |
| 156 | +} |
0 commit comments