From 33c7d6e4113af9acecb3beb451f210e7a50f96e6 Mon Sep 17 00:00:00 2001 From: Samuel Vanderwaal Date: Sat, 23 Dec 2023 11:23:37 -0900 Subject: [PATCH] fix create fungible decimals and optionally create ata --- src/create/methods.rs | 13 ++++++++++- src/utils.rs | 54 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/create/methods.rs b/src/create/methods.rs index 1c5dfdf..4574fda 100644 --- a/src/create/methods.rs +++ b/src/create/methods.rs @@ -10,6 +10,8 @@ use solana_sdk::signature::read_keypair_file; use spl_associated_token_account::get_associated_token_address; use spl_token::instruction::mint_to; +use crate::utils::create_token_if_missing_instruction; + use super::*; pub struct CreateMetadataArgs { @@ -127,7 +129,7 @@ pub fn create_fungible(args: CreateFungibleArgs) -> Result<()> { collection: None, uses: None, collection_details: None, - decimals: None, + decimals: Some(args.decimals), rule_set: None, print_supply: None, }; @@ -150,6 +152,15 @@ pub fn create_fungible(args: CreateFungibleArgs) -> Result<()> { // Derive associated token account let assoc = get_associated_token_address(&keypair.pubkey(), &mint.pubkey()); + // Create associated token account if needed + instructions.push(create_token_if_missing_instruction( + &keypair.pubkey(), + &assoc, + &mint.pubkey(), + &keypair.pubkey(), + &assoc, + )); + // Mint to instruction let mint_to_ix = mint_to( &spl_token::ID, diff --git a/src/utils.rs b/src/utils.rs index d577b62..dd47bcd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,11 +1,14 @@ use anyhow::{anyhow, Result}; +use borsh::{BorshDeserialize, BorshSerialize}; use retry::{delay::Exponential, retry}; use serde::Deserialize; use serde_json::json; use solana_client::rpc_request::RpcRequest; use solana_client::{nonblocking::rpc_client::RpcClient as AsyncRpcClient, rpc_client::RpcClient}; +use solana_program::instruction::AccountMeta; use solana_program::program_pack::Pack; -use solana_program::pubkey::Pubkey; +use solana_program::system_program; +use solana_program::{pubkey, pubkey::Pubkey}; use solana_sdk::commitment_config::CommitmentConfig; use solana_sdk::{ instruction::Instruction, signature::Keypair, signer::Signer, transaction::Transaction, @@ -300,3 +303,52 @@ struct TokenAccount { // #[serde(rename = "uiAmountString")] // ui_amount_string: String, } + +const MPL_TOOLBOX_ID: Pubkey = pubkey!("TokExjvjJmhKaRBShsBAsbSvEWMA1AgUNK7ps4SAc2p"); + +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +#[rustfmt::skip] +pub enum TokenExtrasInstruction { + /// Creates a new associated token account for the given mint and owner, if and only if + /// the given token account does not exists and the token account is the same as the + /// associated token account. That way, clients can ensure that, after this instruction, + /// the token account will exists. + /// + /// Notice this instruction asks for both the token account and the associated token account (ATA) + /// These may or may not be the same account. Here are all the possible cases: + /// + /// - Token exists and Token is ATA: Instruction succeeds. + /// - Token exists and Token is not ATA: Instruction succeeds. + /// - Token does not exist and Token is ATA: Instruction creates the ATA account and succeeds. + /// - Token does not exist and Token is not ATA: Instruction fails as we cannot create a + /// non-ATA account without it being a signer. + /// + /// Note that additional checks are made to ensure that the token account provided + /// matches the mint account and owner account provided. + CreateTokenIfMissing, +} + +pub fn create_token_if_missing_instruction( + payer: &Pubkey, + token: &Pubkey, + mint: &Pubkey, + owner: &Pubkey, + ata: &Pubkey, +) -> Instruction { + Instruction { + program_id: MPL_TOOLBOX_ID, + accounts: vec![ + AccountMeta::new(*payer, true), + AccountMeta::new_readonly(*token, false), + AccountMeta::new_readonly(*mint, false), + AccountMeta::new_readonly(*owner, false), + AccountMeta::new(*ata, false), + AccountMeta::new_readonly(system_program::id(), false), + AccountMeta::new_readonly(spl_token::id(), false), + AccountMeta::new_readonly(spl_associated_token_account::id(), false), + ], + data: TokenExtrasInstruction::CreateTokenIfMissing + .try_to_vec() + .unwrap(), + } +}