diff --git a/src/mint.rs b/src/mint.rs index e1e76d2..e4ff582 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -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, @@ -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::{ @@ -267,6 +269,92 @@ pub fn mint_from_uris( Ok(()) } +pub fn mint_fungible( + client: &RpcClient, + keypair_path: Option, + mint_address: &str, + amount: u64, + receiver: &Option, +) -> 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>( client: &RpcClient, diff --git a/src/opt.rs b/src/opt.rs index f7d3393..34ff526 100644 --- a/src/opt.rs +++ b/src/opt.rs @@ -377,7 +377,7 @@ pub enum CreateSubcommands { immutable: bool, }, - /// Create a new SPL Token mint and metadata account. + /// Mint SPL tokens from an existing mint account using the Token Program. Fungible { /// Path to the update authority keypair file #[structopt(short, long)] @@ -852,6 +852,24 @@ pub enum FindSubcommands { #[derive(Debug, StructOpt)] pub enum MintSubcommands { + /// Mint a normal SPL Token from the Token Program + Fungible { + /// Path to the mint_authority keypair file + #[structopt(short, long)] + keypair: Option, + + /// Token Mint address + #[structopt(short = "M", long)] + mint_address: String, + + /// Receiving address, if different from mint authority. + #[structopt(short = "R", long)] + receiver: Option, + + /// 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 diff --git a/src/process_subcommands.rs b/src/process_subcommands.rs index 84c1267..d2798b6 100644 --- a/src/process_subcommands.rs +++ b/src/process_subcommands.rs @@ -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}; @@ -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,