3

I am come from evm to Solana so I have question in evm we have modifier call is_owner... so in Solana how it work, as I understand using cpi here is my code

I create custom token

create.rs

use {
    anchor_lang::prelude::*,
    anchor_spl::{
        metadata::{
            create_metadata_accounts_v3, mpl_token_metadata::types::DataV2,
            CreateMetadataAccountsV3, Metadata,
        },
        token::{Mint, Token},
    },
};

#[derive(Accounts)]
pub struct CreateToken<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,

    // Create mint account
    // Same PDA as address of the account and mint/freeze authority
    #[account(
        init,
        seeds = [b"mint"],
        bump,
        payer = payer,
        mint::decimals = 9,
        mint::authority = mint_account.key(),
        mint::freeze_authority = mint_account.key(),

    )]
    pub mint_account: Account<'info, Mint>,

    /// CHECK: Validate address by deriving pda
    #[account(
        mut,
        seeds = [b"metadata", token_metadata_program.key().as_ref(), mint_account.key().as_ref()],
        bump,
        seeds::program = token_metadata_program.key(),
    )]
    pub metadata_account: UncheckedAccount<'info>,

    pub token_program: Program<'info, Token>,
    pub token_metadata_program: Program<'info, Metadata>,
    pub system_program: Program<'info, System>,
    pub rent: Sysvar<'info, Rent>,
}

pub fn create_token(
    ctx: Context<CreateToken>,
    token_name: String,
    token_symbol: String,
    token_uri: String,
) -> Result<()> {
    msg!("Creating metadata account");

    // PDA signer seeds
    let signer_seeds: &[&[&[u8]]] = &[&[b"mint", &[ctx.bumps.mint_account]]];

    // Cross Program Invocation (CPI) signed by PDA
    // Invoking the create_metadata_account_v3 instruction on the token metadata program
    create_metadata_accounts_v3(
        CpiContext::new(
            ctx.accounts.token_metadata_program.to_account_info(),
            CreateMetadataAccountsV3 {
                metadata: ctx.accounts.metadata_account.to_account_info(),
                mint: ctx.accounts.mint_account.to_account_info(),
                mint_authority: ctx.accounts.mint_account.to_account_info(), // PDA is mint authority
                update_authority: ctx.accounts.mint_account.to_account_info(), // PDA is update authority
                payer: ctx.accounts.payer.to_account_info(),
                system_program: ctx.accounts.system_program.to_account_info(),
                rent: ctx.accounts.rent.to_account_info(),
            },
        )
        .with_signer(signer_seeds),
        DataV2 {
            name: token_name,
            symbol: token_symbol,
            uri: token_uri,
            seller_fee_basis_points: 0,
            creators: None,
            collection: None,
            uses: None,
        },
        false, // Is mutable
        true,  // Update authority is signer
        None,  // Collection details
    )?;

    msg!("Token created successfully.");

    Ok(())
}

mint.rs

use {
    anchor_lang::prelude::*,
    anchor_spl::{
        associated_token::AssociatedToken,
        token::{mint_to, Mint, MintTo, Token, TokenAccount},
    },
};

#[derive(Accounts)]
pub struct MintToken<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,

    // Mint account address is a PDA
    #[account(
        mut,
        seeds = [b"mint"],
        bump
    )]
    pub mint_account: Account<'info, Mint>,

    // Create Associated Token Account, if needed
    // This is the account that will hold the minted tokens
    #[account(
        init_if_needed,
        payer = payer,
        associated_token::mint = mint_account,
        associated_token::authority = payer,
    )]
    pub associated_token_account: Account<'info, TokenAccount>,

    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub system_program: Program<'info, System>,
}

pub fn mint_token(ctx: Context<MintToken>, amount: u64) -> Result<()> {
    msg!("Minting token to associated token account...");
    msg!("Mint: {}", &ctx.accounts.mint_account.key());
    msg!(
        "Token Address: {}",
        &ctx.accounts.associated_token_account.key()
    );

    // PDA signer seeds
    let signer_seeds: &[&[&[u8]]] = &[&[b"mint", &[ctx.bumps.mint_account]]];

    // Invoke the mint_to instruction on the token program
    mint_to(
        CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            MintTo {
                mint: ctx.accounts.mint_account.to_account_info(),
                to: ctx.accounts.associated_token_account.to_account_info(),
                authority: ctx.accounts.mint_account.to_account_info(), // PDA mint authority, required as signer
            },
        )
        .with_signer(signer_seeds), // using PDA to sign
        amount * 10u64.pow(ctx.accounts.mint_account.decimals as u32), // Mint tokens, adjust for decimals
    )?;

    msg!("Token minted successfully.");

    Ok(())
}

lib.rs

#![allow(clippy::result_large_err)]

