Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added command for minting fungible tokens #321

Merged
Show file tree
Hide file tree
Changes from 2 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
89 changes: 89 additions & 0 deletions src/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use retry::{delay::Exponential, retry};
use serde::Serialize;
use serde_json::Value;
use solana_client::rpc_client::RpcClient;
use solana_program::program_pack::Pack;
use solana_sdk::{
commitment_config::CommitmentConfig,
pubkey::Pubkey,
Expand All @@ -36,6 +37,7 @@ use spl_associated_token_account::{
};
use spl_token::{
instruction::{initialize_mint, mint_to},
state::Mint,
ID as TOKEN_PROGRAM_ID,
};
use std::{
Expand Down Expand Up @@ -267,6 +269,93 @@ pub fn mint_from_uris(
Ok(())
}

#[allow(clippy::too_many_arguments)]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a MintFungibleArgs struct here to fix the clippy issue and follow the patterns of other commands?

pub fn mint_fungible(
client: &RpcClient,
keypair_path: Option<String>,
mint_address: &str,
amount: u64,
receiver: &Option<String>,
) -> Result<()> {
let solana_opts = parse_solana_config();
let mint_authority_keypair = parse_keypair(keypair_path, solana_opts);

let mint_authority_pubkey = mint_authority_keypair.pubkey();
let mint_pubkey = Pubkey::from_str(mint_address)?;

let mint_account_info = client
.get_account_with_commitment(&mint_pubkey, CommitmentConfig::confirmed())?
.value;

if mint_account_info.is_none() {
return Err(anyhow!("Invalid Mint Address"));
}

let destination_pubkey = if let Some(receiver_str) = receiver {
Pubkey::from_str(receiver_str)?
} else {
mint_authority_pubkey
};

let destination_ata_pubkey = get_associated_token_address(&destination_pubkey, &mint_pubkey);

let destination_ata_pubkey_info = client
.get_account_with_commitment(&destination_ata_pubkey, CommitmentConfig::confirmed())?
.value;

let mut instructions = vec![];

if destination_ata_pubkey_info.is_none() {
instructions.push(create_associated_token_account(
&mint_authority_pubkey,
&destination_pubkey,
&mint_pubkey,
&TOKEN_PROGRAM_ID,
))
}

let mint_data = Mint::unpack(&mint_account_info.unwrap().data)?;

let mint_amount = amount
.checked_mul(10_u64.pow(mint_data.decimals.into()))
.ok_or(anyhow!("Invalid Mint Amount"))?;

let mint_ix = mint_to(
&TOKEN_PROGRAM_ID,
&mint_pubkey,
&destination_ata_pubkey,
&mint_authority_pubkey,
&[&mint_authority_pubkey],
mint_amount,
)?;

instructions.push(mint_ix);

let recent_blockhash = client.get_latest_blockhash()?;
let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&mint_authority_pubkey),
&[&mint_authority_keypair],
recent_blockhash,
);

// Send tx with retries.
let res = retry(
Exponential::from_millis_with_factor(250, 2.0).take(3),
|| client.send_and_confirm_transaction(&tx),
);
let sig = res?;

println!(
"Mint: {:?} minted {:?} tokens successfully!",
mint_address, amount
);

println!("Created in tx: {:?}", &sig);

Ok(())
}

#[allow(clippy::too_many_arguments)]
pub fn mint_one<P: AsRef<Path>>(
client: &RpcClient,
Expand Down
18 changes: 18 additions & 0 deletions src/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,24 @@ pub enum FindSubcommands {

#[derive(Debug, StructOpt)]
pub enum MintSubcommands {
/// Mint a normal SPL Token from the Token Program
Fungible {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about changing this to Tokens to clarify and maybe the docs comment can be:

/// Mint SPL tokens from an existing mint account using the Token Program.

Copy link
Contributor Author

@sunguru98 sunguru98 Feb 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of following the already built command structure in create
metaboss create fungible
metaboss mint fungible

Would be a bit relatable for users.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think the problem is that the naming is already inconsistent as "mint" basically means "create" when used for an asset like a NFT but I also have the "create" commands for setting up a fungible mint. I guess Fungible is ok if we change the doc comment to explain.

/// Path to the mint_authority keypair file
#[structopt(short, long)]
keypair: Option<String>,

/// Token Mint address
#[structopt(short = "M", long)]
mint_address: String,

/// Receiving address, if different from mint authority.
#[structopt(short = "R", long)]
receiver: Option<String>,

/// Amount of tokens to mint
#[structopt(short, long)]
amount: u64,
},
/// Mint an asset from the new Token Metadata Program unified handlers.
Asset {
/// Path to the update_authority keypair file
Expand Down
9 changes: 8 additions & 1 deletion src/process_subcommands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ use crate::derive::{
};
use crate::find::find_missing_editions_process;
use crate::mint::{
mint_editions, mint_list, mint_missing_editions, mint_one, process_mint_asset, MintAssetParams,
mint_editions, mint_fungible, mint_list, mint_missing_editions, mint_one, process_mint_asset,
MintAssetParams,
};
use crate::opt::*;
use crate::parse::{is_only_one_option, parse_errors_code, parse_errors_file};
Expand Down Expand Up @@ -498,6 +499,12 @@ pub fn process_find(client: &RpcClient, commands: FindSubcommands) -> Result<()>

pub fn process_mint(client: RpcClient, commands: MintSubcommands) -> Result<()> {
match commands {
MintSubcommands::Fungible {
keypair,
receiver,
amount,
mint_address,
} => mint_fungible(&client, keypair, &mint_address, amount, &receiver),
MintSubcommands::Asset {
keypair,
receiver,
Expand Down
Loading