3

when I use a live library like for example the code below I want to pass the instruction arguments which are of the same type as in the mpl_bubblegum library (mpl_bubblegum::state::metaplex_adapter::MetadataArgs).

use {
    anchor_lang::prelude::*,
};


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


    pub fn process_redeem_reward<'a, 'b, 'c, 'info>(
        ctx: Context<'a, 'b, 'c, 'info, ProcessRedeemRewardContext<'info>>,
        metadata: mpl_bubblegum::state::metaplex_adapter::MetadataArgs,
    ) -> Result<()> {
        Ok(())
    }
}

when the program is successfully built and deployed, then I call it from the client side, the anchor gives the following error :

Anchor IDL type not defined: MetadataArgs

when the program is successfully built and deployed, then I call it from the client side, the anchor gives the following error

when I checked from idl.json, MetadataArgs was not defined like when we created our own struct type, so MetadataArgs was not exported to idl, probably because it came from the type library that I used, namely mpl_bubblegum.

# idl.json
...
"args": [
        {
          "name": "metadata",
          "type": {
            "defined": "MetadataArgs"  <== make reference to MetadataArgs, but not found on this idl
          }
        },
...

how do I include the type that I use from this external library? so that it is also included in idl.json ? Thank You.

4 Answers 4

1

The reason of this error: Anchor IDL type not defined: MetadataArgs is due to MetadataArgs is not your program's defined types, it's from Bubblegum. So if you want to solve the error:

  1. create a rs file in your program, for example: metaplex-adapter.
  2. c/p the exact same content in Bubblegum/state/metaplex-adapter to the rs file.
  3. anchor build again.

Then the type will shown up in you program's IDL, which will solve the error. https://0xksure.medium.com/solana-anchor-idlerror-type-not-found-ac8897803856#:~:text=If%20you%20ever%20encounter%20an,not%20implement%20deserialization%20or%20serialization.

0

This may not answer the question about the external types, i'm not sure that is possible to export, but here is an example on how to create this meta data within an anchor program by passing in the needed parameters seperatly: https://github.com/solana-playground/solana-playground/blob/master/client/src/tutorials/BattleCoins/files/lib.rs

1
  • 1
    yea I am now using the same as this, i.e. defining the types itself. but I think this is a waste of time, if the types that have been made in the library can be used immediately, I don't think things like this need to be done again, namely to redefine types which are the same as the types of the library. Commented Apr 28, 2023 at 2:52
0

Not sure how you could set it up the way you asked, but another approach is to set up the MetadataArgs within the instruction. Here is an example I used to test minting a compressed NFT with Anchor:

pub fn mint_compressed_nft(ctx: Context<MintCompressedNft>) -> Result<()> {
    let signer_seeds: &[&[&[u8]]] = &[&[SEED.as_bytes(), &[*ctx.bumps.get("pda").unwrap()]]];

    // use collection nft metadata as the metadata for the compressed nft
    let metadata_account = &ctx.accounts.collection_metadata;

    let metadata = MetadataArgs {
        name: metadata_account.data.name.to_string(),
        symbol: metadata_account.data.symbol.to_string(),
        uri: metadata_account.data.uri.to_string(),
        collection: Some(Collection {
            key: ctx.accounts.collection_mint.key(),
            verified: false,
        }),
        primary_sale_happened: true,
        is_mutable: true,
        edition_nonce: None,
        token_standard: Some(TokenStandard::NonFungible),
        uses: None,
        token_program_version: TokenProgramVersion::Original,
        creators: vec![Creator {
            address: ctx.accounts.pda.key(), // set creator as pda
            verified: true,
            share: 100,
        }],
        seller_fee_basis_points: 0,
    };

    let cpi_ctx = CpiContext::new_with_signer(
        ctx.accounts.bubblegum_program.to_account_info(),
        MintToCollectionV1 {
            tree_authority: ctx.accounts.tree_authority.to_account_info(),
            leaf_owner: ctx.accounts.payer.to_account_info(),
            leaf_delegate: ctx.accounts.payer.to_account_info(),
            merkle_tree: ctx.accounts.merkle_tree.to_account_info(),
            payer: ctx.accounts.payer.to_account_info(),
            tree_delegate: ctx.accounts.pda.to_account_info(), // required as a signer
            collection_authority: ctx.accounts.pda.to_account_info(), // collection authority is pda (nft metadata update authority)
            collection_authority_record_pda: ctx.accounts.bubblegum_program.to_account_info(),
            collection_mint: ctx.accounts.collection_mint.to_account_info(), // collection nft mint account
            collection_metadata: ctx.accounts.collection_metadata.to_account_info(), // collection nft metadata account
            edition_account: ctx.accounts.edition_account.to_account_info(), // collection nft master edition account
            bubblegum_signer: ctx.accounts.bubblegum_signer.to_account_info(),
            log_wrapper: ctx.accounts.log_wrapper.to_account_info(),
            compression_program: ctx.accounts.compression_program.to_account_info(),
            token_metadata_program: ctx.accounts.token_metadata_program.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
        },
        signer_seeds,
    );

    mint_to_collection_v1(cpi_ctx, metadata)?;

    Ok(())
}

You could then modify to provide some of the information as instruction data. For example:

pub fn mint_compressed_nft(
    ctx: Context<MintCompressedNft>,
    name: String,
    symbol: String,
    uri: String,
) -> Result<()> {

    let metadata = MetadataArgs {
        name: name,
        symbol: symbol,
        uri: uri,
        ...
    };
    ...

    Ok(())
}
6
  • have you ever tried to match the hash results from compressed nft? I keep running into hash mismatches from compressed nft and from my self-defined metadata args. I don't know where the error is because I think my metadata is the same but when I match it with the onchain hash results it looks different. that's why I asked that, is it possible to directly use the types from bubblegum, who knows the problem is in the different types of structs. do you have any opinion on this? Commented Apr 28, 2023 at 2:50
  • Not entirely sure what you mean by match the hash results. Can you clarify what you're referring to? MetadataArgs is from mpl_bubblegum, but I haven't tried using instruction data directly. Could you update with what you're instruction looks like and what error you're getting?
    – john
    Commented Apr 28, 2023 at 3:40
  • okay, so I have a project that is to burn compressed nft with certain specifications. for example, the nft being burned must have the name "nft a" and the symbol "nft b". because the compressed nft from bgum only saves its hash metadata to onchain, when i do nft validation in smart contract, i also have to pass metadata from rpc client side to the instruction. Commented Apr 28, 2023 at 4:11
  • now in this new instruction I can retrieve compressed nft metadata information, but for security we don't know whether the metadata passed to the instruction really belongs to the nft user, it could be that the metadata passed to the instruction is the metadata created by the user, because of this I have to do security validation by matching the metadata hash passed by the user, and the hash stored in the bgum leaf. Commented Apr 28, 2023 at 4:11
  • that way I can ensure that the metadata passed by the user is really the same as the metadata hashed by bubblegum. Commented Apr 28, 2023 at 4:12
0

This doesn't work because the token metadata program wasn't built with anchor and thus MetadataArgs does not implement AnchorSerialize and AnchorDeserialize. To make it work, write a wrapper type around it and use that instead.

use std::ops::Deref;
use anchor_lang::prelude::ProgramError;

#[derive(Debug, Clone)]
pub struct MetaArgsWrapper {
    pub inner: MetadataArgs,
}

impl anchor_lang::AccountDeserialize for MetaArgsWrapper {
    fn try_deserialize(buf: &mut &[u8]) -> Result<Self, ProgramError> {
        Self::try_deserialize_unchecked(buf)
    }

    fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError> {
        let result = Self {
            inner: bincode::deserialize(buf).map_err(|_| ProgramError::InvalidAccountData)?,
        };
        *buf = &buf[std::mem::size_of::<MetadataArgs>()..];
        Ok(result)
    }
}

impl Deref for MetaArgsWrapper {
    type Target = MetadataArgs;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

It might be worth checking if there's an existing project that already provides functionality like this as a library.

It's kind of like the example here but without the need for the Owner trait.

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.