use anchor_lang::prelude::*;
use instructions::*;
pub mod instructions;

declare_id!("54JwCGa4fCHNDaidhsdsS5RA4z4nRs8dKKfUWgsdCcDb");

#[program]
pub mod token {
    use super::*;

    pub fn create_token(
        ctx: Context<CreateToken>,
        token_name: String,
        token_symbol: String,
        token_uri: String,
    ) -> Result<()> {
        create::create_token(ctx, token_name, token_symbol, token_uri)
    }

    pub fn mint_token(ctx: Context<MintToken>, amount: u64) -> Result<()> {
        mint::mint_token(ctx, amount)
    }
}

and I wanna call mint_token in this contract from another contract another contract code

claimed.rs

use {
    anchor_lang::prelude::*,
    anchor_spl::{
        associated_token::AssociatedToken,
        token::{ Mint, Token, TokenAccount},
    },
};
use token::program::Token as TokenProgram;
use token::cpi::accounts::MintToken;

declare_id!("BJNis8sHDAkNrQvZhRxh9f9tMeZwwzAQ7KiiNPhjifVo");

#[program]
pub mod cpi {

    use super::*;

    pub fn mint_from_pda(ctx: Context<InitializePDA>, amount: u64) -> Result<()> {

        let cpi_program = ctx.accounts.token_pda.to_account_info();
        let cpi_account = MintToken {
            payer: ctx.accounts.payer.to_account_info(),
            mint_account: ctx.accounts.mint_account.to_account_info(),
            associated_token_account: ctx.accounts.associated_token_account.to_account_info(),
            token_program: ctx.accounts.token_program.to_account_info(),
            associated_token_program: ctx.accounts.associated_token_program.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
        };

        let cpi_ctx = CpiContext::new(cpi_program.clone(), cpi_account);
        token::cpi::mint_token(cpi_ctx, amount)?;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct InitializePDA<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    pub token_pda: Program<'info, TokenProgram>,

    #[account(mut)]
    pub mint_account: Account<'info, Mint>,

    #[account(
        init_if_needed,
        payer = payer,
        associated_token::mint = mint_account,
        associated_token::authority = payer,
    )]
    pub associated_token_account: Account<'info, TokenAccount>,

    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub system_program: Program<'info, System>,
}



// impl<'info> InitializePDA<'info> {
//     pub fn mint_pda_token(&self) -> CpiContext<'_, '_, '_, 'info, MintToken<'info>> {
//         let cpi_program = self.token_program.to_account_info();

//         let cpi_account = MintToken {
//             payer: self.payer.to_account_info(),
//             mint_account: self.mint_account.to_account_info(),
//             associated_token_account: self.token.to_account_info(),
//             token_program: self.token_program.to_account_info(),
//             associated_token_program: self.associated_token_program.to_account_info(),
//             system_program: self.system_program.to_account_info(),
//         };

//         CpiContext::new(cpi_program, cpi_account)
//     }
// }

here is test.ts

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import { Cpi } from "../target/types/cpi";
import { Token } from "../target/types/token";



