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.