Update the `initialize_token_mint` instruction to also create a token metadata account. Here's a reference `initialize` instruction that: - initialize a mint - creates a metadata account for the mint - mints the token You can remove the `mint_to` cpi from the `initialize` instruction for your use case. use anchor_lang::{prelude::*, solana_program::program::invoke_signed}; use anchor_spl::{ associated_token::AssociatedToken, token::{mint_to, Mint, MintTo, Token, TokenAccount}, }; use borsh::BorshDeserialize; use mpl_token_metadata::{ instruction::create_metadata_accounts_v3, pda::find_metadata_account, ID as MetadataID, }; declare_id!("8LgkDyXcYXtX6h78eNRydLr2tkWHxduREnpb5TZdgE8b"); #[program] pub mod token_with_metadata { use super::*; pub fn initialize( ctx: Context<InitializeMint>, uri: String, name: String, symbol: String, ) -> Result<()> { let seeds = &["mint".as_bytes(), &[*ctx.bumps.get("mint").unwrap()]]; let signer = [&seeds[..]]; let account_info = vec![ ctx.accounts.metadata.to_account_info(), ctx.accounts.mint.to_account_info(), ctx.accounts.user.to_account_info(), ctx.accounts.token_metadata_program.to_account_info(), ctx.accounts.token_program.to_account_info(), ctx.accounts.system_program.to_account_info(), ctx.accounts.rent.to_account_info(), ]; invoke_signed( &create_metadata_accounts_v3( ctx.accounts.token_metadata_program.key(), // token metadata program ctx.accounts.metadata.key(), // metadata account PDA for mint ctx.accounts.mint.key(), // mint account ctx.accounts.mint.key(), // mint authority ctx.accounts.user.key(), // payer for transaction ctx.accounts.mint.key(), // update authority name, // name symbol, // symbol uri, // uri (offchain metadata) None, // (optional) creators 0, // seller free basis points true, // (bool) update authority is signer true, // (bool) is mutable None, // (optional) collection None, // (optional) uses None, // (optional) collection details ), account_info.as_slice(), &signer, )?; mint_to( CpiContext::new_with_signer( ctx.accounts.token_program.to_account_info(), MintTo { authority: ctx.accounts.mint.to_account_info(), to: ctx.accounts.token_account.to_account_info(), mint: ctx.accounts.mint.to_account_info(), }, &signer, ), 1, )?; Ok(()) } } #[derive(Accounts)] pub struct InitializeMint<'info> { #[account( init, seeds = [b"mint"], bump, payer = user, mint::decimals = 6, mint::authority = mint, )] pub mint: Account<'info, Mint>, /// CHECK: Using "address" constraint to validate metadata account address #[account( mut, address=find_metadata_account(&mint.key()).0 )] pub metadata: UncheckedAccount<'info>, #[account( init_if_needed, payer = user, associated_token::mint = mint, associated_token::authority = user )] pub token_account: Account<'info, TokenAccount>, #[account(mut)] pub user: Signer<'info>, pub token_program: Program<'info, Token>, pub token_metadata_program: Program<'info, TokenMetaData>, pub associated_token_program: Program<'info, AssociatedToken>, pub rent: Sysvar<'info, Rent>, pub system_program: Program<'info, System>, } #[derive(Clone)] pub struct TokenMetaData; impl anchor_lang::Id for TokenMetaData { fn id() -> Pubkey { MetadataID } } Here's how the test looks import * as anchor from "@project-serum/anchor" import { Program } from "@project-serum/anchor" import { TokenWithMetadata } from "../target/types/token_with_metadata" import { findMetadataPda } from "@metaplex-foundation/js" import { getAccount, getAssociatedTokenAddressSync } from "@solana/spl-token" describe("token-with-metadata", () => { const provider = anchor.AnchorProvider.env() anchor.setProvider(provider) const program = anchor.workspace .TokenWithMetadata as Program<TokenWithMetadata> const TOKEN_METADATA_PROGRAM_ID = new anchor.web3.PublicKey( "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" ) const nft = { uri: "https://arweave.net/OwXDf7SM6nCVY2cvQ4svNjtV7WBTz3plbI4obN9JNkk", name: "NAME", symbol: "SYMBOL", } const [mintPDA] = anchor.web3.PublicKey.findProgramAddressSync( [Buffer.from("mint")], program.programId ) const tokenAccount = getAssociatedTokenAddressSync( mintPDA, provider.wallet.publicKey ) it("Is initialized!", async () => { // Add your test here. const metadataPDA = await findMetadataPda(mintPDA) const tx = await program.methods .initialize(nft.uri, nft.name, nft.symbol) .accounts({ mint: mintPDA, metadata: metadataPDA, tokenAccount: tokenAccount, user: provider.wallet.publicKey, tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID, }) .rpc() console.log("Your transaction signature", tx) const account = await getAccount(provider.connection, tokenAccount) console.log(account.amount) }) }) Also update `Cargo.toml`, you can remove `init-if-needed` if you're not using it [dependencies] anchor-lang = { version = "0.25.0", features = ["init-if-needed"] } anchor-spl = "0.25.0" mpl-token-metadata = {version = "1.5.0", features = ["no-entrypoint"]} Check out the metaplex sdk to see how to create the uri for the off-chain metadata for your token https://github.com/metaplex-foundation/js#readme You'll need to: - upload an image to a storage service to get the image uri (ex. arweave) - then upload again with the other metadata you want for the token (name, symbol etc) and the include the image uri from the previous step Here's an example of the BONK token metadata image uri: https://arweave.net/hQiPZOsRZXGXBJd_82PhVdlM_hACsT_q6wqwf5cSY7I token metadata uri (which points to the image uri): https://arweave.net/QPC6FYdUn-3V8ytFNuoCS85S2tHAuiDblh6u3CIZLsw Recommend looking through the metaplex docs, great diagrams for understanding relationship between accounts https://docs.metaplex.com/programs/understanding-programs https://docs.metaplex.com/programs/token-metadata/overview