describe("Meme Account", () => {
  const provider = anchor.AnchorProvider.env()
  anchor.setProvider(provider);

  const tokenProgram = anchor.workspace.token as Program<Token>;
  const cpiProgram = anchor.workspace.cpi as Program<Cpi>;
  const connection = provider.connection;
 
  const METADATA_SEED = "metadata";
  const MINT_SEED = "mint";

  const TOKEN_METADATA_PROGRAM_ID = new anchor.web3.PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
  const metadata = {
    name: "Meme",
    symbol: "MEME",
    uri: "https://5vfxc4tr6xoy23qefqbj4qx2adzkzapneebanhcalf7myvn5gzja.arweave.net/7UtxcnH13Y1uBCwCnkL6APKsge0hAgacQFl-zFW9NlI",
    decimals: 9,
  }

  

  const payer = provider.wallet;
  const mintAmount = 100;

  const [mintAccount, mintAccountBum] = anchor.web3.PublicKey.findProgramAddressSync([Buffer.from('mint')], tokenProgram.programId);
  const [metadataAccount] = anchor.web3.PublicKey.findProgramAddressSync(
    [
      Buffer.from(METADATA_SEED),
      TOKEN_METADATA_PROGRAM_ID.toBuffer(),
      mintAccount.toBuffer(),
    ],
    TOKEN_METADATA_PROGRAM_ID
  );

  it("Create Token!", async () => {
    const transactionSignature = await tokenProgram.methods
      .createToken(metadata.name, metadata.symbol, metadata.uri)
      .accounts({
        payer: payer.publicKey,
        mintAccount,
        metadataAccount,
        tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
        tokenProgram: TOKEN_PROGRAM_ID,
      })
      .rpc();

    console.log('Success!');
    console.log(`   Mint Address: ${mintAccount}`);
    console.log(`   Transaction Signature: ${transactionSignature}`);
  });

  it("Mint", async () => {
    const associatedTokenAccountAddress = getAssociatedTokenAddressSync(mintAccount, payer.publicKey);
    const amount = new anchor.BN(mintAmount);

    const transactionSignature = await tokenProgram.methods
      .mintToken(amount)
      .accounts({
        payer: payer.publicKey,
        mintAccount,
        associatedTokenAccount: associatedTokenAccountAddress,
      })
      .rpc();


      const associatedAddress = await connection.getParsedTokenAccountsByOwner(
        provider.wallet.publicKey,
        {
          mint: mintAccount,
        },
      );
    

    console.log('Success!');
    console.log(`   Associated Token Account Address: ${associatedTokenAccountAddress}`);
    console.log(`   Transaction Signature: ${transactionSignature}`);
    console.log(`   Associated Token Account: ${associatedAddress.value[0].pubkey}`);
    console.log(`   Associated Token Account Balance: ${associatedAddress.value[0]?.account.data.parsed.info.tokenAmount.uiAmount}`);
  });

  it ("Call Cpi", async () => {
    const associatedTokenAccountAddress = getAssociatedTokenAddressSync(mintAccount, payer.publicKey);
    const amount = new anchor.BN(mintAmount);

    const transactionSignature = await cpiProgram.methods
      .mintFromPda(amount)
      .accounts({
        payer: payer.publicKey,
        tokenPda: tokenProgram.programId,
        mintAccount,
        tokenProgram: TOKEN_PROGRAM_ID,
        associatedTokenProgram: new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"),
        systemProgram: anchor.web3.SystemProgram.programId,
        associatedTokenAccount: associatedTokenAccountAddress,
      })
      .rpc();
  });
});

when run anchor test got

Error: Simulation failed. 
Message: Transaction simulation failed: Error processing Instruction 0: Cross-program invocation with unauthorized signer or writable account. 
Logs: 
[
  "Program BJNis8sHDAkNrQvZhRxh9f9tMeZwwzAQ7KiiNPhjifVo invoke [1]",
  "Program log: Instruction: MintFromPda",
  "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL's writable privilege escalated",
  "Program BJNis8sHDAkNrQvZhRxh9f9tMeZwwzAQ7KiiNPhjifVo consumed 10709 of 200000 compute units",
  "Program BJNis8sHDAkNrQvZhRxh9f9tMeZwwzAQ7KiiNPhjifVo failed: Cross-program invocation with unauthorized signer or writable account"
]. 
Catch the `SendTransactionError` and call `getLogs()` on it for full details.

anyone can explain how to call mint_token from another contract and implement authority is best.

1 Answer 1

1

The reason why CreateToken is probably because you are using different seeds. The mint seeds will be your program ID + your mint seeds

        const [mintAccount, mintAccountBum] =
            anchor.web3.PublicKey.findProgramAddressSync(
                [Buffer.from("mint")],
                program.programId
            );

From my understanding of the code you shared, this will set the mint and freeze authorities to this mint pda, even though the account is assigned to the Token Program as the owner.

When calling this from another program, you'll also need to change the cpi program to your original program(that created the mint) and not the token program as you've done here let cpi_program = ctx.accounts.token_pda.to_account_info();


    pub fn mint_from_pda(ctx: Context<Initialize>, amount: u64) -> Result<()> {
        let cpi_program = ctx.accounts.mint_token_program.to_account_info();

        let cpi_account = MintToken {
            payer: ctx.accounts.payer.to_account_info(),
            mint_account: ctx.accounts.mint_account.to_account_info(),
            associated_token_account: ctx.accounts.associated_token_account.to_account_info(),
            token_program: ctx.accounts.token_program.to_account_info(),
            associated_token_program: ctx.accounts.associated_token_program.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
        };

        let cpi_ctx = CpiContext::new(cpi_program.clone(), cpi_account);
        mint_token(cpi_ctx, amount)?;
        Ok(())
    }

/// Accounts struct 
#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,

    // Mint account address is a PDA
    #[account(
        mut,
        // seeds = [b"mint"],
        // bump
    )]
    pub mint_account: Account<'info, Mint>,

    // Create Associated Token Account, if needed
    // This is the account that will hold the minted tokens
    #[account(
        init_if_needed,
        payer = payer,
        associated_token::mint = mint_account,
        associated_token::authority = payer,
    )]
    pub associated_token_account: Account<'info, TokenAccount>,

    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    /// CHECK: ok
    pub mint_token_program: Program<'info, MyProgram>,
    pub system_program: Program<'info, System>,
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.