From 46aaefa8a228638bf8d4fa46c35cf653e3c82f4d Mon Sep 17 00:00:00 2001 From: Samuel Vanderwaal Date: Thu, 21 Mar 2024 22:37:47 -0800 Subject: [PATCH] add mint priority fees (#334) --- Cargo.lock | 4 +- Cargo.toml | 2 +- src/mint.rs | 108 +++++++++++++++++++++++++++++++++---- src/opt.rs | 30 +++++++++++ src/process_subcommands.rs | 19 +++++-- 5 files changed, 147 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b263e7..be16ea2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2075,9 +2075,9 @@ dependencies = [ [[package]] name = "metaboss_lib" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b213eca5d67125be2b429bc691350423f340159e22416a276022d136d71c1c17" +checksum = "0d7968fa871a61b875fc743da18f111f004e096318bbe189fea275b5f35cfb40" dependencies = [ "anyhow", "borsh 0.10.3", diff --git a/Cargo.toml b/Cargo.toml index 49fe80e..ebada70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ indicatif = { version = "0.16.2", features = ["rayon"] } jib = "0.8.0" lazy_static = "1.4.0" log = "0.4.20" -metaboss_lib = "0.19.0" +metaboss_lib = "0.20.0" mpl-token-metadata = { version = "3.2.3", features = ["serde"] } num_cpus = "1.16.0" once_cell = "1.19.0" diff --git a/src/mint.rs b/src/mint.rs index e4ff582..a277a7e 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -3,9 +3,11 @@ use borsh::BorshDeserialize; use glob::glob; use log::{error, info}; use metaboss_lib::{ + data::Priority, decode::*, derive::derive_edition_marker_pda, mint::{mint_asset, AssetData, MintAssetArgs}, + transaction::get_compute_units, }; use mpl_token_metadata::{ accounts::EditionMarker, @@ -26,6 +28,7 @@ use solana_client::rpc_client::RpcClient; use solana_program::program_pack::Pack; use solana_sdk::{ commitment_config::CommitmentConfig, + compute_budget::ComputeBudgetInstruction, pubkey::Pubkey, signature::Signature, signer::{keypair::Keypair, Signer}, @@ -67,6 +70,7 @@ pub fn mint_list( primary_sale_happened: bool, sign: bool, track: bool, + priority: Priority, ) -> Result<()> { if !is_only_one_option(&list_dir, &external_metadata_uris) { return Err(anyhow!( @@ -86,6 +90,7 @@ pub fn mint_list( max_editions, sign, false, + priority, )?; } else if let Some(external_metadata_uris) = external_metadata_uris { mint_from_uris( @@ -98,6 +103,7 @@ pub fn mint_list( max_editions, sign, track, + priority, )?; } else { return Err(anyhow!( @@ -119,6 +125,7 @@ pub fn mint_from_files( max_editions: i64, sign: bool, sized: bool, + priority: Priority, ) -> Result<()> { let use_rate_limit = *USE_RATE_LIMIT.read().unwrap(); let handle = create_default_rate_limiter(); @@ -151,6 +158,7 @@ pub fn mint_from_files( None, // Generate new mint keypair. sign, sized, + &priority, ) { Ok(_) => (), Err(e) => error!("Failed to mint {:?}: {}", &path, e), @@ -179,6 +187,7 @@ pub fn mint_from_uris( max_editions: i64, sign: bool, track: bool, + priority: Priority, ) -> Result<()> { let f = File::open(&external_metadata_uris_path)?; let external_metadata_uris: Vec = serde_json::from_reader(f)?; @@ -200,6 +209,7 @@ pub fn mint_from_uris( None, sign, false, + &priority, ) { Ok(_) => (), Err(e) => println!("Failed to mint {:?}: {}", &uri, e), @@ -234,6 +244,7 @@ pub fn mint_from_uris( None, sign, false, + &priority, ) { Ok(m) => MintResult { uri: uri.clone(), @@ -275,6 +286,7 @@ pub fn mint_fungible( mint_address: &str, amount: u64, receiver: &Option, + priority: Priority, ) -> Result<()> { let solana_opts = parse_solana_config(); let mint_authority_keypair = parse_keypair(keypair_path, solana_opts); @@ -330,9 +342,27 @@ pub fn mint_fungible( instructions.push(mint_ix); + let signers = vec![&mint_authority_keypair]; + + let compute_units = get_compute_units(client, &instructions, &signers)?.unwrap_or(200_000); + + let micro_lamports = match priority { + Priority::None => 20, + Priority::Low => 20_000, + Priority::Medium => 200_000, + Priority::High => 1_000_000, + Priority::Max => 2_000_000, + }; + + let mut final_instructions = vec![ + ComputeBudgetInstruction::set_compute_unit_limit(compute_units as u32), + ComputeBudgetInstruction::set_compute_unit_price(micro_lamports), + ]; + final_instructions.extend(instructions); + let recent_blockhash = client.get_latest_blockhash()?; let tx = Transaction::new_signed_with_payer( - &instructions, + &final_instructions, Some(&mint_authority_pubkey), &[&mint_authority_keypair], recent_blockhash, @@ -368,6 +398,7 @@ pub fn mint_one>( mint_path: Option, sign: bool, sized: bool, + priority: &Priority, ) -> Result { if !is_only_one_option(&nft_data_file, &external_metadata_uri) { return Err(anyhow!( @@ -421,6 +452,7 @@ pub fn mint_one>( max_editions, mint_path, sized, + priority.clone(), )?; info!("Tx sig: {:?}\nMint account: {:?}", &tx_id, &mint_account); let message = format!("Tx sig: {:?}\nMint account: {:?}", &tx_id, &mint_account,); @@ -440,17 +472,18 @@ pub fn mint_editions( receiver: &Option, next_editions: Option, specific_editions: Option>, + priority: Priority, ) -> Result<()> { let spinner = create_spinner("Minting..."); if let Some(next_editions) = next_editions { for _ in 0..next_editions { - mint_next_edition(client, &keypair_path, &account, receiver)?; + mint_next_edition(client, &keypair_path, &account, receiver, &priority)?; } return Ok(()); } if let Some(specific_editions) = specific_editions { for num in specific_editions { - mint_edition(client, &keypair_path, &account, num, receiver)?; + mint_edition(client, &keypair_path, &account, num, receiver, &priority)?; } return Ok(()); } @@ -464,6 +497,7 @@ fn mint_next_edition( keypair_path: &Option, account: &str, receiver: &Option, + priority: &Priority, ) -> Result<()> { // Send tx with retries. let master_edition = retry( @@ -516,7 +550,14 @@ fn mint_next_edition( edition_num += 1; } - mint_edition(client, keypair_path, account, edition_num, receiver)?; + mint_edition( + client, + keypair_path, + account, + edition_num, + receiver, + priority, + )?; Ok(()) } @@ -546,6 +587,7 @@ fn mint_edition( account: &str, edition_num: u64, receiver: &Option, + priority: &Priority, ) -> Result<(Signature, Pubkey)> { let solana_opts = parse_solana_config(); let funder = parse_keypair(keypair_path.clone(), solana_opts); @@ -631,11 +673,29 @@ fn mint_edition( mint_editions_ix, ]; + let signers = vec![&funder, &new_mint_keypair]; + + let compute_units = get_compute_units(client, &instructions, &signers)?.unwrap_or(200_000); + + let micro_lamports = match priority { + Priority::None => 20, + Priority::Low => 20_000, + Priority::Medium => 200_000, + Priority::High => 1_000_000, + Priority::Max => 2_000_000, + }; + + let mut final_instructions = vec![ + ComputeBudgetInstruction::set_compute_unit_limit(compute_units as u32), + ComputeBudgetInstruction::set_compute_unit_price(micro_lamports), + ]; + final_instructions.extend(instructions); + let recent_blockhash = client.get_latest_blockhash()?; let tx = Transaction::new_signed_with_payer( - &instructions, + &final_instructions, Some(&funder.pubkey()), - &[&funder, &new_mint_keypair], + &signers, recent_blockhash, ); @@ -655,12 +715,20 @@ pub fn mint_missing_editions( client: &RpcClient, keypair_path: &Option, mint_account: &str, + priority: Priority, ) -> Result<()> { let missing_editions = find_missing_editions(client, mint_account)?; let spinner = create_spinner("Printing missing editions"); for missing_edition in missing_editions { - mint_edition(client, keypair_path, mint_account, missing_edition, &None)?; + mint_edition( + client, + keypair_path, + mint_account, + missing_edition, + &None, + &priority, + )?; } spinner.finish(); @@ -678,6 +746,7 @@ pub fn mint( max_editions: i64, mint_path: Option, sized: bool, + priority: Priority, ) -> Result<(Signature, Pubkey)> { let metaplex_program_id = Pubkey::from_str(METAPLEX_PROGRAM_ID)?; let mint = if let Some(mint_path) = mint_path { @@ -814,11 +883,29 @@ pub fn mint( instructions.push(ix); } + let signers = vec![&funder, &mint]; + + let compute_units = get_compute_units(client, &instructions, &signers)?.unwrap_or(200_000); + + let micro_lamports = match priority { + Priority::None => 20, + Priority::Low => 20_000, + Priority::Medium => 200_000, + Priority::High => 1_000_000, + Priority::Max => 2_000_000, + }; + + let mut final_instructions = vec![ + ComputeBudgetInstruction::set_compute_unit_limit(compute_units as u32), + ComputeBudgetInstruction::set_compute_unit_price(micro_lamports), + ]; + final_instructions.extend(instructions); + let recent_blockhash = client.get_latest_blockhash()?; let tx = Transaction::new_signed_with_payer( - &instructions, + &final_instructions, Some(&funder.pubkey()), - &[&funder, &mint], + &signers, recent_blockhash, ); @@ -841,6 +928,7 @@ pub struct MintAssetParams { pub decimals: u8, pub amount: u64, pub max_print_edition_supply: Option, + pub priority: Priority, } pub fn process_mint_asset(args: MintAssetParams) -> Result<()> { @@ -853,6 +941,7 @@ pub fn process_mint_asset(args: MintAssetParams) -> Result<()> { decimals, amount, max_print_edition_supply, + priority, } = args; let solana_opts = parse_solana_config(); @@ -882,6 +971,7 @@ pub fn process_mint_asset(args: MintAssetParams) -> Result<()> { mint_decimals: Some(decimals), print_supply, authorization_data: None, + priority, }; let mint_result = mint_asset(&client, args)?; diff --git a/src/opt.rs b/src/opt.rs index 1813f15..44c1605 100644 --- a/src/opt.rs +++ b/src/opt.rs @@ -928,6 +928,11 @@ pub enum MintSubcommands { /// Amount of tokens to mint #[structopt(short, long)] amount: u64, + + /// Priority of the transaction: higher priority costs more. + /// See https://metaboss.rs/priority-fees.html for more details. + #[structopt(short = "P", long, default_value = "none")] + priority: Priority, }, /// Mint an asset from the new Token Metadata Program unified handlers. Asset { @@ -959,6 +964,11 @@ pub enum MintSubcommands { /// 0 for no prints, n for n prints, 'unlimited' for unlimited prints. #[structopt(short = "s", long)] max_print_edition_supply: Option, + + /// Priority of the transaction: higher priority costs more. + /// See https://metaboss.rs/priority-fees.html for more details. + #[structopt(short = "P", long, default_value = "none")] + priority: Priority, }, /// Mint a single NFT from a JSON file #[structopt(name = "one")] @@ -1002,6 +1012,11 @@ pub enum MintSubcommands { /// Create a sized collection parent NFT #[structopt(long)] sized: bool, + + /// Priority of the transaction: higher priority costs more. + /// See https://metaboss.rs/priority-fees.html for more details. + #[structopt(short = "P", long, default_value = "none")] + priority: Priority, }, /// Mint one or more editions from a Master NFT. #[structopt(name = "editions")] @@ -1025,6 +1040,11 @@ pub enum MintSubcommands { /// Mint the provided specific editions e.g.: --specific-editions 1,7,10 #[structopt(short = "s", long)] specific_editions: Option>, + + /// Priority of the transaction: higher priority costs more. + /// See https://metaboss.rs/priority-fees.html for more details. + #[structopt(short = "P", long, default_value = "none")] + priority: Priority, }, /// Find any missing editions for a Master NFT. #[structopt(name = "missing-editions")] @@ -1035,6 +1055,11 @@ pub enum MintSubcommands { #[structopt(short, long)] account: String, + + /// Priority of the transaction: higher priority costs more. + /// See https://metaboss.rs/priority-fees.html for more details. + #[structopt(short = "P", long, default_value = "none")] + priority: Priority, }, #[structopt(name = "list")] /// Mint a list of NFTs from a directory of JSON files @@ -1072,6 +1097,11 @@ pub enum MintSubcommands { /// with list of unminted URIs for easy continuation of command #[structopt(long)] track: bool, + + /// Priority of the transaction: higher priority costs more. + /// See https://metaboss.rs/priority-fees.html for more details. + #[structopt(short = "P", long, default_value = "none")] + priority: Priority, }, } diff --git a/src/process_subcommands.rs b/src/process_subcommands.rs index 0b92fe3..cf7f337 100644 --- a/src/process_subcommands.rs +++ b/src/process_subcommands.rs @@ -544,7 +544,8 @@ pub fn process_mint(client: RpcClient, commands: MintSubcommands) -> Result<()> receiver, amount, mint_address, - } => mint_fungible(&client, keypair, &mint_address, amount, &receiver), + priority, + } => mint_fungible(&client, keypair, &mint_address, amount, &receiver, priority), MintSubcommands::Asset { keypair, receiver, @@ -553,6 +554,7 @@ pub fn process_mint(client: RpcClient, commands: MintSubcommands) -> Result<()> amount, decimals, max_print_edition_supply, + priority, } => process_mint_asset(MintAssetParams { client, keypair_path: keypair, @@ -562,6 +564,7 @@ pub fn process_mint(client: RpcClient, commands: MintSubcommands) -> Result<()> decimals, amount, max_print_edition_supply, + priority, }), MintSubcommands::One { keypair, @@ -574,6 +577,7 @@ pub fn process_mint(client: RpcClient, commands: MintSubcommands) -> Result<()> max_editions, sign, sized, + priority, } => mint_one( &client, keypair, @@ -586,6 +590,7 @@ pub fn process_mint(client: RpcClient, commands: MintSubcommands) -> Result<()> mint_path, sign, sized, + &priority, ) .map(|_| ()), MintSubcommands::Editions { @@ -594,6 +599,7 @@ pub fn process_mint(client: RpcClient, commands: MintSubcommands) -> Result<()> receiver, next_editions, specific_editions, + priority, } => mint_editions( &client, keypair, @@ -601,10 +607,13 @@ pub fn process_mint(client: RpcClient, commands: MintSubcommands) -> Result<()> &receiver, next_editions, specific_editions, + priority, ), - MintSubcommands::MissingEditions { keypair, account } => { - mint_missing_editions(&client, &keypair, &account) - } + MintSubcommands::MissingEditions { + keypair, + account, + priority, + } => mint_missing_editions(&client, &keypair, &account, priority), MintSubcommands::List { keypair, receiver, @@ -614,6 +623,7 @@ pub fn process_mint(client: RpcClient, commands: MintSubcommands) -> Result<()> primary_sale_happened, sign, track, + priority, } => mint_list( &client, keypair, @@ -624,6 +634,7 @@ pub fn process_mint(client: RpcClient, commands: MintSubcommands) -> Result<()> primary_sale_happened, sign, track, + priority, ), } }