|
| 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_spl_token, |
| 21 | + }, |
| 22 | + std::{ |
| 23 | + fmt::{Display, Formatter}, |
| 24 | + rc::Rc, |
| 25 | + }, |
| 26 | +}; |
| 27 | + |
| 28 | +#[derive(Clone, Debug, Args)] |
| 29 | +pub struct SyncMetadataToSplTokenArgs { |
| 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 | + /// Optional source metadata account when the unwrapped mint's metadata |
| 35 | + /// pointer points to an external account or third-party program |
| 36 | + #[clap(long, value_parser = parse_pubkey)] |
| 37 | + pub source_metadata: Option<Pubkey>, |
| 38 | + |
| 39 | + /// Optional owner program for the source metadata account, when owned by a |
| 40 | + /// third-party program |
| 41 | + #[clap(long, value_parser = parse_pubkey)] |
| 42 | + pub owner_program: Option<Pubkey>, |
| 43 | +} |
| 44 | + |
| 45 | +#[serde_as] |
| 46 | +#[derive(Debug, Serialize, Deserialize)] |
| 47 | +#[serde(rename_all = "camelCase")] |
| 48 | +pub struct SyncMetadataToSplTokenOutput { |
| 49 | + #[serde_as(as = "DisplayFromStr")] |
| 50 | + pub unwrapped_mint: Pubkey, |
| 51 | + |
| 52 | + #[serde_as(as = "DisplayFromStr")] |
| 53 | + pub wrapped_mint: Pubkey, |
| 54 | + |
| 55 | + #[serde_as(as = "DisplayFromStr")] |
| 56 | + pub wrapped_mint_authority: Pubkey, |
| 57 | + |
| 58 | + #[serde_as(as = "DisplayFromStr")] |
| 59 | + pub metaplex_metadata: Pubkey, |
| 60 | + |
| 61 | + #[serde_as(as = "Option<DisplayFromStr>")] |
| 62 | + pub source_metadata: Option<Pubkey>, |
| 63 | + |
| 64 | + #[serde_as(as = "Option<DisplayFromStr>")] |
| 65 | + pub owner_program: Option<Pubkey>, |
| 66 | + |
| 67 | + #[serde_as(as = "Option<DisplayFromStr>")] |
| 68 | + pub signature: Option<Signature>, |
| 69 | +} |
| 70 | + |
| 71 | +impl Display for SyncMetadataToSplTokenOutput { |
| 72 | + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
| 73 | + writeln_name_value(f, "Unwrapped mint:", &self.unwrapped_mint.to_string())?; |
| 74 | + writeln_name_value(f, "Wrapped mint:", &self.wrapped_mint.to_string())?; |
| 75 | + writeln_name_value( |
| 76 | + f, |
| 77 | + "Wrapped mint authority:", |
| 78 | + &self.wrapped_mint_authority.to_string(), |
| 79 | + )?; |
| 80 | + writeln_name_value( |
| 81 | + f, |
| 82 | + "Metaplex metadata account:", |
| 83 | + &self.metaplex_metadata.to_string(), |
| 84 | + )?; |
| 85 | + if let Some(src) = self.source_metadata { |
| 86 | + writeln_name_value(f, "Source metadata:", &src.to_string())?; |
| 87 | + } |
| 88 | + if let Some(owner) = self.owner_program { |
| 89 | + writeln_name_value(f, "Owner program:", &owner.to_string())?; |
| 90 | + } |
| 91 | + if let Some(signature) = self.signature { |
| 92 | + writeln_name_value(f, "Signature:", &signature.to_string())?; |
| 93 | + } |
| 94 | + Ok(()) |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +impl QuietDisplay for SyncMetadataToSplTokenOutput { |
| 99 | + fn write_str(&self, _: &mut dyn std::fmt::Write) -> std::fmt::Result { |
| 100 | + Ok(()) |
| 101 | + } |
| 102 | +} |
| 103 | +impl VerboseDisplay for SyncMetadataToSplTokenOutput {} |
| 104 | + |
| 105 | +pub async fn command_sync_metadata_to_spl_token( |
| 106 | + config: &Config, |
| 107 | + args: SyncMetadataToSplTokenArgs, |
| 108 | + _matches: &ArgMatches, |
| 109 | + _wallet_manager: &mut Option<Rc<RemoteWalletManager>>, |
| 110 | +) -> CommandResult { |
| 111 | + let payer = config.fee_payer()?; |
| 112 | + let wrapped_token_program = spl_token::id(); |
| 113 | + |
| 114 | + let wrapped_mint = get_wrapped_mint_address(&args.unwrapped_mint, &wrapped_token_program); |
| 115 | + let wrapped_mint_authority = get_wrapped_mint_authority(&wrapped_mint); |
| 116 | + let (metaplex_metadata, _) = MetaplexMetadata::find_pda(&wrapped_mint); |
| 117 | + |
| 118 | + println_display( |
| 119 | + config, |
| 120 | + format!( |
| 121 | + "Syncing metadata to SPL Token mint {} from {}", |
| 122 | + wrapped_mint, args.unwrapped_mint |
| 123 | + ), |
| 124 | + ); |
| 125 | + |
| 126 | + let instruction = sync_metadata_to_spl_token( |
| 127 | + &spl_token_wrap::id(), |
| 128 | + &metaplex_metadata, |
| 129 | + &wrapped_mint_authority, |
| 130 | + &wrapped_mint, |
| 131 | + &args.unwrapped_mint, |
| 132 | + args.source_metadata.as_ref(), |
| 133 | + args.owner_program.as_ref(), |
| 134 | + ); |
| 135 | + |
| 136 | + let blockhash = config.rpc_client.get_latest_blockhash().await?; |
| 137 | + |
| 138 | + let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey())); |
| 139 | + transaction.partial_sign(&[payer.clone()], blockhash); |
| 140 | + |
| 141 | + let signature = process_transaction(config, transaction).await?; |
| 142 | + |
| 143 | + let output = SyncMetadataToSplTokenOutput { |
| 144 | + unwrapped_mint: args.unwrapped_mint, |
| 145 | + wrapped_mint, |
| 146 | + wrapped_mint_authority, |
| 147 | + metaplex_metadata, |
| 148 | + source_metadata: args.source_metadata, |
| 149 | + owner_program: args.owner_program, |
| 150 | + signature, |
| 151 | + }; |
| 152 | + |
| 153 | + Ok(format_output(config, output)) |
| 154 | +} |
0 commit comments