Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ num-derive = "0.4.2"
num-traits = "0.2.19"
serde = "1.0.219"
serde_derive = "1.0.219"
serde_json = "1.0.140"
serde_with = "3.12.0"
serial_test = "3.2.0"
solana-account = "2.2.1"
Expand Down
1 change: 1 addition & 0 deletions clients/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ tokio = { workspace = true }

[dev-dependencies]
bytemuck = { workspace = true }
serde_json = { workspace = true }
serial_test = { workspace = true }
solana-keypair = { workspace = true }
solana-sdk-ids = { workspace = true }
Expand Down
3 changes: 3 additions & 0 deletions clients/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use {
crate::{
config::Config,
create_mint::{command_create_mint, CreateMintArgs},
find_pdas::{command_get_pdas, FindPdasArgs},
output::parse_output_format,
CommandResult,
},
Expand Down Expand Up @@ -84,6 +85,7 @@ pub struct Cli {
pub enum Command {
/// Create a wrapped mint for a given SPL Token
CreateMint(CreateMintArgs),
FindPdas(FindPdasArgs),
// TODO: Wrap, Unwrap
}

Expand All @@ -96,6 +98,7 @@ impl Command {
) -> CommandResult {
match self {
Command::CreateMint(args) => command_create_mint(config, args).await,
Command::FindPdas(args) => command_get_pdas(config, args).await,
}
}
}
85 changes: 85 additions & 0 deletions clients/cli/src/find_pdas.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use {
crate::{
common::{parse_pubkey, parse_token_program},
config::Config,
output::format_output,
CommandResult,
},
clap::Args,
serde_derive::{Deserialize, Serialize},
serde_with::{serde_as, DisplayFromStr},
solana_cli_output::{display::writeln_name_value, QuietDisplay, VerboseDisplay},
solana_pubkey::Pubkey,
spl_token_wrap::{
get_wrapped_mint_address, get_wrapped_mint_authority, get_wrapped_mint_backpointer_address,
},
std::fmt::{Display, Formatter},
};

#[derive(Clone, Debug, Args)]
pub struct FindPdasArgs {
/// The address of the mint to wrap
#[clap(value_parser = parse_pubkey)]
pub unwrapped_mint: Pubkey,

/// The address of the token program that the wrapped mint should belong to
#[clap(value_parser = parse_token_program)]
pub wrapped_token_program: Pubkey,
}

#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PdasOutput {
#[serde_as(as = "DisplayFromStr")]
pub wrapped_mint_address: Pubkey,
#[serde_as(as = "DisplayFromStr")]
pub wrapped_mint_authority: Pubkey,
#[serde_as(as = "DisplayFromStr")]
pub wrapped_backpointer_address: Pubkey,
}

impl Display for PdasOutput {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln_name_value(
f,
"Wrapped mint address:",
&self.wrapped_mint_address.to_string(),
)?;
writeln_name_value(
f,
"Wrapped mint authority:",
&self.wrapped_mint_authority.to_string(),
)?;
writeln_name_value(
f,
"Wrapped backpointer address:",
&self.wrapped_backpointer_address.to_string(),
)?;

Ok(())
}
}

impl QuietDisplay for PdasOutput {
fn write_str(&self, _: &mut dyn std::fmt::Write) -> std::fmt::Result {
Ok(())
}
}
impl VerboseDisplay for PdasOutput {}

pub async fn command_get_pdas(config: &Config, args: FindPdasArgs) -> CommandResult {
let wrapped_mint_address =
get_wrapped_mint_address(&args.unwrapped_mint, &args.wrapped_token_program);
let wrapped_backpointer_address = get_wrapped_mint_backpointer_address(&wrapped_mint_address);
let wrapped_mint_authority = get_wrapped_mint_authority(&wrapped_mint_address);

Ok(format_output(
config,
PdasOutput {
wrapped_mint_address,
wrapped_mint_authority,
wrapped_backpointer_address,
},
))
}
1 change: 1 addition & 0 deletions clients/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod cli;
mod common;
mod config;
mod create_mint;
mod find_pdas;
mod output;

use {
Expand Down
55 changes: 55 additions & 0 deletions clients/cli/tests/test_pdas.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use {
crate::helpers::TOKEN_WRAP_CLI_BIN,
solana_pubkey::Pubkey,
spl_token_wrap::{
self, get_wrapped_mint_address, get_wrapped_mint_authority,
get_wrapped_mint_backpointer_address,
},
std::{process::Command, str::FromStr},
};

pub mod helpers;

#[test]
fn test_pdas() {
let unwrapped_mint = Pubkey::from_str("FSi5sv14NFh71zDpBx1Ee1EFtDYRnN2fNYu7ixsPKNzJ").unwrap();

// Execute the pdas command with JSON output
let mut command = Command::new(TOKEN_WRAP_CLI_BIN);
let output = command
.args([
"find-pdas",
&unwrapped_mint.to_string(),
&spl_token_2022::id().to_string(),
"--output",
"json",
])
.output()
.unwrap();
assert!(output.status.success());

// Parse the JSON output
let output_str = String::from_utf8(output.stdout).unwrap();
let json_result: serde_json::Value = serde_json::from_str(&output_str).unwrap();

// Calculate the expected addresses
let expected_wrapped_mint = get_wrapped_mint_address(&unwrapped_mint, &spl_token_2022::id());
let expected_authority = get_wrapped_mint_authority(&expected_wrapped_mint);
let expected_backpointer = get_wrapped_mint_backpointer_address(&expected_wrapped_mint);

// Verify the JSON values match the expected addresses
assert_eq!(
json_result["wrappedMintAddress"].as_str().unwrap(),
expected_wrapped_mint.to_string(),
);

assert_eq!(
json_result["wrappedMintAuthority"].as_str().unwrap(),
expected_authority.to_string(),
);

assert_eq!(
json_result["wrappedBackpointerAddress"].as_str().unwrap(),
expected_backpointer.to_string(),
);
}